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

Skip to content

Commit 88f45f5

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

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
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 continously 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: 61 additions & 0 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;
@@ -1203,6 +1204,66 @@ public function testIteratorInput()
12031204
$this->assertSame('pingpong', $process->getOutput());
12041205
}
12051206

1207+
public function testSimpleInputStream()
1208+
{
1209+
$input = new InputStream();
1210+
1211+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('echo \'ping\'; stream_copy_to_stream(STDIN, STDOUT);'));
1212+
$process->setInput($input);
1213+
1214+
$process->start(function ($type, $data) use ($input) {
1215+
if ('ping' === $data) {
1216+
$input->write('pang');
1217+
} elseif (!$input->isClosed()) {
1218+
$input->write('pong');
1219+
$input->close();
1220+
}
1221+
});
1222+
1223+
$process->wait();
1224+
$this->assertSame('pingpangpong', $process->getOutput());
1225+
}
1226+
1227+
public function testInputStreamWithCallable()
1228+
{
1229+
$i = 0;
1230+
$stream = fopen('php://memory', 'w+');
1231+
$stream = function () use ($stream, &$i) {
1232+
rewind($stream);
1233+
fwrite($stream, ++$i);
1234+
rewind($stream);
1235+
1236+
return $stream;
1237+
};
1238+
1239+
$input = new InputStream($stream);
1240+
$input->write($stream());
1241+
1242+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'));
1243+
$process->setInput($input);
1244+
$process->start(function ($type, $data) use ($input) {
1245+
$input->close();
1246+
});
1247+
1248+
$process->wait();
1249+
$this->assertStringStartsWith('123', $process->getOutput());
1250+
}
1251+
1252+
public function testInputStreamWithGenerator()
1253+
{
1254+
$input = new InputStream(function ($input) {
1255+
yield 'pong';
1256+
$input->close();
1257+
});
1258+
1259+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'));
1260+
$process->setInput($input);
1261+
$process->start();
1262+
$input->write('ping');
1263+
$process->wait();
1264+
$this->assertSame('pingpong', $process->getOutput());
1265+
}
1266+
12061267
/**
12071268
* @param string $commandline
12081269
* @param null|string $cwd

0 commit comments

Comments
 (0)