$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 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 * * @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 { 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"); } 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"); } return $this->handle->stderr; } } (function () { /** @noinspection PhpUndefinedClassInspection */ self::$processRunner = \strncasecmp(\PHP_OS, "WIN", 3) === 0 ? new WindowsProcessRunner : new PosixProcessRunner; })->bindTo(null, Process::class)();