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

Skip to content

Commit b9782b7

Browse files
[Process] Accept Traversable input
1 parent 06eb52c commit b9782b7

File tree

5 files changed

+69
-18
lines changed

5 files changed

+69
-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: 30 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
{
@@ -1156,6 +1156,35 @@ public function provideVariousIncrementals() {
11561156
);
11571157
}
11581158

1159+
public function testIteratorInput()
1160+
{
1161+
$nextData = 'ping';
1162+
$input = function () use (&$nextData) {
1163+
while (false !== $nextData) {
1164+
yield $nextData;
1165+
yield $nextData = '';
1166+
}
1167+
};
1168+
$input = $input();
1169+
1170+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'));
1171+
$process->setInput($input);
1172+
$process->start(function ($type, $data) use ($input, &$nextData) {
1173+
if ('ping' === $data) {
1174+
$h = fopen('php://memory', 'r+');
1175+
fwrite($h, 'pong');
1176+
rewind($h);
1177+
$nextData = $h;
1178+
$input->next();
1179+
} else {
1180+
$nextData = false;
1181+
}
1182+
});
1183+
1184+
$process->wait();
1185+
$this->assertSame('pingpong', $process->getOutput());
1186+
}
1187+
11591188
/**
11601189
* @param string $commandline
11611190
* @param null|string $cwd

0 commit comments

Comments
 (0)