mirror of
https://github.com/danog/process.git
synced 2024-11-30 04:39:04 +01:00
Use shared CS config; update styles
This commit is contained in:
parent
c2c529d72d
commit
9460633c7d
46
.php_cs
46
.php_cs
@ -1,40 +1,10 @@
|
||||
<?php
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
"@PSR1" => true,
|
||||
"@PSR2" => true,
|
||||
"braces" => [
|
||||
"allow_single_line_closure" => true,
|
||||
"position_after_functions_and_oop_constructs" => "same",
|
||||
],
|
||||
"array_syntax" => ["syntax" => "short"],
|
||||
"cast_spaces" => true,
|
||||
"combine_consecutive_unsets" => true,
|
||||
"function_to_constant" => true,
|
||||
"no_multiline_whitespace_before_semicolons" => true,
|
||||
"no_unused_imports" => true,
|
||||
"no_useless_else" => true,
|
||||
"no_useless_return" => true,
|
||||
"no_whitespace_before_comma_in_array" => true,
|
||||
"no_whitespace_in_blank_line" => true,
|
||||
"non_printable_character" => true,
|
||||
"normalize_index_brace" => true,
|
||||
"ordered_imports" => true,
|
||||
"php_unit_construct" => true,
|
||||
"php_unit_dedicate_assert" => true,
|
||||
"php_unit_fqcn_annotation" => true,
|
||||
"phpdoc_summary" => true,
|
||||
"phpdoc_types" => true,
|
||||
"psr4" => true,
|
||||
"return_type_declaration" => ["space_before" => "none"],
|
||||
"short_scalar_cast" => true,
|
||||
"single_blank_line_before_namespace" => true,
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . "/examples")
|
||||
->in(__DIR__ . "/lib")
|
||||
->in(__DIR__ . "/test")
|
||||
);
|
||||
$config = new Amp\CodeStyle\Config();
|
||||
$config->getFinder()->in(__DIR__);
|
||||
|
||||
$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;
|
||||
|
||||
$config->setCacheFile($cacheDir . '/.php_cs.cache');
|
||||
|
||||
return $config;
|
||||
|
@ -5,12 +5,12 @@
|
||||
"require": {
|
||||
"php": ">=7",
|
||||
"amphp/amp": "^2",
|
||||
"amphp/byte-stream": "^1"
|
||||
"amphp/byte-stream": "^1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6",
|
||||
"amphp/phpunit-util": "^1",
|
||||
"friendsofphp/php-cs-fixer": "^2.3"
|
||||
"amphp/php-cs-fixer-config": "dev-master"
|
||||
},
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
@ -42,5 +42,14 @@
|
||||
"platform": {
|
||||
"php": "7.0.0"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"check": [
|
||||
"@cs",
|
||||
"@test"
|
||||
],
|
||||
"cs": "php-cs-fixer fix -v --diff --dry-run",
|
||||
"cs-fix": "php-cs-fixer fix -v --diff",
|
||||
"test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
include dirname(__DIR__) . "/vendor/autoload.php";
|
||||
include \dirname(__DIR__) . "/vendor/autoload.php";
|
||||
|
||||
use Amp\ByteStream\Message;
|
||||
use Amp\Process\Process;
|
||||
|
@ -1,11 +1,12 @@
|
||||
<?php
|
||||
|
||||
include dirname(__DIR__) . "/vendor/autoload.php";
|
||||
include \dirname(__DIR__) . "/vendor/autoload.php";
|
||||
|
||||
use Amp\Process\Process;
|
||||
use function Amp\Promise\all;
|
||||
|
||||
function show_process_output(Process $process): \Generator {
|
||||
function show_process_output(Process $process): \Generator
|
||||
{
|
||||
$stream = $process->getStdout();
|
||||
|
||||
while (null !== $chunk = yield $stream->read()) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
include dirname(__DIR__) . "/vendor/autoload.php";
|
||||
include \dirname(__DIR__) . "/vendor/autoload.php";
|
||||
|
||||
use Amp\Process\Process;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
include dirname(__DIR__) . "/vendor/autoload.php";
|
||||
include \dirname(__DIR__) . "/vendor/autoload.php";
|
||||
|
||||
use Amp\ByteStream\Message;
|
||||
use Amp\Process\Process;
|
||||
|
@ -6,8 +6,10 @@ use Amp\Deferred;
|
||||
use Amp\Process\Internal\ProcessHandle;
|
||||
|
||||
/** @internal */
|
||||
final class Handle extends ProcessHandle {
|
||||
public function __construct() {
|
||||
final class Handle extends ProcessHandle
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->pidDeferred = new Deferred;
|
||||
$this->joinDeferred = new Deferred;
|
||||
$this->originalParentPid = \getmypid();
|
||||
|
@ -15,7 +15,8 @@ use Amp\Process\ProcessOutputStream;
|
||||
use Amp\Promise;
|
||||
|
||||
/** @internal */
|
||||
final class Runner implements ProcessRunner {
|
||||
final class Runner implements ProcessRunner
|
||||
{
|
||||
const FD_SPEC = [
|
||||
["pipe", "r"], // stdin
|
||||
["pipe", "w"], // stdout
|
||||
@ -23,7 +24,8 @@ final class Runner implements ProcessRunner {
|
||||
["pipe", "w"], // exit code pipe
|
||||
];
|
||||
|
||||
public static function onProcessEndExtraDataPipeReadable($watcher, $stream, Handle $handle) {
|
||||
public static function onProcessEndExtraDataPipeReadable($watcher, $stream, Handle $handle)
|
||||
{
|
||||
Loop::cancel($watcher);
|
||||
$handle->extraDataPipeWatcher = null;
|
||||
|
||||
@ -36,7 +38,8 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
}
|
||||
|
||||
public static function onProcessStartExtraDataPipeReadable($watcher, $stream, $data) {
|
||||
public static function onProcessStartExtraDataPipeReadable($watcher, $stream, $data)
|
||||
{
|
||||
Loop::cancel($watcher);
|
||||
|
||||
$pid = \rtrim(@\fgets($stream));
|
||||
@ -70,7 +73,8 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function start(string $command, string $cwd = null, array $env = [], array $options = []): ProcessHandle {
|
||||
public function start(string $command, string $cwd = null, array $env = [], array $options = []): ProcessHandle
|
||||
{
|
||||
$command = \sprintf(
|
||||
'{ (%s) <&3 3<&- 3>/dev/null & } 3<&0;' .
|
||||
'pid=$!; echo $pid >&3; wait $pid; RC=$?; echo $RC >&3; exit $RC',
|
||||
@ -124,7 +128,8 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function join(ProcessHandle $handle): Promise {
|
||||
public function join(ProcessHandle $handle): Promise
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
if ($handle->extraDataPipeWatcher !== null) {
|
||||
Loop::reference($handle->extraDataPipeWatcher);
|
||||
@ -134,7 +139,8 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function kill(ProcessHandle $handle) {
|
||||
public function kill(ProcessHandle $handle)
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
if ($handle->extraDataPipeWatcher !== null) {
|
||||
Loop::cancel($handle->extraDataPipeWatcher);
|
||||
@ -160,7 +166,8 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function signal(ProcessHandle $handle, int $signo) {
|
||||
public function signal(ProcessHandle $handle, int $signo)
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
if (!\proc_terminate($handle->proc, $signo)) {
|
||||
throw new ProcessException("Sending signal to process failed");
|
||||
@ -168,7 +175,8 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function destroy(ProcessHandle $handle) {
|
||||
public function destroy(ProcessHandle $handle)
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
if ($handle->status < ProcessStatus::ENDED && \getmypid() === $handle->originalParentPid) {
|
||||
try {
|
||||
@ -182,7 +190,8 @@ final class Runner implements ProcessRunner {
|
||||
$this->free($handle);
|
||||
}
|
||||
|
||||
private function free(Handle $handle) {
|
||||
private function free(Handle $handle)
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
if ($handle->extraDataPipeWatcher !== null) {
|
||||
Loop::cancel($handle->extraDataPipeWatcher);
|
||||
|
@ -7,7 +7,8 @@ use Amp\Process\ProcessInputStream;
|
||||
use Amp\Process\ProcessOutputStream;
|
||||
use Amp\Struct;
|
||||
|
||||
abstract class ProcessHandle {
|
||||
abstract class ProcessHandle
|
||||
{
|
||||
use Struct;
|
||||
|
||||
/** @var ProcessOutputStream */
|
||||
|
@ -5,7 +5,8 @@ namespace Amp\Process\Internal;
|
||||
use Amp\Process\ProcessException;
|
||||
use Amp\Promise;
|
||||
|
||||
interface ProcessRunner {
|
||||
interface ProcessRunner
|
||||
{
|
||||
/**
|
||||
* Start a process using the supplied parameters.
|
||||
*
|
||||
|
@ -2,12 +2,14 @@
|
||||
|
||||
namespace Amp\Process\Internal;
|
||||
|
||||
final class ProcessStatus {
|
||||
final class ProcessStatus
|
||||
{
|
||||
const STARTING = 0;
|
||||
const RUNNING = 1;
|
||||
const ENDED = 2;
|
||||
|
||||
private function __construct() {
|
||||
private function __construct()
|
||||
{
|
||||
// empty to prevent instances of this class
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,10 @@ use Amp\Process\Internal\ProcessHandle;
|
||||
* @internal
|
||||
* @codeCoverageIgnore Windows only.
|
||||
*/
|
||||
final class Handle extends ProcessHandle {
|
||||
public function __construct() {
|
||||
final class Handle extends ProcessHandle
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->joinDeferred = new Deferred;
|
||||
$this->pidDeferred = new Deferred;
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ namespace Amp\Process\Internal\Windows;
|
||||
* @internal
|
||||
* @codeCoverageIgnore Windows only.
|
||||
*/
|
||||
final class HandshakeStatus {
|
||||
final class HandshakeStatus
|
||||
{
|
||||
const SUCCESS = 0;
|
||||
const SIGNAL_UNEXPECTED = 0x01;
|
||||
const INVALID_STREAM_ID = 0x02;
|
||||
@ -14,7 +15,8 @@ final class HandshakeStatus {
|
||||
const DUPLICATE_STREAM_ID = 0x04;
|
||||
const INVALID_CLIENT_TOKEN = 0x05;
|
||||
|
||||
private function __construct() {
|
||||
private function __construct()
|
||||
{
|
||||
// empty to prevent instances of this class
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ use Amp\Struct;
|
||||
* @internal
|
||||
* @codeCoverageIgnore Windows only.
|
||||
*/
|
||||
final class PendingSocketClient {
|
||||
final class PendingSocketClient
|
||||
{
|
||||
use Struct;
|
||||
|
||||
public $readWatcher;
|
||||
|
@ -17,7 +17,8 @@ use const Amp\Process\BIN_DIR;
|
||||
* @internal
|
||||
* @codeCoverageIgnore Windows only.
|
||||
*/
|
||||
final class Runner implements ProcessRunner {
|
||||
final class Runner implements ProcessRunner
|
||||
{
|
||||
const FD_SPEC = [
|
||||
["pipe", "r"], // stdin
|
||||
["pipe", "w"], // stdout
|
||||
@ -33,11 +34,12 @@ final class Runner implements ProcessRunner {
|
||||
|
||||
private $socketConnector;
|
||||
|
||||
private function makeCommand(string $workingDirectory): string {
|
||||
private function makeCommand(string $workingDirectory): string
|
||||
{
|
||||
$wrapperPath = self::WRAPPER_EXE_PATH;
|
||||
|
||||
// We can't execute the exe from within the PHAR, so copy it out...
|
||||
if (strncmp($wrapperPath, "phar://", 7) === 0) {
|
||||
if (\strncmp($wrapperPath, "phar://", 7) === 0) {
|
||||
if (self::$pharWrapperPath === null) {
|
||||
self::$pharWrapperPath = \tempnam(\sys_get_temp_dir(), "amphp-process-wrapper-");
|
||||
\copy(self::WRAPPER_EXE_PATH, self::$pharWrapperPath);
|
||||
@ -65,12 +67,14 @@ final class Runner implements ProcessRunner {
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
public function __construct()
|
||||
{
|
||||
$this->socketConnector = new SocketConnector;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function start(string $command, string $cwd = null, array $env = [], array $options = []): ProcessHandle {
|
||||
public function start(string $command, string $cwd = null, array $env = [], array $options = []): ProcessHandle
|
||||
{
|
||||
if (\strpos($command, "\0") !== false) {
|
||||
throw new ProcessException("Can't execute commands that contain null bytes.");
|
||||
}
|
||||
@ -130,7 +134,8 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function join(ProcessHandle $handle): Promise {
|
||||
public function join(ProcessHandle $handle): Promise
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
$handle->exitCodeRequested = true;
|
||||
|
||||
@ -142,7 +147,8 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function kill(ProcessHandle $handle) {
|
||||
public function kill(ProcessHandle $handle)
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
// todo: send a signal to the wrapper to kill the child instead?
|
||||
if (!\proc_terminate($handle->proc)) {
|
||||
@ -174,12 +180,14 @@ final class Runner implements ProcessRunner {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function signal(ProcessHandle $handle, int $signo) {
|
||||
public function signal(ProcessHandle $handle, int $signo)
|
||||
{
|
||||
throw new ProcessException('Signals are not supported on Windows');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function destroy(ProcessHandle $handle) {
|
||||
public function destroy(ProcessHandle $handle)
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
if ($handle->status < ProcessStatus::ENDED && \is_resource($handle->proc)) {
|
||||
try {
|
||||
@ -193,7 +201,8 @@ final class Runner implements ProcessRunner {
|
||||
$this->free($handle);
|
||||
}
|
||||
|
||||
private function free(Handle $handle) {
|
||||
private function free(Handle $handle)
|
||||
{
|
||||
if ($handle->childPidWatcher !== null) {
|
||||
Loop::cancel($handle->childPidWatcher);
|
||||
$handle->childPidWatcher = null;
|
||||
|
@ -6,13 +6,15 @@ namespace Amp\Process\Internal\Windows;
|
||||
* @internal
|
||||
* @codeCoverageIgnore Windows only.
|
||||
*/
|
||||
final class SignalCode {
|
||||
final class SignalCode
|
||||
{
|
||||
const HANDSHAKE = 0x01;
|
||||
const HANDSHAKE_ACK = 0x02;
|
||||
const CHILD_PID = 0x03;
|
||||
const EXIT_CODE = 0x04;
|
||||
|
||||
private function __construct() {
|
||||
private function __construct()
|
||||
{
|
||||
// empty to prevent instances of this class
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ use Amp\Process\ProcessException;
|
||||
* @internal
|
||||
* @codeCoverageIgnore Windows only.
|
||||
*/
|
||||
final class SocketConnector {
|
||||
final class SocketConnector
|
||||
{
|
||||
const SERVER_SOCKET_URI = 'tcp://127.0.0.1:0';
|
||||
const SECURITY_TOKEN_SIZE = 16;
|
||||
const CONNECT_TIMEOUT = 1000;
|
||||
@ -32,7 +33,8 @@ final class SocketConnector {
|
||||
/** @var int */
|
||||
public $port;
|
||||
|
||||
public function __construct() {
|
||||
public function __construct()
|
||||
{
|
||||
$flags = \STREAM_SERVER_LISTEN | \STREAM_SERVER_BIND;
|
||||
$this->server = \stream_socket_server(self::SERVER_SOCKET_URI, $errNo, $errStr, $flags);
|
||||
|
||||
@ -50,14 +52,16 @@ final class SocketConnector {
|
||||
Loop::unreference(Loop::onReadable($this->server, [$this, 'onServerSocketReadable']));
|
||||
}
|
||||
|
||||
private function failClientHandshake($socket, int $code) {
|
||||
private function failClientHandshake($socket, int $code)
|
||||
{
|
||||
\fwrite($socket, \chr(SignalCode::HANDSHAKE_ACK) . \chr($code));
|
||||
\fclose($socket);
|
||||
|
||||
unset($this->pendingClients[(int) $socket]);
|
||||
}
|
||||
|
||||
public function failHandleStart(Handle $handle, string $message, ...$args) {
|
||||
public function failHandleStart(Handle $handle, string $message, ...$args)
|
||||
{
|
||||
Loop::cancel($handle->connectTimeoutWatcher);
|
||||
|
||||
unset($this->pendingProcesses[$handle->wrapperPid]);
|
||||
@ -88,7 +92,8 @@ final class SocketConnector {
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function readDataFromPendingClient($socket, int $length, PendingSocketClient $state) {
|
||||
private function readDataFromPendingClient($socket, int $length, PendingSocketClient $state)
|
||||
{
|
||||
$data = \fread($socket, $length);
|
||||
|
||||
if ($data === false || $data === '') {
|
||||
@ -109,7 +114,8 @@ final class SocketConnector {
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function onReadableHandshake($watcher, $socket) {
|
||||
public function onReadableHandshake($watcher, $socket)
|
||||
{
|
||||
$socketId = (int) $socket;
|
||||
$pendingClient = $this->pendingClients[$socketId];
|
||||
|
||||
@ -169,7 +175,8 @@ final class SocketConnector {
|
||||
$pendingClient->readWatcher = Loop::onReadable($socket, [$this, 'onReadableHandshakeAck']);
|
||||
}
|
||||
|
||||
public function onReadableHandshakeAck($watcher, $socket) {
|
||||
public function onReadableHandshakeAck($watcher, $socket)
|
||||
{
|
||||
$socketId = (int) $socket;
|
||||
$pendingClient = $this->pendingClients[$socketId];
|
||||
|
||||
@ -217,7 +224,8 @@ final class SocketConnector {
|
||||
}
|
||||
}
|
||||
|
||||
public function onReadableChildPid($watcher, $socket, Handle $handle) {
|
||||
public function onReadableChildPid($watcher, $socket, Handle $handle)
|
||||
{
|
||||
$data = \fread($socket, 5);
|
||||
|
||||
if ($data === false || $data === '') {
|
||||
@ -264,7 +272,8 @@ final class SocketConnector {
|
||||
unset($this->pendingProcesses[$handle->wrapperPid]);
|
||||
}
|
||||
|
||||
public function onReadableExitCode($watcher, $socket, Handle $handle) {
|
||||
public function onReadableExitCode($watcher, $socket, Handle $handle)
|
||||
{
|
||||
$data = \fread($socket, 5);
|
||||
|
||||
if ($data === false || $data === '') {
|
||||
@ -305,7 +314,8 @@ final class SocketConnector {
|
||||
}
|
||||
}
|
||||
|
||||
public function onClientSocketConnectTimeout($watcher, $socket) {
|
||||
public function onClientSocketConnectTimeout($watcher, $socket)
|
||||
{
|
||||
$id = (int) $socket;
|
||||
|
||||
Loop::cancel($this->pendingClients[$id]->readWatcher);
|
||||
@ -314,7 +324,8 @@ final class SocketConnector {
|
||||
\fclose($socket);
|
||||
}
|
||||
|
||||
public function onServerSocketReadable() {
|
||||
public function onServerSocketReadable()
|
||||
{
|
||||
$socket = \stream_socket_accept($this->server);
|
||||
|
||||
if (!\stream_set_blocking($socket, false)) {
|
||||
@ -328,7 +339,8 @@ final class SocketConnector {
|
||||
$this->pendingClients[(int) $socket] = $pendingClient;
|
||||
}
|
||||
|
||||
public function onProcessConnectTimeout($watcher, Handle $handle) {
|
||||
public function onProcessConnectTimeout($watcher, Handle $handle)
|
||||
{
|
||||
$running = \is_resource($handle->proc) && \proc_get_status($handle->proc)['running'];
|
||||
|
||||
$error = null;
|
||||
@ -352,7 +364,8 @@ final class SocketConnector {
|
||||
$handle->joinDeferred->fail($error);
|
||||
}
|
||||
|
||||
public function registerPendingProcess(Handle $handle) {
|
||||
public function registerPendingProcess(Handle $handle)
|
||||
{
|
||||
// Use Loop::defer() to start the timeout only after the loop has ticked once. This prevents issues with many
|
||||
// things started at once, see https://github.com/amphp/process/issues/21.
|
||||
$handle->connectTimeoutWatcher = Loop::defer(function () use ($handle) {
|
||||
|
@ -10,7 +10,8 @@ use Amp\Process\Internal\ProcessStatus;
|
||||
use Amp\Process\Internal\Windows\Runner as WindowsProcessRunner;
|
||||
use Amp\Promise;
|
||||
|
||||
class Process {
|
||||
class Process
|
||||
{
|
||||
/** @var ProcessRunner */
|
||||
private $processRunner;
|
||||
|
||||
@ -38,7 +39,8 @@ class Process {
|
||||
*
|
||||
* @throws \Error If the arguments are invalid.
|
||||
*/
|
||||
public function __construct($command, string $cwd = null, array $env = [], array $options = []) {
|
||||
public function __construct($command, string $cwd = null, array $env = [], array $options = [])
|
||||
{
|
||||
$command = \is_array($command)
|
||||
? \implode(" ", \array_map("escapeshellarg", $command))
|
||||
: (string) $command;
|
||||
@ -73,13 +75,15 @@ class Process {
|
||||
/**
|
||||
* Stops the process if it is still running.
|
||||
*/
|
||||
public function __destruct() {
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->handle !== null) {
|
||||
$this->processRunner->destroy($this->handle);
|
||||
}
|
||||
}
|
||||
|
||||
public function __clone() {
|
||||
public function __clone()
|
||||
{
|
||||
throw new \Error("Cloning is not allowed!");
|
||||
}
|
||||
|
||||
@ -88,7 +92,8 @@ class Process {
|
||||
*
|
||||
* @throws StatusError If the process has already been started.
|
||||
*/
|
||||
public function start() {
|
||||
public function start()
|
||||
{
|
||||
if ($this->handle) {
|
||||
throw new StatusError("Process has already been started.");
|
||||
}
|
||||
@ -103,7 +108,8 @@ class Process {
|
||||
*
|
||||
* @throws StatusError If the process has already been started.
|
||||
*/
|
||||
public function join(): Promise {
|
||||
public function join(): Promise
|
||||
{
|
||||
if (!$this->handle) {
|
||||
throw new StatusError("Process has not been started.");
|
||||
}
|
||||
@ -117,7 +123,8 @@ class Process {
|
||||
* @throws StatusError If the process is not running.
|
||||
* @throws ProcessException If terminating the process fails.
|
||||
*/
|
||||
public function kill() {
|
||||
public function kill()
|
||||
{
|
||||
if (!$this->isRunning()) {
|
||||
throw new StatusError("Process is not running.");
|
||||
}
|
||||
@ -133,7 +140,8 @@ class Process {
|
||||
* @throws StatusError If the process is not running.
|
||||
* @throws ProcessException If sending the signal fails.
|
||||
*/
|
||||
public function signal(int $signo) {
|
||||
public function signal(int $signo)
|
||||
{
|
||||
if (!$this->isRunning()) {
|
||||
throw new StatusError("Process is not running.");
|
||||
}
|
||||
@ -148,7 +156,8 @@ class Process {
|
||||
*
|
||||
* @throws StatusError If the process has not started.
|
||||
*/
|
||||
public function getPid(): Promise {
|
||||
public function getPid(): Promise
|
||||
{
|
||||
if (!$this->handle) {
|
||||
throw new StatusError("Process has not been started.");
|
||||
}
|
||||
@ -161,7 +170,8 @@ class Process {
|
||||
*
|
||||
* @return string The command to execute.
|
||||
*/
|
||||
public function getCommand(): string {
|
||||
public function getCommand(): string
|
||||
{
|
||||
return $this->command;
|
||||
}
|
||||
|
||||
@ -170,7 +180,8 @@ class Process {
|
||||
*
|
||||
* @return string The current working directory an empty string if inherited from the current PHP process.
|
||||
*/
|
||||
public function getWorkingDirectory(): string {
|
||||
public function getWorkingDirectory(): string
|
||||
{
|
||||
if ($this->cwd === "") {
|
||||
return \getcwd() ?: "";
|
||||
}
|
||||
@ -183,7 +194,8 @@ class Process {
|
||||
*
|
||||
* @return string[] Array of environment variables.
|
||||
*/
|
||||
public function getEnv(): array {
|
||||
public function getEnv(): array
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
@ -192,7 +204,8 @@ class Process {
|
||||
*
|
||||
* @return mixed[] Array of options.
|
||||
*/
|
||||
public function getOptions(): array {
|
||||
public function getOptions(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
@ -201,7 +214,8 @@ class Process {
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRunning(): bool {
|
||||
public function isRunning(): bool
|
||||
{
|
||||
return $this->handle && $this->handle->status !== ProcessStatus::ENDED;
|
||||
}
|
||||
|
||||
@ -210,7 +224,8 @@ class Process {
|
||||
*
|
||||
* @return ProcessOutputStream
|
||||
*/
|
||||
public function getStdin(): ProcessOutputStream {
|
||||
public function getStdin(): ProcessOutputStream
|
||||
{
|
||||
if (!$this->handle) {
|
||||
throw new StatusError("Process has not been started.");
|
||||
}
|
||||
@ -223,7 +238,8 @@ class Process {
|
||||
*
|
||||
* @return ProcessInputStream
|
||||
*/
|
||||
public function getStdout(): ProcessInputStream {
|
||||
public function getStdout(): ProcessInputStream
|
||||
{
|
||||
if (!$this->handle) {
|
||||
throw new StatusError("Process has not been started.");
|
||||
}
|
||||
@ -236,7 +252,8 @@ class Process {
|
||||
*
|
||||
* @return ProcessInputStream
|
||||
*/
|
||||
public function getStderr(): ProcessInputStream {
|
||||
public function getStderr(): ProcessInputStream
|
||||
{
|
||||
if (!$this->handle) {
|
||||
throw new StatusError("Process has not been started.");
|
||||
}
|
||||
|
@ -2,5 +2,6 @@
|
||||
|
||||
namespace Amp\Process;
|
||||
|
||||
class ProcessException extends \Exception {
|
||||
class ProcessException extends \Exception
|
||||
{
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ use Amp\Failure;
|
||||
use Amp\Promise;
|
||||
use Amp\Success;
|
||||
|
||||
class ProcessInputStream implements InputStream {
|
||||
class ProcessInputStream implements InputStream
|
||||
{
|
||||
/** @var Deferred */
|
||||
private $initialRead;
|
||||
|
||||
@ -27,7 +28,8 @@ class ProcessInputStream implements InputStream {
|
||||
/** @var StreamException|null */
|
||||
private $error;
|
||||
|
||||
public function __construct(Promise $resourceStreamPromise) {
|
||||
public function __construct(Promise $resourceStreamPromise)
|
||||
{
|
||||
$resourceStreamPromise->onResolve(function ($error, $resourceStream) {
|
||||
if ($error) {
|
||||
$this->error = new StreamException("Failed to launch process", 0, $error);
|
||||
@ -64,7 +66,8 @@ class ProcessInputStream implements InputStream {
|
||||
*
|
||||
* @throws PendingReadError Thrown if another read operation is still pending.
|
||||
*/
|
||||
public function read(): Promise {
|
||||
public function read(): Promise
|
||||
{
|
||||
if ($this->initialRead) {
|
||||
throw new PendingReadError;
|
||||
}
|
||||
@ -86,7 +89,8 @@ class ProcessInputStream implements InputStream {
|
||||
return $this->initialRead->promise();
|
||||
}
|
||||
|
||||
public function reference() {
|
||||
public function reference()
|
||||
{
|
||||
$this->referenced = true;
|
||||
|
||||
if ($this->resourceStream) {
|
||||
@ -94,7 +98,8 @@ class ProcessInputStream implements InputStream {
|
||||
}
|
||||
}
|
||||
|
||||
public function unreference() {
|
||||
public function unreference()
|
||||
{
|
||||
$this->referenced = false;
|
||||
|
||||
if ($this->resourceStream) {
|
||||
@ -102,7 +107,8 @@ class ProcessInputStream implements InputStream {
|
||||
}
|
||||
}
|
||||
|
||||
public function close() {
|
||||
public function close()
|
||||
{
|
||||
$this->shouldClose = true;
|
||||
|
||||
if ($this->initialRead) {
|
||||
|
@ -10,7 +10,8 @@ use Amp\Deferred;
|
||||
use Amp\Failure;
|
||||
use Amp\Promise;
|
||||
|
||||
class ProcessOutputStream implements OutputStream {
|
||||
class ProcessOutputStream implements OutputStream
|
||||
{
|
||||
/** @var \SplQueue */
|
||||
private $queuedWrites;
|
||||
|
||||
@ -23,7 +24,8 @@ class ProcessOutputStream implements OutputStream {
|
||||
/** @var StreamException|null */
|
||||
private $error;
|
||||
|
||||
public function __construct(Promise $resourceStreamPromise) {
|
||||
public function __construct(Promise $resourceStreamPromise)
|
||||
{
|
||||
$this->queuedWrites = new \SplQueue;
|
||||
$resourceStreamPromise->onResolve(function ($error, $resourceStream) {
|
||||
if ($error) {
|
||||
@ -55,7 +57,8 @@ class ProcessOutputStream implements OutputStream {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function write(string $data): Promise {
|
||||
public function write(string $data): Promise
|
||||
{
|
||||
if ($this->resourceStream) {
|
||||
return $this->resourceStream->write($data);
|
||||
}
|
||||
@ -75,7 +78,8 @@ class ProcessOutputStream implements OutputStream {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function end(string $finalData = ""): Promise {
|
||||
public function end(string $finalData = ""): Promise
|
||||
{
|
||||
if ($this->resourceStream) {
|
||||
return $this->resourceStream->end($finalData);
|
||||
}
|
||||
@ -96,7 +100,8 @@ class ProcessOutputStream implements OutputStream {
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
public function close() {
|
||||
public function close()
|
||||
{
|
||||
$this->shouldClose = true;
|
||||
|
||||
if ($this->resourceStream) {
|
||||
|
@ -2,5 +2,6 @@
|
||||
|
||||
namespace Amp\Process;
|
||||
|
||||
class StatusError extends \Error {
|
||||
class StatusError extends \Error
|
||||
{
|
||||
}
|
||||
|
@ -11,14 +11,16 @@ use Amp\Process\ProcessOutputStream;
|
||||
use Amp\Promise;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ProcessTest extends TestCase {
|
||||
class ProcessTest extends TestCase
|
||||
{
|
||||
const CMD_PROCESS = \DIRECTORY_SEPARATOR === "\\" ? "cmd /c echo foo" : "echo foo";
|
||||
const CMD_PROCESS_SLOW = \DIRECTORY_SEPARATOR === "\\" ? "cmd /c ping -n 3 127.0.0.1 > nul" : "sleep 2";
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
*/
|
||||
public function testMultipleExecution() {
|
||||
public function testMultipleExecution()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$process->start();
|
||||
@ -26,7 +28,8 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testIsRunning() {
|
||||
public function testIsRunning()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(\DIRECTORY_SEPARATOR === "\\" ? "cmd /c exit 42" : "exit 42");
|
||||
$process->start();
|
||||
@ -40,7 +43,8 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testExecuteResolvesToExitCode() {
|
||||
public function testExecuteResolvesToExitCode()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(\DIRECTORY_SEPARATOR === "\\" ? "cmd /c exit 42" : "exit 42");
|
||||
$process->start();
|
||||
@ -52,7 +56,8 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testCommandCanRun() {
|
||||
public function testCommandCanRun()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$process->start();
|
||||
@ -62,7 +67,8 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testProcessCanTerminate() {
|
||||
public function testProcessCanTerminate()
|
||||
{
|
||||
if (\DIRECTORY_SEPARATOR === "\\") {
|
||||
$this->markTestSkipped("Signals are not supported on Windows");
|
||||
}
|
||||
@ -76,28 +82,32 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testGetWorkingDirectoryIsDefault() {
|
||||
public function testGetWorkingDirectoryIsDefault()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$this->assertSame(getcwd(), $process->getWorkingDirectory());
|
||||
$this->assertSame(\getcwd(), $process->getWorkingDirectory());
|
||||
});
|
||||
}
|
||||
|
||||
public function testGetWorkingDirectoryIsCustomized() {
|
||||
public function testGetWorkingDirectoryIsCustomized()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS, __DIR__);
|
||||
$this->assertSame(__DIR__, $process->getWorkingDirectory());
|
||||
});
|
||||
}
|
||||
|
||||
public function testGetEnv() {
|
||||
public function testGetEnv()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$this->assertSame([], $process->getEnv());
|
||||
});
|
||||
}
|
||||
|
||||
public function testGetStdin() {
|
||||
public function testGetStdin()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$process->start();
|
||||
@ -106,7 +116,8 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testGetStdout() {
|
||||
public function testGetStdout()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$process->start();
|
||||
@ -115,7 +126,8 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testGetStderr() {
|
||||
public function testGetStderr()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$process->start();
|
||||
@ -124,7 +136,8 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testProcessEnvIsValid() {
|
||||
public function testProcessEnvIsValid()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS, null, [
|
||||
'test' => 'foobar',
|
||||
@ -140,7 +153,8 @@ class ProcessTest extends TestCase {
|
||||
/**
|
||||
* @expectedException \Error
|
||||
*/
|
||||
public function testProcessEnvIsInvalid() {
|
||||
public function testProcessEnvIsInvalid()
|
||||
{
|
||||
$process = new Process(self::CMD_PROCESS, null, [
|
||||
['error_value']
|
||||
]);
|
||||
@ -150,7 +164,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
* @expectedExceptionMessage Process has not been started.
|
||||
*/
|
||||
public function testGetStdinIsStatusError() {
|
||||
public function testGetStdinIsStatusError()
|
||||
{
|
||||
$process = new Process(self::CMD_PROCESS, null, []);
|
||||
$process->getStdin();
|
||||
}
|
||||
@ -159,7 +174,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
* @expectedExceptionMessage Process has not been started.
|
||||
*/
|
||||
public function testGetStdoutIsStatusError() {
|
||||
public function testGetStdoutIsStatusError()
|
||||
{
|
||||
$process = new Process(self::CMD_PROCESS, null, []);
|
||||
$process->getStdout();
|
||||
}
|
||||
@ -168,7 +184,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
* @expectedExceptionMessage Process has not been started.
|
||||
*/
|
||||
public function testGetStderrIsStatusError() {
|
||||
public function testGetStderrIsStatusError()
|
||||
{
|
||||
$process = new Process(self::CMD_PROCESS, null, []);
|
||||
$process->getStderr();
|
||||
}
|
||||
@ -177,7 +194,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Error
|
||||
* @expectedExceptionMessage Cloning is not allowed!
|
||||
*/
|
||||
public function testProcessCantBeCloned() {
|
||||
public function testProcessCantBeCloned()
|
||||
{
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
clone $process;
|
||||
}
|
||||
@ -186,7 +204,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\ProcessException
|
||||
* @expectedExceptionMessage The process was killed
|
||||
*/
|
||||
public function testKillImmediately() {
|
||||
public function testKillImmediately()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS_SLOW);
|
||||
$process->start();
|
||||
@ -199,7 +218,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\ProcessException
|
||||
* @expectedExceptionMessage The process was killed
|
||||
*/
|
||||
public function testKillThenReadStdout() {
|
||||
public function testKillThenReadStdout()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS_SLOW);
|
||||
$process->start();
|
||||
@ -219,7 +239,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
* @expectedExceptionMessage Process has not been started.
|
||||
*/
|
||||
public function testProcessHasNotBeenStartedWithJoin() {
|
||||
public function testProcessHasNotBeenStartedWithJoin()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
yield $process->join();
|
||||
@ -230,7 +251,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
* @expectedExceptionMessage Process has not been started.
|
||||
*/
|
||||
public function testProcessHasNotBeenStartedWithGetPid() {
|
||||
public function testProcessHasNotBeenStartedWithGetPid()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
yield $process->getPid();
|
||||
@ -241,7 +263,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
* @expectedExceptionMessage Process is not running.
|
||||
*/
|
||||
public function testProcessIsNotRunningWithKill() {
|
||||
public function testProcessIsNotRunningWithKill()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$process->kill();
|
||||
@ -252,7 +275,8 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
* @expectedExceptionMessage Process is not running.
|
||||
*/
|
||||
public function testProcessIsNotRunningWithSignal() {
|
||||
public function testProcessIsNotRunningWithSignal()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$process->signal(0);
|
||||
@ -263,24 +287,28 @@ class ProcessTest extends TestCase {
|
||||
* @expectedException \Amp\Process\StatusError
|
||||
* @expectedExceptionMessage Process has not been started.
|
||||
*/
|
||||
public function testProcessHasBeenStarted() {
|
||||
public function testProcessHasBeenStarted()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
yield $process->join();
|
||||
});
|
||||
}
|
||||
|
||||
public function testCommand() {
|
||||
public function testCommand()
|
||||
{
|
||||
$process = new Process([self::CMD_PROCESS]);
|
||||
$this->assertSame(\implode(" ", \array_map("escapeshellarg", [self::CMD_PROCESS])), $process->getCommand());
|
||||
}
|
||||
|
||||
public function testOptions() {
|
||||
public function testOptions()
|
||||
{
|
||||
$process = new Process(self::CMD_PROCESS);
|
||||
$this->assertSame([], $process->getOptions());
|
||||
}
|
||||
|
||||
public function getProcessCounts(): array {
|
||||
public function getProcessCounts(): array
|
||||
{
|
||||
return \array_map(function (int $count): array {
|
||||
return [$count];
|
||||
}, \range(2, 32, 2));
|
||||
@ -291,7 +319,8 @@ class ProcessTest extends TestCase {
|
||||
*
|
||||
* @param int $count
|
||||
*/
|
||||
public function testSpawnMultipleProcesses(int $count) {
|
||||
public function testSpawnMultipleProcesses(int $count)
|
||||
{
|
||||
Loop::run(function () use ($count) {
|
||||
$processes = [];
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
@ -309,7 +338,8 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testReadOutputAfterExit() {
|
||||
public function testReadOutputAfterExit()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(["php", __DIR__ . "/bin/worker.php"]);
|
||||
$process->start();
|
||||
@ -321,14 +351,15 @@ class ProcessTest extends TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testReadOutputAfterExitWithLongOutput() {
|
||||
public function testReadOutputAfterExitWithLongOutput()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$process = new Process(["php", __DIR__ . "/bin/worker.php"]);
|
||||
$process->start();
|
||||
|
||||
$count = 128 * 1024 + 1;
|
||||
$process->getStdin()->write("exit " . $count);
|
||||
$this->assertSame(str_repeat(".", $count), yield new Message($process->getStdout()));
|
||||
$this->assertSame(\str_repeat(".", $count), yield new Message($process->getStdout()));
|
||||
|
||||
$this->assertSame(0, yield $process->join());
|
||||
});
|
||||
|
@ -1,15 +1,15 @@
|
||||
<?php
|
||||
|
||||
$content = fread(STDIN, 1024);
|
||||
$content = \fread(STDIN, 1024);
|
||||
|
||||
$command = explode(" ", $content);
|
||||
$command = \explode(" ", $content);
|
||||
|
||||
if (count($command) !== 2) {
|
||||
if (\count($command) !== 2) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ($command[0] === "exit") {
|
||||
echo str_repeat(".", (int) $command[1]);
|
||||
echo \str_repeat(".", (int) $command[1]);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user