diff --git a/CHANGELOG.md b/CHANGELOG.md index 69d4cbd7..31b9ee6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,26 @@ CHANGELOG ========= +5.2.0 +----- + + * added `Process::setOptions()` to set `Process` specific options + * added option `create_new_console` to allow a subprocess to continue + to run after the main script exited, both on Linux and on Windows + +5.1.0 +----- + + * added `Process::getStartTime()` to retrieve the start time of the process as float + +5.0.0 +----- + + * removed `Process::inheritEnvironmentVariables()` + * removed `PhpProcess::setPhpBinary()` + * `Process` must be instantiated with a command array, use `Process::fromShellCommandline()` when the command should be parsed by the shell + * removed `Process::setCommandLine()` + 4.4.0 ----- diff --git a/ExecutableFinder.php b/ExecutableFinder.php index e2dd064d..eb8f0629 100644 --- a/ExecutableFinder.php +++ b/ExecutableFinder.php @@ -31,10 +31,8 @@ public function setSuffixes(array $suffixes) /** * Adds new possible suffix to check for executable. - * - * @param string $suffix */ - public function addSuffix($suffix) + public function addSuffix(string $suffix) { $this->suffixes[] = $suffix; } @@ -46,9 +44,9 @@ public function addSuffix($suffix) * @param string|null $default The default to return if no executable is found * @param array $extraDirs Additional dirs to check into * - * @return string|null The executable path or default value + * @return string|null */ - public function find($name, $default = null, array $extraDirs = []) + public function find(string $name, string $default = null, array $extraDirs = []) { if (\ini_get('open_basedir')) { $searchPath = array_merge(explode(\PATH_SEPARATOR, \ini_get('open_basedir')), $extraDirs); diff --git a/InputStream.php b/InputStream.php index 4f8f7133..240665f3 100644 --- a/InputStream.php +++ b/InputStream.php @@ -17,6 +17,8 @@ * Provides a way to continuously write to the input of a Process until the InputStream is closed. * * @author Nicolas Grekas + * + * @implements \IteratorAggregate */ class InputStream implements \IteratorAggregate { @@ -67,7 +69,7 @@ public function isClosed() } /** - * @return \Traversable + * @return \Traversable */ #[\ReturnTypeWillChange] public function getIterator() diff --git a/LICENSE b/LICENSE index 88bf75bb..0138f8f0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PhpExecutableFinder.php b/PhpExecutableFinder.php index 92e0262a..998808b6 100644 --- a/PhpExecutableFinder.php +++ b/PhpExecutableFinder.php @@ -29,11 +29,9 @@ public function __construct() /** * Finds The PHP executable. * - * @param bool $includeArgs Whether or not include command arguments - * - * @return string|false The PHP executable path or false if it cannot be found + * @return string|false */ - public function find($includeArgs = true) + public function find(bool $includeArgs = true) { if ($php = getenv('PHP_BINARY')) { if (!is_executable($php)) { @@ -91,7 +89,7 @@ public function find($includeArgs = true) /** * Finds the PHP executable arguments. * - * @return array The PHP executable arguments + * @return array */ public function findArguments() { diff --git a/PhpProcess.php b/PhpProcess.php index dc064e0b..2bc338e5 100644 --- a/PhpProcess.php +++ b/PhpProcess.php @@ -58,18 +58,6 @@ public static function fromShellCommandline(string $command, string $cwd = null, throw new LogicException(sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class)); } - /** - * Sets the path to the PHP binary to use. - * - * @deprecated since Symfony 4.2, use the $php argument of the constructor instead. - */ - public function setPhpBinary($php) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the $php argument of the constructor instead.', __METHOD__), \E_USER_DEPRECATED); - - $this->setCommandLine($php); - } - /** * {@inheritdoc} */ diff --git a/Pipes/AbstractPipes.php b/Pipes/AbstractPipes.php index 9532e3ef..656dc032 100644 --- a/Pipes/AbstractPipes.php +++ b/Pipes/AbstractPipes.php @@ -105,7 +105,7 @@ protected function write(): ?array } elseif (!isset($this->inputBuffer[0])) { if (!\is_string($input)) { if (!\is_scalar($input)) { - throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', \get_class($this->input), \gettype($input))); + throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', get_debug_type($this->input), get_debug_type($input))); } $input = (string) $input; } diff --git a/Pipes/UnixPipes.php b/Pipes/UnixPipes.php index 58a8da07..5a0e9d47 100644 --- a/Pipes/UnixPipes.php +++ b/Pipes/UnixPipes.php @@ -35,10 +35,7 @@ public function __construct(?bool $ttyMode, bool $ptyMode, $input, bool $haveRea parent::__construct($input); } - /** - * @return array - */ - public function __sleep() + public function __sleep(): array { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } diff --git a/Pipes/WindowsPipes.php b/Pipes/WindowsPipes.php index 69768f3d..bca84f57 100644 --- a/Pipes/WindowsPipes.php +++ b/Pipes/WindowsPipes.php @@ -88,10 +88,7 @@ public function __construct($input, bool $haveReadSupport) parent::__construct($input); } - /** - * @return array - */ - public function __sleep() + public function __sleep(): array { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } diff --git a/Process.php b/Process.php index 09cd9602..b47ecca1 100644 --- a/Process.php +++ b/Process.php @@ -27,6 +27,8 @@ * * @author Fabien Potencier * @author Romain Neutron + * + * @implements \IteratorAggregate */ class Process implements \IteratorAggregate { @@ -71,6 +73,7 @@ class Process implements \IteratorAggregate private $incrementalErrorOutputOffset = 0; private $tty = false; private $pty; + private $options = ['suppress_errors' => true, 'bypass_shell' => true]; private $useFileHandles = false; /** @var PipesInterface */ @@ -137,16 +140,12 @@ class Process implements \IteratorAggregate * * @throws LogicException When proc_open is not installed */ - public function __construct($command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) + public function __construct(array $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) { if (!\function_exists('proc_open')) { throw new LogicException('The Process class relies on proc_open, which is not available on your PHP installation.'); } - if (!\is_array($command)) { - @trigger_error(sprintf('Passing a command as string when creating a "%s" instance is deprecated since Symfony 4.2, pass it as an array of its arguments instead, or use the "Process::fromShellCommandline()" constructor if you need features provided by the shell.', __CLASS__), \E_USER_DEPRECATED); - } - $this->commandline = $command; $this->cwd = $cwd; @@ -213,7 +212,11 @@ public function __wakeup() public function __destruct() { - $this->stop(0); + if ($this->options['create_new_console'] ?? false) { + $this->processPipes->close(); + } else { + $this->stop(0); + } } public function __clone() @@ -320,10 +323,7 @@ public function start(callable $callback = null, array $env = []) $commandline = $this->replacePlaceholders($commandline, $env); } - $options = ['suppress_errors' => true]; - if ('\\' === \DIRECTORY_SEPARATOR) { - $options['bypass_shell'] = true; $commandline = $this->prepareWindowsCommandLine($commandline, $env); } elseif (!$this->useFileHandles && $this->isSigchildEnabled()) { // last exit code is output on the fourth pipe and caught to work around --enable-sigchild @@ -349,7 +349,7 @@ public function start(callable $callback = null, array $env = []) throw new RuntimeException(sprintf('The provided cwd "%s" does not exist.', $this->cwd)); } - $this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $options); + $this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options); if (!\is_resource($this->process)) { throw new RuntimeException('Unable to launch a new process.'); @@ -511,7 +511,7 @@ public function getPid() * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed * @throws RuntimeException In case of failure */ - public function signal($signal) + public function signal(int $signal) { $this->doSignal($signal, true); @@ -532,7 +532,7 @@ public function disableOutput() throw new RuntimeException('Disabling output while the process is running is not possible.'); } if (null !== $this->idleTimeout) { - throw new LogicException('Output can not be disabled while an idle timeout is set.'); + throw new LogicException('Output cannot be disabled while an idle timeout is set.'); } $this->outputDisabled = true; @@ -571,7 +571,7 @@ public function isOutputDisabled() /** * Returns the current output of the process (STDOUT). * - * @return string The process output + * @return string * * @throws LogicException in case the output has been disabled * @throws LogicException In case the process is not started @@ -593,7 +593,7 @@ public function getOutput() * 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 + * @return string * * @throws LogicException in case the output has been disabled * @throws LogicException In case the process is not started @@ -617,13 +617,13 @@ public function getIncrementalOutput() * * @param int $flags A bit field of Process::ITER_* flags * + * @return \Generator + * * @throws LogicException in case the output has been disabled * @throws LogicException In case the process is not started - * - * @return \Generator */ #[\ReturnTypeWillChange] - public function getIterator($flags = 0) + public function getIterator(int $flags = 0) { $this->readPipesForOutput(__FUNCTION__, false); @@ -687,7 +687,7 @@ public function clearOutput() /** * Returns the current error output of the process (STDERR). * - * @return string The process error output + * @return string * * @throws LogicException in case the output has been disabled * @throws LogicException In case the process is not started @@ -710,7 +710,7 @@ public function getErrorOutput() * whole error output, this one returns the new error output since the last * call. * - * @return string The process error output since the last call + * @return string * * @throws LogicException in case the output has been disabled * @throws LogicException In case the process is not started @@ -778,7 +778,7 @@ public function getExitCodeText() /** * Checks if the process ended successfully. * - * @return bool true if the process ended successfully, false otherwise + * @return bool */ public function isSuccessful() { @@ -816,7 +816,7 @@ public function getTermSignal() $this->requireProcessIsTerminated(__FUNCTION__); if ($this->isSigchildEnabled() && -1 === $this->processInformation['termsig']) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); + throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal cannot be retrieved.'); } return $this->processInformation['termsig']; @@ -857,7 +857,7 @@ public function getStopSignal() /** * Checks if the process is currently running. * - * @return bool true if the process is currently running, false otherwise + * @return bool */ public function isRunning() { @@ -873,7 +873,7 @@ public function isRunning() /** * Checks if the process has been started with no regard to the current state. * - * @return bool true if status is ready, false otherwise + * @return bool */ public function isStarted() { @@ -883,7 +883,7 @@ public function isStarted() /** * Checks if the process is terminated. * - * @return bool true if process is terminated, false otherwise + * @return bool */ public function isTerminated() { @@ -897,7 +897,7 @@ public function isTerminated() * * The status is one of: ready, started, terminated. * - * @return string The current process status + * @return string */ public function getStatus() { @@ -910,11 +910,11 @@ public function getStatus() * Stops the process. * * @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) + * @param int|null $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) + public function stop(float $timeout = 10, int $signal = null) { $timeoutMicro = microtime(true) + $timeout; if ($this->isRunning()) { @@ -982,7 +982,7 @@ public function getLastOutputTime(): ?float /** * Gets the command line to be executed. * - * @return string The command to execute + * @return string */ public function getCommandLine() { @@ -990,27 +990,9 @@ public function getCommandLine() } /** - * Sets the command line to be executed. + * Gets the process timeout in seconds (max. runtime). * - * @param string|array $commandline The command to execute - * - * @return $this - * - * @deprecated since Symfony 4.2. - */ - public function setCommandLine($commandline) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), \E_USER_DEPRECATED); - - $this->commandline = $commandline; - - return $this; - } - - /** - * Gets the process timeout (max. runtime). - * - * @return float|null The timeout in seconds or null if it's disabled + * @return float|null */ public function getTimeout() { @@ -1018,9 +1000,9 @@ public function getTimeout() } /** - * Gets the process idle timeout (max. time since last output). + * Gets the process idle timeout in seconds (max. time since last output). * - * @return float|null The timeout in seconds or null if it's disabled + * @return float|null */ public function getIdleTimeout() { @@ -1032,13 +1014,11 @@ public function getIdleTimeout() * * To disable the timeout, set this value to null. * - * @param int|float|null $timeout The timeout in seconds - * * @return $this * * @throws InvalidArgumentException if the timeout is negative */ - public function setTimeout($timeout) + public function setTimeout(?float $timeout) { $this->timeout = $this->validateTimeout($timeout); @@ -1046,21 +1026,19 @@ public function setTimeout($timeout) } /** - * Sets the process idle timeout (max. time since last output). + * Sets the process idle timeout (max. time since last output) in seconds. * * To disable the timeout, set this value to null. * - * @param int|float|null $timeout The timeout in seconds - * * @return $this * * @throws LogicException if the output is disabled * @throws InvalidArgumentException if the timeout is negative */ - public function setIdleTimeout($timeout) + public function setIdleTimeout(?float $timeout) { if (null !== $timeout && $this->outputDisabled) { - throw new LogicException('Idle timeout can not be set while the output is disabled.'); + throw new LogicException('Idle timeout cannot be set while the output is disabled.'); } $this->idleTimeout = $this->validateTimeout($timeout); @@ -1071,13 +1049,11 @@ public function setIdleTimeout($timeout) /** * Enables or disables the TTY mode. * - * @param bool $tty True to enabled and false to disable - * * @return $this * * @throws RuntimeException In case the TTY mode is not supported */ - public function setTty($tty) + public function setTty(bool $tty) { if ('\\' === \DIRECTORY_SEPARATOR && $tty) { throw new RuntimeException('TTY mode is not supported on Windows platform.'); @@ -1087,7 +1063,7 @@ public function setTty($tty) throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.'); } - $this->tty = (bool) $tty; + $this->tty = $tty; return $this; } @@ -1095,7 +1071,7 @@ public function setTty($tty) /** * Checks if the TTY mode is enabled. * - * @return bool true if the TTY mode is enabled, false otherwise + * @return bool */ public function isTty() { @@ -1105,13 +1081,11 @@ public function isTty() /** * Sets PTY mode. * - * @param bool $bool - * * @return $this */ - public function setPty($bool) + public function setPty(bool $bool) { - $this->pty = (bool) $bool; + $this->pty = $bool; return $this; } @@ -1129,7 +1103,7 @@ public function isPty() /** * Gets the working directory. * - * @return string|null The current working directory or null on failure + * @return string|null */ public function getWorkingDirectory() { @@ -1145,11 +1119,9 @@ public function getWorkingDirectory() /** * Sets the current working directory. * - * @param string $cwd The new working directory - * * @return $this */ - public function setWorkingDirectory($cwd) + public function setWorkingDirectory(string $cwd) { $this->cwd = $cwd; @@ -1159,7 +1131,7 @@ public function setWorkingDirectory($cwd) /** * Gets the environment variables. * - * @return array The current environment variables + * @return array */ public function getEnv() { @@ -1183,7 +1155,7 @@ public function setEnv(array $env) /** * Gets the Process input. * - * @return resource|string|\Iterator|null The Process input + * @return resource|string|\Iterator|null */ public function getInput() { @@ -1204,7 +1176,7 @@ public function getInput() public function setInput($input) { if ($this->isRunning()) { - throw new LogicException('Input can not be set while the process is running.'); + throw new LogicException('Input cannot be set while the process is running.'); } $this->input = ProcessUtils::validateInput(__METHOD__, $input); @@ -1212,26 +1184,6 @@ public function setInput($input) return $this; } - /** - * Sets whether environment variables will be inherited or not. - * - * @param bool $inheritEnv - * - * @return $this - * - * @deprecated since Symfony 4.4, env variables are always inherited - */ - public function inheritEnvironmentVariables($inheritEnv = true) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.4, env variables are always inherited.', __METHOD__), \E_USER_DEPRECATED); - - if (!$inheritEnv) { - throw new InvalidArgumentException('Not inheriting environment variables is not supported.'); - } - - return $this; - } - /** * Performs a check between the timeout definition and the time the process started. * @@ -1259,6 +1211,44 @@ public function checkTimeout() } } + /** + * @throws LogicException in case process is not started + */ + public function getStartTime(): float + { + if (!$this->isStarted()) { + throw new LogicException('Start time is only available after process start.'); + } + + return $this->starttime; + } + + /** + * Defines options to pass to the underlying proc_open(). + * + * @see https://php.net/proc_open for the options supported by PHP. + * + * Enabling the "create_new_console" option allows a subprocess to continue + * to run after the main process exited, on both Windows and *nix + */ + public function setOptions(array $options) + { + if ($this->isRunning()) { + throw new RuntimeException('Setting options while the process is running is not possible.'); + } + + $defaultOptions = $this->options; + $existingOptions = ['blocking_pipes', 'create_process_group', 'create_new_console']; + + foreach ($options as $key => $value) { + if (!\in_array($key, $existingOptions)) { + $this->options = $defaultOptions; + throw new LogicException(sprintf('Invalid option "%s" passed to "%s()". Supported options are "%s".', $key, __METHOD__, implode('", "', $existingOptions))); + } + $this->options[$key] = $value; + } + } + /** * Returns whether TTY is supported on the current operating system. */ @@ -1318,7 +1308,7 @@ private function getDescriptors(): array * * @param callable|null $callback The user defined PHP callback * - * @return \Closure A PHP closure + * @return \Closure */ protected function buildCallback(callable $callback = null) { @@ -1346,7 +1336,7 @@ protected function buildCallback(callable $callback = null) * * @param bool $blocking Whether to use a blocking read call */ - protected function updateStatus($blocking) + protected function updateStatus(bool $blocking) { if (self::STATUS_STARTED !== $this->status) { return; @@ -1509,7 +1499,7 @@ private function doSignal(int $signal, bool $throwException): bool { if (null === $pid = $this->getPid()) { if ($throwException) { - throw new LogicException('Can not send signal on a non running process.'); + throw new LogicException('Cannot send signal on a non running process.'); } return false; diff --git a/ProcessUtils.php b/ProcessUtils.php index 121693ba..2a7aff71 100644 --- a/ProcessUtils.php +++ b/ProcessUtils.php @@ -35,11 +35,11 @@ private function __construct() * @param string $caller The name of method call that validates the input * @param mixed $input The input to validate * - * @return mixed The validated input + * @return mixed * * @throws InvalidArgumentException In case the input is not valid */ - public static function validateInput($caller, $input) + public static function validateInput(string $caller, $input) { if (null !== $input) { if (\is_resource($input)) { diff --git a/README.md b/README.md index afce5e45..8777de4a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,17 @@ Process Component The Process component executes commands in sub-processes. +Sponsor +------- + +The Process component for Symfony 5.4/6.0 is [backed][1] by [SensioLabs][2]. + +As the creator of Symfony, SensioLabs supports companies using Symfony, with an +offering encompassing consultancy, expertise, services, training, and technical +assistance to ensure the success of web application development projects. + +Help Symfony by [sponsoring][3] its development! + Resources --------- @@ -11,3 +22,7 @@ Resources * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) + +[1]: https://symfony.com/backers +[2]: https://sensiolabs.com +[3]: https://symfony.com/sponsor diff --git a/Tests/CreateNewConsoleTest.php b/Tests/CreateNewConsoleTest.php new file mode 100644 index 00000000..4d43fb8d --- /dev/null +++ b/Tests/CreateNewConsoleTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\Process; + +/** + * @author Andrei Olteanu + */ +class CreateNewConsoleTest extends TestCase +{ + public function testOptionCreateNewConsole() + { + $this->expectNotToPerformAssertions(); + try { + $process = new Process(['php', __DIR__.'/ThreeSecondProcess.php']); + $process->setOptions(['create_new_console' => true]); + $process->disableOutput(); + $process->start(); + } catch (\Exception $e) { + $this->fail($e); + } + } + + public function testItReturnsFastAfterStart() + { + // The started process must run in background after the main has finished but that can't be tested with PHPUnit + $startTime = microtime(true); + $process = new Process(['php', __DIR__.'/ThreeSecondProcess.php']); + $process->setOptions(['create_new_console' => true]); + $process->disableOutput(); + $process->start(); + $this->assertLessThan(3000, $startTime - microtime(true)); + } +} diff --git a/Tests/ProcessFailedExceptionTest.php b/Tests/ProcessFailedExceptionTest.php index d6d7bfb0..259ffd63 100644 --- a/Tests/ProcessFailedExceptionTest.php +++ b/Tests/ProcessFailedExceptionTest.php @@ -25,7 +25,7 @@ class ProcessFailedExceptionTest extends TestCase */ public function testProcessFailedExceptionThrowsException() { - $process = $this->getMockBuilder(Process::class)->setMethods(['isSuccessful'])->setConstructorArgs([['php']])->getMock(); + $process = $this->getMockBuilder(Process::class)->onlyMethods(['isSuccessful'])->setConstructorArgs([['php']])->getMock(); $process->expects($this->once()) ->method('isSuccessful') ->willReturn(true); @@ -49,7 +49,7 @@ public function testProcessFailedExceptionPopulatesInformationFromProcessOutput( $errorOutput = 'FATAL: Unexpected error'; $workingDirectory = getcwd(); - $process = $this->getMockBuilder(Process::class)->setMethods(['isSuccessful', 'getOutput', 'getErrorOutput', 'getExitCode', 'getExitCodeText', 'isOutputDisabled', 'getWorkingDirectory'])->setConstructorArgs([[$cmd]])->getMock(); + $process = $this->getMockBuilder(Process::class)->onlyMethods(['isSuccessful', 'getOutput', 'getErrorOutput', 'getExitCode', 'getExitCodeText', 'isOutputDisabled', 'getWorkingDirectory'])->setConstructorArgs([[$cmd]])->getMock(); $process->expects($this->once()) ->method('isSuccessful') ->willReturn(false); @@ -97,7 +97,7 @@ public function testDisabledOutputInFailedExceptionDoesNotPopulateOutput() $exitText = 'General error'; $workingDirectory = getcwd(); - $process = $this->getMockBuilder(Process::class)->setMethods(['isSuccessful', 'isOutputDisabled', 'getExitCode', 'getExitCodeText', 'getOutput', 'getErrorOutput', 'getWorkingDirectory'])->setConstructorArgs([[$cmd]])->getMock(); + $process = $this->getMockBuilder(Process::class)->onlyMethods(['isSuccessful', 'isOutputDisabled', 'getExitCode', 'getExitCodeText', 'getOutput', 'getErrorOutput', 'getWorkingDirectory'])->setConstructorArgs([[$cmd]])->getMock(); $process->expects($this->once()) ->method('isSuccessful') ->willReturn(false); diff --git a/Tests/ProcessTest.php b/Tests/ProcessTest.php index 74c662fb..790167fc 100644 --- a/Tests/ProcessTest.php +++ b/Tests/ProcessTest.php @@ -276,7 +276,7 @@ public function testLiveStreamAsInput() public function testSetInputWhileRunningThrowsAnException() { $this->expectException(LogicException::class); - $this->expectExceptionMessage('Input can not be set while the process is running.'); + $this->expectExceptionMessage('Input cannot be set while the process is running.'); $process = $this->getProcessForCode('sleep(30);'); $process->start(); try { @@ -301,7 +301,7 @@ public function testInvalidInput($value) $process->setInput($value); } - public function provideInvalidInputValues() + public static function provideInvalidInputValues() { return [ [[]], @@ -319,7 +319,7 @@ public function testValidInput($expected, $value) $this->assertSame($expected, $process->getInput()); } - public function provideInputValues() + public static function provideInputValues() { return [ [null, null], @@ -328,7 +328,7 @@ public function provideInputValues() ]; } - public function chainedCommandsOutputProvider() + public static function chainedCommandsOutputProvider() { if ('\\' === \DIRECTORY_SEPARATOR) { return [ @@ -422,7 +422,7 @@ public function testIncrementalOutput($getOutput, $getIncrementalOutput, $uri) fclose($h); } - public function provideIncrementalOutput() + public static function provideIncrementalOutput() { return [ ['getOutput', 'getIncrementalOutput', 'php://stdout'], @@ -939,7 +939,7 @@ public function testExitCodeIsAvailableAfterSignal() public function testSignalProcessNotRunning() { $this->expectException(LogicException::class); - $this->expectExceptionMessage('Can not send signal on a non running process.'); + $this->expectExceptionMessage('Cannot send signal on a non running process.'); $process = $this->getProcess('foo'); $process->signal(1); // SIGHUP } @@ -957,7 +957,7 @@ public function testMethodsThatNeedARunningProcess($method) $process->{$method}(); } - public function provideMethodsThatNeedARunningProcess() + public static function provideMethodsThatNeedARunningProcess() { return [ ['getOutput'], @@ -988,7 +988,7 @@ public function testMethodsThatNeedATerminatedProcess($method) throw $e; } - public function provideMethodsThatNeedATerminatedProcess() + public static function provideMethodsThatNeedATerminatedProcess() { return [ ['hasBeenSignaled'], @@ -1058,7 +1058,7 @@ public function testEnableOrDisableOutputAfterRunDoesNotThrowException() public function testDisableOutputWhileIdleTimeoutIsSet() { $this->expectException(LogicException::class); - $this->expectExceptionMessage('Output can not be disabled while an idle timeout is set.'); + $this->expectExceptionMessage('Output cannot be disabled while an idle timeout is set.'); $process = $this->getProcess('foo'); $process->setIdleTimeout(1); $process->disableOutput(); @@ -1067,7 +1067,7 @@ public function testDisableOutputWhileIdleTimeoutIsSet() public function testSetIdleTimeoutWhileOutputIsDisabled() { $this->expectException(LogicException::class); - $this->expectExceptionMessage('timeout can not be set while the output is disabled.'); + $this->expectExceptionMessage('timeout cannot be set while the output is disabled.'); $process = $this->getProcess('foo'); $process->disableOutput(); $process->setIdleTimeout(1); @@ -1093,7 +1093,7 @@ public function testGetOutputWhileDisabled($fetchMethod) $p->{$fetchMethod}(); } - public function provideOutputFetchingMethods() + public static function provideOutputFetchingMethods() { return [ ['getOutput'], @@ -1130,7 +1130,7 @@ public function testTermSignalTerminatesProcessCleanly() $this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException'); } - public function responsesCodeProvider() + public static function responsesCodeProvider() { return [ // expected output / getter / code to execute @@ -1140,7 +1140,7 @@ public function responsesCodeProvider() ]; } - public function pipesCodeProvider() + public static function pipesCodeProvider() { $variations = [ 'fwrite(STDOUT, $in = file_get_contents(\'php://stdin\')); fwrite(STDERR, $in);', @@ -1183,7 +1183,7 @@ public function testIncrementalOutputDoesNotRequireAnotherCall($stream, $method) $process->stop(); } - public function provideVariousIncrementals() + public static function provideVariousIncrementals() { return [ ['php://stdout', 'getIncrementalOutput'], @@ -1449,7 +1449,7 @@ public function testRawCommandLine() $this->assertSame($expected, str_replace('Standard input code', '-', $p->getOutput())); } - public function provideEscapeArgument() + public static function provideEscapeArgument() { yield ['a"b%c%']; yield ['a"b^c^']; diff --git a/Tests/ThreeSecondProcess.php b/Tests/ThreeSecondProcess.php new file mode 100644 index 00000000..e483b4b9 --- /dev/null +++ b/Tests/ThreeSecondProcess.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +echo 'Worker started'; +sleep(3); +echo 'Worker done'; diff --git a/composer.json b/composer.json index c0f7599f..1669eba5 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=7.1.3", + "php": ">=7.2.5", "symfony/polyfill-php80": "^1.16" }, "autoload": {