2015-08-27 16:10:08 +02:00
|
|
|
<?php
|
|
|
|
namespace Icicle\Concurrent\Process;
|
|
|
|
|
2015-08-27 20:06:39 +02:00
|
|
|
use Icicle\Concurrent\Exception\InvalidArgumentError;
|
|
|
|
use Icicle\Concurrent\Exception\StatusError;
|
2015-08-27 16:10:08 +02:00
|
|
|
use Icicle\Concurrent\Exception\SynchronizationError;
|
2015-12-05 06:50:32 +01:00
|
|
|
use Icicle\Concurrent\Process as ProcessContext;
|
2015-08-27 16:10:08 +02:00
|
|
|
use Icicle\Concurrent\Sync\Channel;
|
2015-12-06 07:32:06 +01:00
|
|
|
use Icicle\Concurrent\Sync\ChannelledStream;
|
2015-12-05 06:50:32 +01:00
|
|
|
use Icicle\Concurrent\Sync\Internal\ExitStatus;
|
2015-08-27 16:10:08 +02:00
|
|
|
|
2015-12-05 06:50:32 +01:00
|
|
|
class ChannelledProcess implements Channel, ProcessContext
|
2015-08-27 16:10:08 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var \Icicle\Concurrent\Process\Process
|
|
|
|
*/
|
|
|
|
private $process;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var \Icicle\Concurrent\Sync\Channel
|
|
|
|
*/
|
|
|
|
private $channel;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $path Path to PHP script.
|
|
|
|
* @param string $cwd Working directory.
|
|
|
|
* @param mixed[] $env Array of environment variables.
|
|
|
|
*/
|
|
|
|
public function __construct($path, $cwd = '', array $env = [])
|
|
|
|
{
|
|
|
|
$command = PHP_BINARY . ' ' . $path;
|
|
|
|
|
|
|
|
$this->process = new Process($command, $cwd, $env);
|
|
|
|
}
|
|
|
|
|
2015-08-27 20:06:39 +02:00
|
|
|
/**
|
|
|
|
* Resets process values.
|
|
|
|
*/
|
|
|
|
public function __clone()
|
|
|
|
{
|
|
|
|
$this->process = clone $this->process;
|
|
|
|
$this->channel = null;
|
|
|
|
}
|
|
|
|
|
2015-08-27 16:10:08 +02:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function start()
|
|
|
|
{
|
|
|
|
$this->process->start();
|
|
|
|
|
2015-12-06 07:32:06 +01:00
|
|
|
$this->channel = new ChannelledStream($this->process->getStdOut(), $this->process->getStdIn());
|
2015-08-27 16:10:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function isRunning()
|
|
|
|
{
|
|
|
|
return $this->process->isRunning();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function receive()
|
|
|
|
{
|
2015-09-20 04:34:41 +02:00
|
|
|
if (null === $this->channel) {
|
2015-08-27 20:06:39 +02:00
|
|
|
throw new StatusError('The process has not been started.');
|
2015-08-27 16:10:08 +02:00
|
|
|
}
|
|
|
|
|
2015-08-27 20:06:39 +02:00
|
|
|
$data = (yield $this->channel->receive());
|
|
|
|
|
2015-12-05 06:50:32 +01:00
|
|
|
if ($data instanceof ExitStatus) {
|
2015-08-27 20:06:39 +02:00
|
|
|
$data = $data->getResult();
|
|
|
|
throw new SynchronizationError(sprintf(
|
|
|
|
'Thread unexpectedly exited with result of type: %s',
|
|
|
|
is_object($data) ? get_class($data) : gettype($data)
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
yield $data;
|
2015-08-27 16:10:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function send($data)
|
|
|
|
{
|
2015-09-20 04:34:41 +02:00
|
|
|
if (null === $this->channel) {
|
2015-08-27 20:06:39 +02:00
|
|
|
throw new StatusError('The process has not been started.');
|
|
|
|
}
|
|
|
|
|
2015-12-05 06:50:32 +01:00
|
|
|
if ($data instanceof ExitStatus) {
|
2015-08-27 20:06:39 +02:00
|
|
|
throw new InvalidArgumentError('Cannot send exit status objects.');
|
2015-08-27 16:10:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
yield $this->channel->send($data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function join()
|
|
|
|
{
|
2015-09-20 04:34:41 +02:00
|
|
|
if (null === $this->channel) {
|
2015-08-27 20:06:39 +02:00
|
|
|
throw new StatusError('The process has not been started.');
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:54:09 +02:00
|
|
|
$response = (yield $this->channel->receive());
|
2015-08-27 20:06:39 +02:00
|
|
|
|
2015-10-18 08:54:09 +02:00
|
|
|
yield $this->process->join();
|
2015-08-27 20:06:39 +02:00
|
|
|
|
2015-12-05 06:50:32 +01:00
|
|
|
if (!$response instanceof ExitStatus) {
|
2015-10-18 08:54:09 +02:00
|
|
|
throw new SynchronizationError('Did not receive an exit status from thread.');
|
2015-08-27 20:06:39 +02:00
|
|
|
}
|
2015-10-18 08:54:09 +02:00
|
|
|
|
|
|
|
yield $response->getResult();
|
2015-08-27 16:10:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function kill()
|
|
|
|
{
|
|
|
|
$this->process->kill();
|
2015-10-18 08:54:09 +02:00
|
|
|
$this->channel = null;
|
2015-08-27 16:10:08 +02:00
|
|
|
}
|
2015-10-20 07:06:43 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function getPid()
|
|
|
|
{
|
|
|
|
return $this->process->getPid();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function signal($signo)
|
|
|
|
{
|
|
|
|
$this->process->signal($signo);
|
|
|
|
}
|
2015-08-27 16:10:08 +02:00
|
|
|
}
|