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

Skip to content

Commit 87cc4f8

Browse files
[Process] Add InputStream to seamlessly feed running processes
1 parent 6ed73d5 commit 87cc4f8

File tree

2 files changed

+143
-16
lines changed

2 files changed

+143
-16
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Process;
13+
14+
use Symfony\Component\Process\Exception\RuntimeException;
15+
16+
/**
17+
* Provides a way to continuously write to the input of a Process.
18+
*
19+
* @author Nicolas Grekas <[email protected]>
20+
*/
21+
class InputStream implements \IteratorAggregate
22+
{
23+
private $onEmpty = null;
24+
private $input = array();
25+
private $open = true;
26+
27+
public function __construct(callable $onEmpty = null)
28+
{
29+
$this->onEmpty($onEmpty);
30+
}
31+
32+
public function onEmpty(callable $onEmpty = null)
33+
{
34+
$this->onEmpty = $onEmpty;
35+
}
36+
37+
public function write($input)
38+
{
39+
if (null === $input) {
40+
return;
41+
}
42+
if ($this->isClosed()) {
43+
throw new RuntimeException(sprintf('%s is closed', get_class($this)));
44+
}
45+
$this->input[] = ProcessUtils::validateInput(__METHOD__, $input);
46+
47+
return $this;
48+
}
49+
50+
public function close()
51+
{
52+
$this->open = false;
53+
}
54+
55+
public function isClosed()
56+
{
57+
return !$this->open;
58+
}
59+
60+
public function getIterator()
61+
{
62+
$this->open = true;
63+
64+
while ($this->open || $this->input) {
65+
if (!$this->input) {
66+
yield '';
67+
continue;
68+
}
69+
$current = array_shift($this->input);
70+
71+
if ($current instanceof \Iterator) {
72+
foreach ($current as $cur) {
73+
yield $cur;
74+
}
75+
} else {
76+
yield $current;
77+
}
78+
if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) {
79+
$this->write($onEmpty($this));
80+
}
81+
}
82+
}
83+
}

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

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Process\Exception\LogicException;
1515
use Symfony\Component\Process\Exception\ProcessTimedOutException;
1616
use Symfony\Component\Process\Exception\RuntimeException;
17+
use Symfony\Component\Process\InputStream;
1718
use Symfony\Component\Process\PhpExecutableFinder;
1819
use Symfony\Component\Process\Pipes\PipesInterface;
1920
use Symfony\Component\Process\Process;
@@ -1176,29 +1177,72 @@ public function provideVariousIncrementals() {
11761177

11771178
public function testIteratorInput()
11781179
{
1179-
$nextData = 'ping';
1180-
$input = function () use (&$nextData) {
1181-
while (false !== $nextData) {
1182-
yield $nextData;
1183-
yield $nextData = '';
1184-
}
1180+
$input = function () {
1181+
yield 'ping';
1182+
yield 'pong';
11851183
};
1186-
$input = $input();
11871184

1188-
$process = new Process(self::$phpBin.' -r '.escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'));
1185+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'), null, null, $input());
1186+
$process->run();
1187+
$this->assertSame('pingpong', $process->getOutput());
1188+
}
1189+
1190+
public function testSimpleInputStream()
1191+
{
1192+
$input = new InputStream();
1193+
1194+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('echo \'ping\'; stream_copy_to_stream(STDIN, STDOUT);'));
11891195
$process->setInput($input);
1190-
$process->start(function ($type, $data) use ($input, &$nextData) {
1196+
1197+
$process->start(function ($type, $data) use ($input) {
11911198
if ('ping' === $data) {
1192-
$h = fopen('php://memory', 'r+');
1193-
fwrite($h, 'pong');
1194-
rewind($h);
1195-
$nextData = $h;
1196-
$input->next();
1197-
} else {
1198-
$nextData = false;
1199+
$input->write('pang');
1200+
} elseif (!$input->isClosed()) {
1201+
$input->write('pong');
1202+
$input->close();
11991203
}
12001204
});
12011205

1206+
$process->wait();
1207+
$this->assertSame('pingpangpong', $process->getOutput());
1208+
}
1209+
1210+
public function testInputStreamWithCallable()
1211+
{
1212+
$i = 0;
1213+
$stream = fopen('php://memory', 'w+');
1214+
$stream = function () use ($stream, &$i) {
1215+
rewind($stream);
1216+
fwrite($stream, ++$i);
1217+
rewind($stream);
1218+
1219+
return $stream;
1220+
};
1221+
1222+
$input = new InputStream($stream);
1223+
$input->write($stream());
1224+
1225+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'));
1226+
$process->setInput($input);
1227+
$process->start(function ($type, $data) use ($input) {
1228+
$input->close();
1229+
});
1230+
1231+
$process->wait();
1232+
$this->assertStringStartsWith('123', $process->getOutput());
1233+
}
1234+
1235+
public function testInputStreamWithGenerator()
1236+
{
1237+
$input = new InputStream(function ($input) {
1238+
yield 'pong';
1239+
$input->close();
1240+
});
1241+
1242+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'));
1243+
$process->setInput($input);
1244+
$process->start();
1245+
$input->write('ping');
12021246
$process->wait();
12031247
$this->assertSame('pingpong', $process->getOutput());
12041248
}

0 commit comments

Comments
 (0)