* Returns true in case the output is disabled, false otherwise.
public function isOutputDisabled()
return $this->outputDisabled;
* Returns the current output of the process (STDOUT).
* @return string The process output
* @throws LogicException in case the output has been disabled
* @throws LogicException In case the process is not started
public function getOutput()
$this->readPipesForOutput(__FUNCTION__);
if (false === $ret = stream_get_contents($this->stdout, -1, 0)) {
* Returns the output incrementally.
* In comparison with the getOutput method which always return the whole
* output, this one returns the new output since the last call.
* @return string The process output since the last call
* @throws LogicException in case the output has been disabled
* @throws LogicException In case the process is not started
public function getIncrementalOutput()
$this->readPipesForOutput(__FUNCTION__);
$latest = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
$this->incrementalOutputOffset = ftell($this->stdout);
* Returns an iterator to the output of the process, with the output type as keys (Process::OUT/ERR).
* @param int $flags A bit field of Process::ITER_* flags
* @throws LogicException in case the output has been disabled
* @throws LogicException In case the process is not started
public function getIterator($flags = 0)
$this->readPipesForOutput(__FUNCTION__, false);
$clearOutput = !(self::ITER_KEEP_OUTPUT & $flags);
$blocking = !(self::ITER_NON_BLOCKING & $flags);
$yieldOut = !(self::ITER_SKIP_OUT & $flags);
$yieldErr = !(self::ITER_SKIP_ERR & $flags);
while (null !== $this->callback || ($yieldOut && !feof($this->stdout)) || ($yieldErr && !feof($this->stderr))) {
$out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
$this->incrementalOutputOffset = ftell($this->stdout);
$err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
$this->clearErrorOutput();
$this->incrementalErrorOutputOffset = ftell($this->stderr);
if (!$blocking && !isset($out[0]) && !isset($err[0])) {
$this->readPipesForOutput(__FUNCTION__, $blocking);
* Clears the process output.
public function clearOutput()
ftruncate($this->stdout, 0);
$this->incrementalOutputOffset = 0;
* Returns the current error output of the process (STDERR).
* @return string The process error output
* @throws LogicException in case the output has been disabled
* @throws LogicException In case the process is not started
public function getErrorOutput()
$this->readPipesForOutput(__FUNCTION__);
if (false === $ret = stream_get_contents($this->stderr, -1, 0)) {
* Returns the errorOutput incrementally.
* In comparison with the getErrorOutput method which always return the
* whole error output, this one returns the new error output since the last
* @return string The process error output since the last call
* @throws LogicException in case the output has been disabled
* @throws LogicException In case the process is not started
public function getIncrementalErrorOutput()
$this->readPipesForOutput(__FUNCTION__);
$latest = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
$this->incrementalErrorOutputOffset = ftell($this->stderr);
* Clears the process output.
public function clearErrorOutput()
ftruncate($this->stderr, 0);
$this->incrementalErrorOutputOffset = 0;
* Returns the exit code returned by the process.
* @return int|null The exit status code, null if the Process is not terminated
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
public function getExitCode()
if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
$this->updateStatus(false);
* Returns a string representation for the exit code returned by the process.
* This method relies on the Unix exit code status standardization
* and might not be relevant for other operating systems.
* @return string|null A string representation for the exit status code, null if the Process is not terminated
* @see http://tldp.org/LDP/abs/html/exitcodes.html
* @see http://en.wikipedia.org/wiki/Unix_signal
public function getExitCodeText()
if (null === $exitcode = $this->getExitCode()) {
return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
* Checks if the process ended successfully.
* @return bool true if the process ended successfully, false otherwise
public function isSuccessful()
return 0 === $this->getExitCode();
* Returns true if the child process has been terminated by an uncaught signal.
* It always returns false on Windows.
* @throws RuntimeException In case --enable-sigchild is activated
* @throws LogicException In case the process is not terminated
public function hasBeenSignaled()
$this->requireProcessIsTerminated(__FUNCTION__);
if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
return $this->processInformation['signaled'];
* Returns the number of the signal that caused the child process to terminate its execution.
* It is only meaningful if hasBeenSignaled() returns true.
* @throws RuntimeException In case --enable-sigchild is activated
* @throws LogicException In case the process is not terminated
public function getTermSignal()
$this->requireProcessIsTerminated(__FUNCTION__);
if ($this->isSigchildEnabled() && (!$this->enhanceSigchildCompatibility || -1 === $this->processInformation['termsig'])) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
return $this->processInformation['termsig'];
* Returns true if the child process has been stopped by a signal.
* It always returns false on Windows.
* @throws LogicException In case the process is not terminated
public function hasBeenStopped()
$this->requireProcessIsTerminated(__FUNCTION__);
return $this->processInformation['stopped'];
* Returns the number of the signal that caused the child process to stop its execution.
* It is only meaningful if hasBeenStopped() returns true.
* @throws LogicException In case the process is not terminated
public function getStopSignal()
$this->requireProcessIsTerminated(__FUNCTION__);
return $this->processInformation['stopsig'];
* Checks if the process is currently running.
* @return bool true if the process is currently running, false otherwise
public function isRunning()
if (self::STATUS_STARTED !== $this->status) {
$this->updateStatus(false);
return $this->processInformation['running'];
* Checks if the process has been started with no regard to the current state.
* @return bool true if status is ready, false otherwise
public function isStarted()
return self::STATUS_READY != $this->status;
* Checks if the process is terminated.
* @return bool true if process is terminated, false otherwise
public function isTerminated()
$this->updateStatus(false);
return self::STATUS_TERMINATED == $this->status;
* Gets the process status.
* The status is one of: ready, started, terminated.
* @return string The current process status
public function getStatus()
$this->updateStatus(false);
* @param int|float $timeout The timeout in seconds
* @param int $signal A POSIX signal to send in case the process has not stop at timeout, default is SIGKILL (9)
* @return int|null The exit-code of the process or null if it's not running
public function stop($timeout = 10, $signal = null)
$timeoutMicro = microtime(true) + $timeout;
if ($this->isRunning()) {
// given `SIGTERM` may not be defined and that `proc_terminate` uses the constant value and not the constant itself, we use the same here
$this->doSignal(15, false);
} while ($this->isRunning() && microtime(true) < $timeoutMicro);
if ($this->isRunning()) {
// Avoid exception here: process is supposed to be running, but it might have stopped just
// after this line. In any case, let's silently discard the error, we cannot do anything.
$this->doSignal($signal ?: 9, false);
if ($this->isRunning()) {
if (isset($this->fallbackStatus['pid'])) {
unset($this->fallbackStatus['pid']);
return $this->stop(0, $signal);
* Adds a line to the STDOUT stream.
* @param string $line The line to append
public function addOutput($line)
$this->lastOutputTime = microtime(true);
fseek($this->stdout, 0, \SEEK_END);
fwrite($this->stdout, $line);
fseek($this->stdout, $this->incrementalOutputOffset);
* Adds a line to the STDERR stream.
* @param string $line The line to append
public function addErrorOutput($line)
$this->lastOutputTime = microtime(true);
fseek($this->stderr, 0, \SEEK_END);
fwrite($this->stderr, $line);
fseek($this->stderr, $this->incrementalErrorOutputOffset);
* Gets the command line to be executed.
* @return string The command to execute
public function getCommandLine()
return \is_array($this->commandline) ? implode(' ', array_map([$this, 'escapeArgument'], $this->commandline)) : $this->commandline;
* Sets the command line to be executed.
* @param string|array $commandline The command to execute
public function setCommandLine($commandline)
$this->commandline = $commandline;
* Gets the process timeout (max. runtime).
* @return float|null The timeout in seconds or null if it's disabled
public function getTimeout()
* Gets the process idle timeout (max. time since last output).
* @return float|null The timeout in seconds or null if it's disabled
public function getIdleTimeout()
return $this->idleTimeout;
* Sets the process timeout (max. runtime) in seconds.
* To disable the timeout, set this value to null.
* @param int|float|null $timeout The timeout in seconds
* @throws InvalidArgumentException if the timeout is negative
public function setTimeout($timeout)
$this->timeout = $this->validateTimeout($timeout);
* Sets the process idle timeout (max. time since last output).
* To disable the timeout, set this value to null.
* @param int|float|null $timeout The timeout in seconds