Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8a72e3d

Browse files
[Process] Handle Iterator as input
1 parent 2946932 commit 8a72e3d

File tree

5 files changed

+66
-18
lines changed

5 files changed

+66
-18
lines changed

src/Symfony/Component/Process/Pipes/AbstractPipes.php

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,15 @@ abstract class AbstractPipes implements PipesInterface
2222
public $pipes = array();
2323

2424
/** @var string */
25-
protected $inputBuffer = '';
26-
/** @var resource|null */
27-
protected $input;
28-
25+
private $inputBuffer = '';
26+
/** @var resource|\Iterator|null */
27+
private $input;
2928
/** @var bool */
3029
private $blocked = true;
3130

3231
public function __construct($input)
3332
{
34-
if (is_resource($input)) {
33+
if (is_resource($input) || $input instanceof \Iterator) {
3534
$this->input = $input;
3635
} elseif (is_string($input)) {
3736
$this->inputBuffer = $input;
@@ -76,7 +75,7 @@ protected function unblock()
7675
foreach ($this->pipes as $pipe) {
7776
stream_set_blocking($pipe, 0);
7877
}
79-
if (null !== $this->input) {
78+
if (is_resource($this->input)) {
8079
stream_set_blocking($this->input, 0);
8180
}
8281

@@ -91,9 +90,21 @@ protected function write()
9190
if (!isset($this->pipes[0])) {
9291
return;
9392
}
93+
$input = $this->input;
94+
95+
if ($input instanceof \Iterator) {
96+
if (!$input->valid()) {
97+
$input = null;
98+
} elseif (is_resource($input = $input->current())) {
99+
stream_set_blocking($input, 0);
100+
} else {
101+
$this->inputBuffer .= $input;
102+
$this->input->next();
103+
$input = null;
104+
}
105+
}
94106

95-
$e = array();
96-
$r = null !== $this->input ? array($this->input) : $e;
107+
$r = $e = array();
97108
$w = array($this->pipes[0]);
98109

99110
// let's have a look if something changed in streams
@@ -109,8 +120,7 @@ protected function write()
109120
return array($this->pipes[0]);
110121
}
111122
}
112-
113-
foreach ($r as $input) {
123+
if ($input) {
114124
for (;;) {
115125
$data = fread($input, self::CHUNK_SIZE);
116126
if (!isset($data[0])) {
@@ -124,16 +134,19 @@ protected function write()
124134
return array($this->pipes[0]);
125135
}
126136
}
127-
if (!isset($data[0]) && feof($input)) {
128-
// no more data to read on input resource
129-
// use an empty buffer in the next reads
130-
$this->input = null;
137+
if (feof($input)) {
138+
if ($this->input instanceof \Iterator) {
139+
$this->input->next();
140+
} else {
141+
$this->input = null;
142+
}
131143
}
132144
}
133145
}
134146

135147
// no input to read on resource, buffer is empty
136-
if (null === $this->input && !isset($this->inputBuffer[0])) {
148+
if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) {
149+
$this->input = null;
137150
fclose($this->pipes[0]);
138151
unset($this->pipes[0]);
139152
}

src/Symfony/Component/Process/Process.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,9 @@ public function hasCallback()
12001200
*/
12011201
private function getDescriptors()
12021202
{
1203+
if ($this->input instanceof \Iterator) {
1204+
$this->input->rewind();
1205+
}
12031206
if ('\\' === DIRECTORY_SEPARATOR) {
12041207
$this->processPipes = WindowsPipes::create($this, $this->input);
12051208
} else {

src/Symfony/Component/Process/ProcessUtils.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,14 @@ public static function validateInput($caller, $input)
9393
if (is_scalar($input)) {
9494
return (string) $input;
9595
}
96+
if ($input instanceof \Iterator) {
97+
return $input;
98+
}
99+
if ($input instanceof \Traversable) {
100+
return new \IteratorIterator($input);
101+
}
96102

97-
throw new InvalidArgumentException(sprintf('%s only accepts strings or stream resources.', $caller));
103+
throw new InvalidArgumentException(sprintf('%s only accepts strings, Traversable objects or stream resources.', $caller));
98104
}
99105

100106
return $input;

src/Symfony/Component/Process/Tests/ProcessBuilderTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ public function testShouldReturnProcessWithEnabledOutput()
215215

216216
/**
217217
* @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
218-
* @expectedExceptionMessage Symfony\Component\Process\ProcessBuilder::setInput only accepts strings or stream resources.
218+
* @expectedExceptionMessage Symfony\Component\Process\ProcessBuilder::setInput only accepts strings, Traversable objects or stream resources.
219219
*/
220220
public function testInvalidInput()
221221
{

src/Symfony/Component/Process/Tests/ProcessTest.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public function testSetInputWhileRunningThrowsAnException()
231231
/**
232232
* @dataProvider provideInvalidInputValues
233233
* @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
234-
* @expectedExceptionMessage Symfony\Component\Process\Process::setInput only accepts strings or stream resources.
234+
* @expectedExceptionMessage Symfony\Component\Process\Process::setInput only accepts strings, Traversable objects or stream resources.
235235
*/
236236
public function testInvalidInput($value)
237237
{
@@ -1174,6 +1174,32 @@ public function methodProvider()
11741174
return $defaults;
11751175
}
11761176

1177+
public function testIteratorInput()
1178+
{
1179+
$input = function ($data) {
1180+
while (false !== $data) {
1181+
$data = (yield $data);
1182+
}
1183+
};
1184+
$input = $input('ping');
1185+
1186+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'));
1187+
$process->setInput($input);
1188+
$process->start(function ($type, $data) use ($input) {
1189+
if ('ping' === $data) {
1190+
$h = fopen('php://memory', 'r+');
1191+
fwrite($h, 'pong');
1192+
rewind($h);
1193+
$input->send($h);
1194+
} else {
1195+
$input->send(false);
1196+
}
1197+
});
1198+
1199+
$process->wait();
1200+
$this->assertSame('pingpong', $process->getOutput());
1201+
}
1202+
11771203
/**
11781204
* @param string $commandline
11791205
* @param null|string $cwd

0 commit comments

Comments
 (0)