1
0
mirror of https://github.com/danog/process.git synced 2024-12-12 09:09:44 +01:00
process/lib/Process.php

239 lines
6.2 KiB
PHP
Raw Normal View History

<?php
namespace Amp\Process;
use Amp\Process\Internal\Posix\Runner as PosixProcessRunner;
use Amp\Process\Internal\ProcessHandle;
use Amp\Process\Internal\ProcessRunner;
use Amp\Process\Internal\ProcessStatus;
use Amp\Process\Internal\Windows\Runner as WindowsProcessRunner;
2017-06-15 19:02:50 +02:00
use Amp\Promise;
class Process {
/** @var ProcessRunner */
private static $processRunner;
/** @var string */
private $command;
/** @var string */
private $cwd = "";
/** @var array */
private $env = [];
/** @var array */
private $options;
/** @var ProcessHandle */
private $handle;
/**
* @param string|string[] $command Command to run.
* @param string|null $cwd Working directory or use an empty string to use the working directory of the
* parent.
* @param mixed[] $env Environment variables or use an empty array to inherit from the parent.
* @param mixed[] $options Options for `proc_open()`.
*
* @throws \Error If the arguments are invalid.
*/
public function __construct($command, string $cwd = null, array $env = [], array $options = []) {
$command = \is_array($command)
? \implode(" ", \array_map("escapeshellarg", $command))
: (string) $command;
$cwd = $cwd ?? "";
$envVars = [];
foreach ($env as $key => $value) {
if (\is_array($value)) {
throw new \Error("\$env cannot accept array values");
}
$envVars[(string) $key] = (string) $value;
}
$this->command = $command;
$this->cwd = $cwd;
$this->env = $envVars;
$this->options = $options;
}
/**
* Stops the process if it is still running.
*/
public function __destruct() {
if ($this->handle !== null) {
self::$processRunner->destroy($this->handle);
}
}
public function __clone() {
throw new \Error("Cloning is not allowed!");
}
/**
* Start the process.
*
* @throws StatusError If the process has already been started.
*/
public function start() {
if ($this->handle) {
throw new StatusError("Process has already been started.");
}
$this->handle = self::$processRunner->start($this->command, $this->cwd, $this->env, $this->options);
}
/**
* Wait for the process to end.
*
* @return Promise <int> Succeeds with process exit code or fails with a ProcessException if the process is killed.
*
* @throws StatusError If the process has already been started.
*/
public function join(): Promise {
if (!$this->handle) {
throw new StatusError("Process has not been started.");
}
return self::$processRunner->join($this->handle);
}
/**
* Forcibly end the process.
*
* @throws StatusError If the process is not running.
* @throws ProcessException If terminating the process fails.
*/
public function kill() {
if (!$this->isRunning()) {
throw new StatusError("The process is not running");
}
self::$processRunner->kill($this->handle);
}
/**
* Send a signal signal to the process.
*
* @param int $signo Signal number to send to process.
*
* @throws StatusError If the process is not running.
* @throws ProcessException If sending the signal fails.
*/
public function signal(int $signo) {
if (!$this->isRunning()) {
throw new StatusError("The process is not running");
}
self::$processRunner->signal($this->handle, $signo);
}
/**
* Returns the PID of the child process.
*
* @return Promise<int>
*
* @throws StatusError If the process has not started.
*/
public function getPid(): Promise {
if (!$this->handle) {
throw new StatusError("The process has not been started");
}
return $this->handle->pidDeferred->promise();
}
/**
* Returns the command to execute.
*
* @return string The command to execute.
*/
public function getCommand(): string {
return $this->command;
}
/**
* Gets the current working directory.
*
* @return string The current working directory an empty string if inherited from the current PHP process.
*/
public function getWorkingDirectory(): string {
if ($this->cwd === "") {
return \getcwd() ?: "";
}
return $this->cwd;
}
/**
* Gets the environment variables array.
*
* @return string[] Array of environment variables.
*/
public function getEnv(): array {
return $this->env;
}
/**
* Gets the options to pass to proc_open().
*
* @return mixed[] Array of options.
*/
public function getOptions(): array {
return $this->options;
}
/**
* Determines if the process is still running.
*
* @return bool
*/
public function isRunning(): bool {
return $this->handle && $this->handle->status !== ProcessStatus::ENDED;
}
/**
* Gets the process input stream (STDIN).
*
* @return ProcessOutputStream
*/
public function getStdin(): ProcessOutputStream {
2017-09-17 21:06:05 +02:00
return $this->handle->stdin;
}
/**
* Gets the process output stream (STDOUT).
*
* @return ProcessInputStream
*/
public function getStdout(): ProcessInputStream {
if (!$this->isRunning()) {
throw new StatusError("The process is not running");
}
2017-09-17 21:06:05 +02:00
return $this->handle->stdout;
}
/**
* Gets the process error stream (STDERR).
*
* @return ProcessInputStream
*/
public function getStderr(): ProcessInputStream {
if (!$this->isRunning()) {
throw new StatusError("The process is not running");
}
2017-09-17 21:06:05 +02:00
return $this->handle->stderr;
}
}
(function () {
/** @noinspection PhpUndefinedClassInspection */
self::$processRunner = \strncasecmp(\PHP_OS, "WIN", 3) === 0
? new WindowsProcessRunner
: new PosixProcessRunner;
})->bindTo(null, Process::class)();