New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: ChildProcess #61
Changes from 11 commits
24b0e0d
dbbb4f0
e5ec416
1643f78
161baaa
e3528ca
30cc4fc
adc2a1c
29dd20e
450e093
235533c
d428737
a88189e
200af15
1e1b95a
c17ae42
c84cf66
6155fcc
42502ef
a1ad4ee
26fdf41
8ef907b
a3024bb
a077a02
df0bc00
57ccee7
5849aa9
a28f5f5
b0e04d6
13da337
0ede84c
6297f1e
03ed74c
5d3082a
adcd3fa
70115f6
93a77f1
23cafb5
1b0a31a
5eabf62
8ce7fc7
106c5ee
c6820ed
2b6755a
e970e59
bb39ef0
864cb0d
c213270
dbd9b3f
cc64eb4
4c0995a
59a0bc7
49f765d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php | ||
|
||
// script to spawn multiple child processes | ||
|
||
require __DIR__.'/../vendor/autoload.php'; | ||
|
||
$loop = new React\EventLoop\StreamSelectLoop(); | ||
$factory = new React\ChildProcess\Factory($loop); | ||
|
||
$commands = array( | ||
'A' => array('cmd' => 'php', 'args' => array('-r', 'foreach (range(1, 3) as $i) { echo $i, PHP_EOL; sleep(1); } fputs(STDERR, "Bye.");')), | ||
'B' => array('cmd' => 'php', 'args' => array('-r', 'foreach (range(1, 6) as $i) { echo $i, PHP_EOL; sleep(1); } fputs(STDERR, "Bye.");')), | ||
'C' => array('cmd' => 'php', 'args' => array('-r', 'foreach (range(1, 9) as $i) { echo $i, PHP_EOL; sleep(1); } fputs(STDERR, "Bye.");')), | ||
); | ||
|
||
foreach ($commands as $id => $command) { | ||
$idLabel = "[{$id}] "; | ||
$process = $factory->spawn($command['cmd'], $command['args']); | ||
echo $idLabel, blue('[PID]') . ' pid is ', (string) $process->getPid(), PHP_EOL; | ||
echo $idLabel, blue('[CMD]') . ' ', $process->getCommand(), PHP_EOL; | ||
|
||
$process->stdout->on('data', function ($data) use ($idLabel) { | ||
echo $idLabel, green('[STDOUT]') . ' '; | ||
var_dump($data); | ||
}); | ||
|
||
$process->stderr->on('data', function ($data) use ($idLabel) { | ||
echo $idLabel, red('[STDERR]') . ' '; | ||
var_dump($data); | ||
}); | ||
|
||
$process->on('exit', function ($status) use ($idLabel) { | ||
echo $idLabel, yellow('[EXIT]') . ' exited with status code ', (string) $status, PHP_EOL; | ||
}); | ||
} | ||
|
||
$loop->run(); | ||
|
||
function red($str) { | ||
return "\033[31m{$str}\033[0m"; | ||
} | ||
|
||
function green($str) { | ||
return "\033[32m{$str}\033[0m"; | ||
} | ||
|
||
function yellow($str) { | ||
return "\033[33m{$str}\033[0m"; | ||
} | ||
|
||
function blue($str) { | ||
return "\033[34m{$str}\033[0m"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php | ||
|
||
namespace React\ChildProcess; | ||
|
||
use React\EventLoop\LoopInterface; | ||
use React\Stream\Stream; | ||
use React\ChildProcess\Process; | ||
|
||
class Factory | ||
{ | ||
private $loop; | ||
|
||
public function __construct(LoopInterface $loop) | ||
{ | ||
$this->loop = $loop; | ||
} | ||
|
||
public function spawn($file, $args = NULL, $options = NULL) | ||
{ | ||
$args = $args ? $args : array(); | ||
|
||
$cmd = $this->createCommand($file, $args); | ||
$cwd = isset($options['cwd']) ? $options['cwd'] : NULL; | ||
$env = isset($options['env']) ? $options['env'] : NULL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think since there are only 2 options in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I followd Node.js' Isn't similarities with Node.js needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Node.js is definitely an inspiration, but contrary to popular belief, react is not a port of node to PHP. Unless we implement more options, I agree with @cboden. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for your comments. |
||
|
||
$fdSpec = array( | ||
array('pipe', 'r'), | ||
array('pipe', 'w'), | ||
array('pipe', 'w'), | ||
); | ||
|
||
$process = proc_open($cmd, $fdSpec, $pipes, $cwd, $env); | ||
|
||
$stdin = new Stream($pipes[0], $this->loop); | ||
$stdout = new Stream($pipes[1], $this->loop); | ||
$stderr = new Stream($pipes[2], $this->loop); | ||
|
||
$stdin->pause(); | ||
|
||
stream_set_blocking($pipes[0], 0); | ||
stream_set_blocking($pipes[1], 0); | ||
stream_set_blocking($pipes[2], 0); | ||
|
||
return new Process($process, $stdin, $stdout, $stderr); | ||
} | ||
|
||
private function createCommand($file, $args) | ||
{ | ||
$command = $file; | ||
if (count($args) > 0) { | ||
$command .= ' ' . join(' ', array_map('escapeshellarg', $args)); | ||
} | ||
return $command; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<?php | ||
|
||
namespace React\ChildProcess; | ||
|
||
use React\Stream\WritableStreamInterface; | ||
use React\Stream\ReadableStreamInterface; | ||
use Evenement\EventEmitter; | ||
|
||
class Process extends EventEmitter | ||
{ | ||
public $stdin; | ||
|
||
public $stdout; | ||
|
||
public $stderr; | ||
|
||
private $status = NULL; | ||
|
||
public function __construct($process, WritableStreamInterface $stdin, ReadableStreamInterface $stdout, ReadableStreamInterface $stderr) | ||
{ | ||
$this->process = $process; | ||
$this->stdin = $stdin; | ||
$this->stdout = $stdout; | ||
$this->stderr = $stderr; | ||
|
||
$self = $this; | ||
|
||
$this->stdout->on('end', function () use ($self, $stderr) { | ||
if ($stderr->isReadable() === false) { | ||
$self->handleExit(); | ||
} | ||
}); | ||
|
||
$this->stderr->on('end', function () use ($self, $stdout) { | ||
if ($stdout->isReadable() === false) { | ||
$self->handleExit(); | ||
} | ||
}); | ||
} | ||
|
||
public function handleExit() | ||
{ | ||
$status = proc_close($this->process); | ||
$this->emit('exit', array($status)); | ||
$this->emit('close', array($status)); | ||
} | ||
|
||
public function getPid() | ||
{ | ||
$status = $this->getCachedStatus(); | ||
return $status['pid']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you put a new line between the variable setter and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
} | ||
|
||
public function getCommand() | ||
{ | ||
$status = $this->getCachedStatus(); | ||
return $status['command']; | ||
} | ||
|
||
public function isRunning() | ||
{ | ||
$status = $this->getFreshStatus(); | ||
return $status['running']; | ||
} | ||
|
||
public function isSignaled() | ||
{ | ||
$status = $this->getFreshStatus(); | ||
return $status['signaled']; | ||
} | ||
|
||
public function isStopped() | ||
{ | ||
$status = $this->getFreshStatus(); | ||
return $status['stopped']; | ||
} | ||
|
||
private function getCachedStatus() | ||
{ | ||
if (is_null($this->status)) { | ||
$this->status = proc_get_status($this->process); | ||
} | ||
return $this->status; | ||
} | ||
|
||
private function getFreshStatus() | ||
{ | ||
$this->status = proc_get_status($this->process); | ||
return $this->status; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be done in the function declaration:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
And all of the
NULL
was replaced withnull
.