Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ install:

script:
- vendor/bin/phpunit --coverage-text
- php examples/13-benchmark-throughput.php
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,39 @@ as [Streams](https://github.com/reactphp/stream).

**Table of contents**

* [Quickstart example](#quickstart-example)
* [Processes](#processes)
* [EventEmitter Events](#eventemitter-events)
* [Methods](#methods)
* [Stream Properties](#stream-properties)
* [Usage](#usage)
* [Prepending Commands with `exec`](#prepending-commands-with-exec)
* [Sigchild Compatibility](#sigchild-compatibility)
* [Command Chaining](#command-chaining)
* [Install](#install)
* [Tests](#tests)
* [License](#license)

## Quickstart example

```php
$loop = React\EventLoop\Factory::create();

$process = new React\ChildProcess\Process('echo foo');
$process->start($loop);

$process->stdout->on('data', function ($chunk) {
echo $chunk;
});

$process->on('exit', function($exitCode, $termSignal) {
echo 'Process exited with code ' . $exitCode . PHP_EOL;
});

$loop->run();
```

See also the [examples](examples).

## Processes

### EventEmitter Events
Expand Down Expand Up @@ -81,26 +102,6 @@ $process->stdin->close();
For more details, see the
[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).

## Usage
```php
$loop = React\EventLoop\Factory::create();

$process = new React\ChildProcess\Process('echo foo');

$process->on('exit', function($exitCode, $termSignal) {
// ...
});

$loop->addTimer(0.001, function($timer) use ($process) {
$process->start($timer->getLoop());

$process->stdout->on('data', function($output) {
// ...
});
});

$loop->run();
```
### Prepending Commands with `exec`

Symfony pull request [#5759](https://github.com/symfony/symfony/issues/5759)
Expand Down
32 changes: 32 additions & 0 deletions examples/01-stdio.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use React\EventLoop\Factory;
use React\ChildProcess\Process;

require __DIR__ . '/../vendor/autoload.php';

$loop = Factory::create();

$process = new Process('cat');
$process->start($loop);

$process->stdout->on('data', function ($chunk) {
echo $chunk;
});

$process->on('exit', function ($code) {
echo 'EXIT with code ' . $code . PHP_EOL;
});

// periodically send something to stream
$periodic = $loop->addPeriodicTimer(0.2, function () use ($process) {
$process->stdin->write('hello');
});

// stop sending after a few seconds
$loop->addTimer(2.0, function () use ($periodic, $loop, $process) {
$loop->cancelTimer($periodic);
$process->stdin->end();
});

$loop->run();
24 changes: 24 additions & 0 deletions examples/02-race.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

use React\EventLoop\Factory;
use React\ChildProcess\Process;

require __DIR__ . '/../vendor/autoload.php';

$loop = Factory::create();

$first = new Process('sleep 2; echo welt');
$first->start($loop);

$second = new Process('sleep 1; echo hallo');
$second->start($loop);

$first->stdout->on('data', function ($chunk) {
echo $chunk;
});

$second->stdout->on('data', function ($chunk) {
echo $chunk;
});

$loop->run();
25 changes: 25 additions & 0 deletions examples/03-stdout-stderr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

use React\EventLoop\Factory;
use React\ChildProcess\Process;

require __DIR__ . '/../vendor/autoload.php';

$loop = Factory::create();

$process = new Process('echo hallo;sleep 1;echo welt >&2;sleep 1;echo error;sleep 1;nope');
$process->start($loop);

$process->stdout->on('data', function ($chunk) {
echo '(' . $chunk . ')';
});

$process->stderr->on('data', function ($chunk) {
echo '[' . $chunk . ']';
});

$process->on('exit', function ($code) {
echo 'EXIT with code ' . $code . PHP_EOL;
});

$loop->run();
48 changes: 48 additions & 0 deletions examples/11-benchmark-read.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

// $ php examples/11-benchmark.php
// $ php examples/11-benchmark.php echo test
// $ php examples/11-benchmark.php dd if=/dev/zero bs=1M count=1000

use React\EventLoop\Factory;
use React\ChildProcess\Process;

require __DIR__ . '/../vendor/autoload.php';

$cmd = isset($argv[1]) ? implode(' ', array_slice($argv, 1)) : 'dd if=/dev/zero bs=1M count=1000';

$loop = Factory::create();

$info = new React\Stream\Stream(STDERR, $loop);
$info->pause();
$info->write('Counts number of chunks/bytes received from process STDOUT' . PHP_EOL);
$info->write('Command: ' . $cmd . PHP_EOL);
if (extension_loaded('xdebug')) {
$info->write('NOTICE: The "xdebug" extension is loaded, this has a major impact on performance.' . PHP_EOL);
}

$process = new Process($cmd);
$process->start($loop);
$start = microtime(true);

$chunks = 0;
$bytes = 0;
$process->stdout->on('data', function ($chunk) use (&$chunks, &$bytes) {
++$chunks;
$bytes += strlen($chunk);
});

// print stream position once stream closes
$process->on('exit', function () use (&$chunks, &$bytes, $start, $info) {
$t = microtime(true) - $start;

$info->write('read ' . $chunks . ' chunks with ' . $bytes . ' byte(s) in ' . round($t, 3) . ' second(s) => ' . round($bytes / 1024 / 1024 / $t, 1) . ' MiB/s' . PHP_EOL);
$info->write('peak memory usage of ' . round(memory_get_peak_usage(true) / 1024 / 1024, 1) . ' MiB' . PHP_EOL);
});

// report any other output/errors
$process->stdout->on('error', array($info, 'write'));
$process->stderr->on('data', 'printf');
$process->stdout->on('error', 'printf');

$loop->run();
45 changes: 45 additions & 0 deletions examples/12-benchmark-write.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

use React\EventLoop\Factory;
use React\ChildProcess\Process;
use React\Stream\Stream;

require __DIR__ . '/../vendor/autoload.php';

$loop = Factory::create();

$info = new React\Stream\Stream(STDERR, $loop);
$info->pause();
$info->write('Pipes data to process STDIN' . PHP_EOL);
if (extension_loaded('xdebug')) {
$info->write('NOTICE: The "xdebug" extension is loaded, this has a major impact on performance.' . PHP_EOL);
}

$process = new Process('dd of=/dev/zero');
$process->start($loop);

// 10000 * 100 KB => 1 GB
$i = 10000;
$chunk = str_repeat("\0", 100 * 1000);
$write = function () use ($chunk, $process, &$i, &$write) {
do {
--$i;
$continue = $process->stdin->write($chunk);
} while ($i && $continue);

if ($i > 0) {
// buffer full => wait for drain to continue
$process->stdin->once('drain', $write);
} else {
$process->stdin->end();
}
};
$write();

// report any other output/errors
$process->stdout->on('data', 'printf');
$process->stdout->on('error', 'printf');
$process->stderr->on('data', 'printf');
$process->stdout->on('error', 'printf');

$loop->run();
60 changes: 60 additions & 0 deletions examples/13-benchmark-throughput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

use React\EventLoop\Factory;
use React\ChildProcess\Process;
use React\Stream\Stream;

require __DIR__ . '/../vendor/autoload.php';

$loop = Factory::create();

$info = new React\Stream\Stream(STDERR, $loop);
$info->pause();
$info->write('Pipes data through process STDIN and reads STDOUT again' . PHP_EOL);
if (extension_loaded('xdebug')) {
$info->write('NOTICE: The "xdebug" extension is loaded, this has a major impact on performance.' . PHP_EOL);
}

$process = new Process('cat');
$process->start($loop);
$start = microtime(true);

$chunks = 0;
$bytes = 0;
$process->stdout->on('data', function ($chunk) use (&$chunks, &$bytes) {
++$chunks;
$bytes += strlen($chunk);
});

// 10000 * 100 KB => 1 GB
$i = 10000;
$chunk = str_repeat("\0", 100 * 1000);
$write = function () use ($chunk, $process, &$i, &$write) {
do {
--$i;
$continue = $process->stdin->write($chunk);
} while ($i && $continue);

if ($i > 0) {
// buffer full => wait for drain to continue
$process->stdin->once('drain', $write);
} else {
$process->stdin->end();
}
};
$write();

// print stream position once process exits
$process->on('exit', function () use (&$chunks, &$bytes, $start, $info) {
$t = microtime(true) - $start;

$info->write('read ' . $chunks . ' chunks with ' . $bytes . ' byte(s) in ' . round($t, 3) . ' second(s) => ' . round($bytes / 1024 / 1024 / $t, 1) . ' MiB/s' . PHP_EOL);
$info->write('peak memory usage of ' . round(memory_get_peak_usage(true) / 1024 / 1024, 1) . ' MiB' . PHP_EOL);
});

// report any other output/errors
$process->stdout->on('error', 'printf');
$process->stderr->on('data', 'printf');
$process->stdout->on('error', 'printf');

$loop->run();