updated-packages

This commit is contained in:
RafficMohammed
2023-01-08 00:13:22 +05:30
parent 3ff7df7487
commit da241bacb6
12659 changed files with 563377 additions and 510538 deletions

View File

@@ -1,3 +0,0 @@
vendor/
composer.lock
phpunit.xml

View File

@@ -1,6 +1,12 @@
CHANGELOG
=========
4.4.0
-----
* deprecated `Process::inheritEnvironmentVariables()`: env variables are always inherited.
* added `Process::getLastOutputTime()` method
4.2.0
-----
@@ -21,9 +27,9 @@ CHANGELOG
-----
* environment variables will always be inherited
* added a second `array $env = array()` argument to the `start()`, `run()`,
* added a second `array $env = []` argument to the `start()`, `run()`,
`mustRun()`, and `restart()` methods of the `Process` class
* added a second `array $env = array()` argument to the `start()` method of the
* added a second `array $env = []` argument to the `start()` method of the
`PhpProcess` class
* the `ProcessUtils::escapeArgument()` method has been removed
* the `areEnvironmentVariablesInherited()`, `getOptions()`, and `setOptions()`

View File

@@ -20,8 +20,8 @@ use Symfony\Component\Process\Process;
*/
class ProcessTimedOutException extends RuntimeException
{
const TYPE_GENERAL = 1;
const TYPE_IDLE = 2;
public const TYPE_GENERAL = 1;
public const TYPE_IDLE = 2;
private $process;
private $timeoutType;

View File

@@ -19,7 +19,7 @@ namespace Symfony\Component\Process;
*/
class ExecutableFinder
{
private $suffixes = array('.exe', '.bat', '.cmd', '.com');
private $suffixes = ['.exe', '.bat', '.cmd', '.com'];
/**
* Replaces default suffixes of executable.
@@ -42,17 +42,17 @@ class ExecutableFinder
/**
* Finds an executable by name.
*
* @param string $name The executable name (without the extension)
* @param string $default The default to return if no executable is found
* @param array $extraDirs Additional dirs to check into
* @param string $name The executable name (without the extension)
* @param string|null $default The default to return if no executable is found
* @param array $extraDirs Additional dirs to check into
*
* @return string The executable path or default value
* @return string|null The executable path or default value
*/
public function find($name, $default = null, array $extraDirs = array())
public function find($name, $default = null, array $extraDirs = [])
{
if (ini_get('open_basedir')) {
$searchPath = explode(PATH_SEPARATOR, ini_get('open_basedir'));
$dirs = array();
if (\ini_get('open_basedir')) {
$searchPath = array_merge(explode(\PATH_SEPARATOR, \ini_get('open_basedir')), $extraDirs);
$dirs = [];
foreach ($searchPath as $path) {
// Silencing against https://bugs.php.net/69240
if (@is_dir($path)) {
@@ -65,15 +65,15 @@ class ExecutableFinder
}
} else {
$dirs = array_merge(
explode(PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
explode(\PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
$extraDirs
);
}
$suffixes = array('');
$suffixes = [''];
if ('\\' === \DIRECTORY_SEPARATOR) {
$pathExt = getenv('PATHEXT');
$suffixes = array_merge($pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes);
$suffixes = array_merge($pathExt ? explode(\PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes);
}
foreach ($suffixes as $suffix) {
foreach ($dirs as $dir) {

View File

@@ -22,7 +22,7 @@ class InputStream implements \IteratorAggregate
{
/** @var callable|null */
private $onEmpty = null;
private $input = array();
private $input = [];
private $open = true;
/**
@@ -45,7 +45,7 @@ class InputStream implements \IteratorAggregate
return;
}
if ($this->isClosed()) {
throw new RuntimeException(sprintf('%s is closed', static::class));
throw new RuntimeException(sprintf('"%s" is closed.', static::class));
}
$this->input[] = ProcessUtils::validateInput(__METHOD__, $input);
}
@@ -66,6 +66,10 @@ class InputStream implements \IteratorAggregate
return !$this->open;
}
/**
* @return \Traversable
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
$this->open = true;

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2018 Fabien Potencier
Copyright (c) 2004-2022 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

View File

@@ -38,7 +38,7 @@ class PhpExecutableFinder
if ($php = getenv('PHP_BINARY')) {
if (!is_executable($php)) {
$command = '\\' === \DIRECTORY_SEPARATOR ? 'where' : 'command -v';
if ($php = strtok(exec($command.' '.escapeshellarg($php)), PHP_EOL)) {
if ($php = strtok(exec($command.' '.escapeshellarg($php)), \PHP_EOL)) {
if (!is_executable($php)) {
return false;
}
@@ -47,6 +47,10 @@ class PhpExecutableFinder
}
}
if (@is_dir($php)) {
return false;
}
return $php;
}
@@ -54,12 +58,12 @@ class PhpExecutableFinder
$args = $includeArgs && $args ? ' '.implode(' ', $args) : '';
// PHP_BINARY return the current sapi executable
if (PHP_BINARY && \in_array(\PHP_SAPI, array('cli', 'cli-server', 'phpdbg'), true)) {
return PHP_BINARY.$args;
if (\PHP_BINARY && \in_array(\PHP_SAPI, ['cgi-fcgi', 'cli', 'cli-server', 'phpdbg'], true)) {
return \PHP_BINARY.$args;
}
if ($php = getenv('PHP_PATH')) {
if (!@is_executable($php)) {
if (!@is_executable($php) || @is_dir($php)) {
return false;
}
@@ -67,16 +71,16 @@ class PhpExecutableFinder
}
if ($php = getenv('PHP_PEAR_PHP_BIN')) {
if (@is_executable($php)) {
if (@is_executable($php) && !@is_dir($php)) {
return $php;
}
}
if (@is_executable($php = PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) {
if (@is_executable($php = \PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php')) && !@is_dir($php)) {
return $php;
}
$dirs = array(PHP_BINDIR);
$dirs = [\PHP_BINDIR];
if ('\\' === \DIRECTORY_SEPARATOR) {
$dirs[] = 'C:\xampp\php\\';
}
@@ -91,7 +95,7 @@ class PhpExecutableFinder
*/
public function findArguments()
{
$arguments = array();
$arguments = [];
if ('phpdbg' === \PHP_SAPI) {
$arguments[] = '-qrr';
}

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Process;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Process\Exception\RuntimeException;
/**
@@ -33,11 +34,10 @@ class PhpProcess extends Process
*/
public function __construct(string $script, string $cwd = null, array $env = null, int $timeout = 60, array $php = null)
{
$executableFinder = new PhpExecutableFinder();
if (false === $php = $php ?? $executableFinder->find(false)) {
$php = null;
} else {
$php = array_merge(array($php), $executableFinder->findArguments());
if (null === $php) {
$executableFinder = new PhpExecutableFinder();
$php = $executableFinder->find(false);
$php = false === $php ? null : array_merge([$php], $executableFinder->findArguments());
}
if ('phpdbg' === \PHP_SAPI) {
$file = tempnam(sys_get_temp_dir(), 'dbg');
@@ -50,6 +50,14 @@ class PhpProcess extends Process
parent::__construct($php, $cwd, $env, $script, $timeout);
}
/**
* {@inheritdoc}
*/
public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
{
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.
*
@@ -57,7 +65,7 @@ class PhpProcess extends Process
*/
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);
@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);
}
@@ -65,7 +73,7 @@ class PhpProcess extends Process
/**
* {@inheritdoc}
*/
public function start(callable $callback = null, array $env = array())
public function start(callable $callback = null, array $env = [])
{
if (null === $this->getCommandLine()) {
throw new RuntimeException('Unable to find the PHP executable.');

View File

@@ -20,7 +20,7 @@ use Symfony\Component\Process\Exception\InvalidArgumentException;
*/
abstract class AbstractPipes implements PipesInterface
{
public $pipes = array();
public $pipes = [];
private $inputBuffer = '';
private $input;
@@ -47,17 +47,17 @@ abstract class AbstractPipes implements PipesInterface
public function close()
{
foreach ($this->pipes as $pipe) {
fclose($pipe);
if (\is_resource($pipe)) {
fclose($pipe);
}
}
$this->pipes = array();
$this->pipes = [];
}
/**
* Returns true if a system call has been interrupted.
*
* @return bool
*/
protected function hasSystemCallBeenInterrupted()
protected function hasSystemCallBeenInterrupted(): bool
{
$lastError = $this->lastError;
$this->lastError = null;
@@ -90,10 +90,10 @@ abstract class AbstractPipes implements PipesInterface
*
* @throws InvalidArgumentException When an input iterator yields a non supported value
*/
protected function write()
protected function write(): ?array
{
if (!isset($this->pipes[0])) {
return;
return null;
}
$input = $this->input;
@@ -104,8 +104,8 @@ abstract class AbstractPipes implements PipesInterface
stream_set_blocking($input, 0);
} 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)));
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)));
}
$input = (string) $input;
}
@@ -117,12 +117,12 @@ abstract class AbstractPipes implements PipesInterface
}
}
$r = $e = array();
$w = array($this->pipes[0]);
$r = $e = [];
$w = [$this->pipes[0]];
// let's have a look if something changed in streams
if (false === @stream_select($r, $w, $e, 0, 0)) {
return;
return null;
}
foreach ($w as $stdin) {
@@ -130,12 +130,12 @@ abstract class AbstractPipes implements PipesInterface
$written = fwrite($stdin, $this->inputBuffer);
$this->inputBuffer = substr($this->inputBuffer, $written);
if (isset($this->inputBuffer[0])) {
return array($this->pipes[0]);
return [$this->pipes[0]];
}
}
if ($input) {
for (;;) {
while (true) {
$data = fread($input, self::CHUNK_SIZE);
if (!isset($data[0])) {
break;
@@ -145,7 +145,7 @@ abstract class AbstractPipes implements PipesInterface
if (isset($data[0])) {
$this->inputBuffer = $data;
return array($this->pipes[0]);
return [$this->pipes[0]];
}
}
if (feof($input)) {
@@ -164,14 +164,16 @@ abstract class AbstractPipes implements PipesInterface
fclose($this->pipes[0]);
unset($this->pipes[0]);
} elseif (!$w) {
return array($this->pipes[0]);
return [$this->pipes[0]];
}
return null;
}
/**
* @internal
*/
public function handleError($type, $msg)
public function handleError(int $type, string $msg)
{
$this->lastError = $msg;
}

View File

@@ -20,21 +20,19 @@ namespace Symfony\Component\Process\Pipes;
*/
interface PipesInterface
{
const CHUNK_SIZE = 16384;
public const CHUNK_SIZE = 16384;
/**
* Returns an array of descriptors for the use of proc_open.
*
* @return array
*/
public function getDescriptors();
public function getDescriptors(): array;
/**
* Returns an array of filenames indexed by their related stream in case these pipes use temporary files.
*
* @return string[]
*/
public function getFiles();
public function getFiles(): array;
/**
* Reads data in file handles and pipes.
@@ -44,21 +42,17 @@ interface PipesInterface
*
* @return string[] An array of read data indexed by their fd
*/
public function readAndWrite($blocking, $close = false);
public function readAndWrite(bool $blocking, bool $close = false): array;
/**
* Returns if the current state has open file handles or pipes.
*
* @return bool
*/
public function areOpen();
public function areOpen(): bool;
/**
* Returns if pipes are able to read output.
*
* @return bool
*/
public function haveReadSupport();
public function haveReadSupport(): bool;
/**
* Closes file handles and pipes.

View File

@@ -35,6 +35,19 @@ class UnixPipes extends AbstractPipes
parent::__construct($input);
}
/**
* @return array
*/
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->close();
@@ -43,69 +56,69 @@ class UnixPipes extends AbstractPipes
/**
* {@inheritdoc}
*/
public function getDescriptors()
public function getDescriptors(): array
{
if (!$this->haveReadSupport) {
$nullstream = fopen('/dev/null', 'c');
return array(
array('pipe', 'r'),
return [
['pipe', 'r'],
$nullstream,
$nullstream,
);
];
}
if ($this->ttyMode) {
return array(
array('file', '/dev/tty', 'r'),
array('file', '/dev/tty', 'w'),
array('file', '/dev/tty', 'w'),
);
return [
['file', '/dev/tty', 'r'],
['file', '/dev/tty', 'w'],
['file', '/dev/tty', 'w'],
];
}
if ($this->ptyMode && Process::isPtySupported()) {
return array(
array('pty'),
array('pty'),
array('pty'),
);
return [
['pty'],
['pty'],
['pty'],
];
}
return array(
array('pipe', 'r'),
array('pipe', 'w'), // stdout
array('pipe', 'w'), // stderr
);
return [
['pipe', 'r'],
['pipe', 'w'], // stdout
['pipe', 'w'], // stderr
];
}
/**
* {@inheritdoc}
*/
public function getFiles()
public function getFiles(): array
{
return array();
return [];
}
/**
* {@inheritdoc}
*/
public function readAndWrite($blocking, $close = false)
public function readAndWrite(bool $blocking, bool $close = false): array
{
$this->unblock();
$w = $this->write();
$read = $e = array();
$read = $e = [];
$r = $this->pipes;
unset($r[0]);
// let's have a look if something changed in streams
set_error_handler(array($this, 'handleError'));
set_error_handler([$this, 'handleError']);
if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
restore_error_handler();
// if a system call has been interrupted, forget about it, let's try again
// otherwise, an error occurred, let's reset pipes
if (!$this->hasSystemCallBeenInterrupted()) {
$this->pipes = array();
$this->pipes = [];
}
return $read;
@@ -118,7 +131,7 @@ class UnixPipes extends AbstractPipes
$read[$type = array_search($pipe, $this->pipes, true)] = '';
do {
$data = fread($pipe, self::CHUNK_SIZE);
$data = @fread($pipe, self::CHUNK_SIZE);
$read[$type] .= $data;
} while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1])));
@@ -138,7 +151,7 @@ class UnixPipes extends AbstractPipes
/**
* {@inheritdoc}
*/
public function haveReadSupport()
public function haveReadSupport(): bool
{
return $this->haveReadSupport;
}
@@ -146,7 +159,7 @@ class UnixPipes extends AbstractPipes
/**
* {@inheritdoc}
*/
public function areOpen()
public function areOpen(): bool
{
return (bool) $this->pipes;
}

View File

@@ -17,8 +17,8 @@ use Symfony\Component\Process\Process;
/**
* WindowsPipes implementation uses temporary files as handles.
*
* @see https://bugs.php.net/bug.php?id=51800
* @see https://bugs.php.net/bug.php?id=65650
* @see https://bugs.php.net/51800
* @see https://bugs.php.net/65650
*
* @author Romain Neutron <imprec@gmail.com>
*
@@ -26,13 +26,13 @@ use Symfony\Component\Process\Process;
*/
class WindowsPipes extends AbstractPipes
{
private $files = array();
private $fileHandles = array();
private $lockHandles = array();
private $readBytes = array(
private $files = [];
private $fileHandles = [];
private $lockHandles = [];
private $readBytes = [
Process::STDOUT => 0,
Process::STDERR => 0,
);
];
private $haveReadSupport;
public function __construct($input, bool $haveReadSupport)
@@ -43,11 +43,11 @@ class WindowsPipes extends AbstractPipes
// Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big.
// Workaround for this problem is to use temporary files instead of pipes on Windows platform.
//
// @see https://bugs.php.net/bug.php?id=51800
$pipes = array(
// @see https://bugs.php.net/51800
$pipes = [
Process::STDOUT => Process::OUT,
Process::STDERR => Process::ERR,
);
];
$tmpDir = sys_get_temp_dir();
$lastError = 'unknown reason';
set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; });
@@ -56,20 +56,23 @@ class WindowsPipes extends AbstractPipes
$file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name);
if (!$h = fopen($file.'.lock', 'w')) {
if (file_exists($file.'.lock')) {
continue 2;
}
restore_error_handler();
throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s', $lastError));
throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError);
}
if (!flock($h, LOCK_EX | LOCK_NB)) {
if (!flock($h, \LOCK_EX | \LOCK_NB)) {
continue 2;
}
if (isset($this->lockHandles[$pipe])) {
flock($this->lockHandles[$pipe], LOCK_UN);
flock($this->lockHandles[$pipe], \LOCK_UN);
fclose($this->lockHandles[$pipe]);
}
$this->lockHandles[$pipe] = $h;
if (!fclose(fopen($file, 'w')) || !$h = fopen($file, 'r')) {
flock($this->lockHandles[$pipe], LOCK_UN);
if (!($h = fopen($file, 'w')) || !fclose($h) || !$h = fopen($file, 'r')) {
flock($this->lockHandles[$pipe], \LOCK_UN);
fclose($this->lockHandles[$pipe]);
unset($this->lockHandles[$pipe]);
continue 2;
@@ -85,6 +88,19 @@ class WindowsPipes extends AbstractPipes
parent::__construct($input);
}
/**
* @return array
*/
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->close();
@@ -93,32 +109,32 @@ class WindowsPipes extends AbstractPipes
/**
* {@inheritdoc}
*/
public function getDescriptors()
public function getDescriptors(): array
{
if (!$this->haveReadSupport) {
$nullstream = fopen('NUL', 'c');
return array(
array('pipe', 'r'),
return [
['pipe', 'r'],
$nullstream,
$nullstream,
);
];
}
// We're not using pipe on Windows platform as it hangs (https://bugs.php.net/bug.php?id=51800)
// We're not using file handles as it can produce corrupted output https://bugs.php.net/bug.php?id=65650
// We're not using pipe on Windows platform as it hangs (https://bugs.php.net/51800)
// We're not using file handles as it can produce corrupted output https://bugs.php.net/65650
// So we redirect output within the commandline and pass the nul device to the process
return array(
array('pipe', 'r'),
array('file', 'NUL', 'w'),
array('file', 'NUL', 'w'),
);
return [
['pipe', 'r'],
['file', 'NUL', 'w'],
['file', 'NUL', 'w'],
];
}
/**
* {@inheritdoc}
*/
public function getFiles()
public function getFiles(): array
{
return $this->files;
}
@@ -126,11 +142,11 @@ class WindowsPipes extends AbstractPipes
/**
* {@inheritdoc}
*/
public function readAndWrite($blocking, $close = false)
public function readAndWrite(bool $blocking, bool $close = false): array
{
$this->unblock();
$w = $this->write();
$read = $r = $e = array();
$read = $r = $e = [];
if ($blocking) {
if ($w) {
@@ -149,7 +165,7 @@ class WindowsPipes extends AbstractPipes
if ($close) {
ftruncate($fileHandle, 0);
fclose($fileHandle);
flock($this->lockHandles[$type], LOCK_UN);
flock($this->lockHandles[$type], \LOCK_UN);
fclose($this->lockHandles[$type]);
unset($this->fileHandles[$type], $this->lockHandles[$type]);
}
@@ -161,7 +177,7 @@ class WindowsPipes extends AbstractPipes
/**
* {@inheritdoc}
*/
public function haveReadSupport()
public function haveReadSupport(): bool
{
return $this->haveReadSupport;
}
@@ -169,7 +185,7 @@ class WindowsPipes extends AbstractPipes
/**
* {@inheritdoc}
*/
public function areOpen()
public function areOpen(): bool
{
return $this->pipes && $this->fileHandles;
}
@@ -183,9 +199,9 @@ class WindowsPipes extends AbstractPipes
foreach ($this->fileHandles as $type => $handle) {
ftruncate($handle, 0);
fclose($handle);
flock($this->lockHandles[$type], LOCK_UN);
flock($this->lockHandles[$type], \LOCK_UN);
fclose($this->lockHandles[$type]);
}
$this->fileHandles = $this->lockHandles = array();
$this->fileHandles = $this->lockHandles = [];
}
}

View File

@@ -30,37 +30,37 @@ use Symfony\Component\Process\Pipes\WindowsPipes;
*/
class Process implements \IteratorAggregate
{
const ERR = 'err';
const OUT = 'out';
public const ERR = 'err';
public const OUT = 'out';
const STATUS_READY = 'ready';
const STATUS_STARTED = 'started';
const STATUS_TERMINATED = 'terminated';
public const STATUS_READY = 'ready';
public const STATUS_STARTED = 'started';
public const STATUS_TERMINATED = 'terminated';
const STDIN = 0;
const STDOUT = 1;
const STDERR = 2;
public const STDIN = 0;
public const STDOUT = 1;
public const STDERR = 2;
// Timeout Precision in seconds.
const TIMEOUT_PRECISION = 0.2;
public const TIMEOUT_PRECISION = 0.2;
const ITER_NON_BLOCKING = 1; // By default, iterating over outputs is a blocking call, use this flag to make it non-blocking
const ITER_KEEP_OUTPUT = 2; // By default, outputs are cleared while iterating, use this flag to keep them in memory
const ITER_SKIP_OUT = 4; // Use this flag to skip STDOUT while iterating
const ITER_SKIP_ERR = 8; // Use this flag to skip STDERR while iterating
public const ITER_NON_BLOCKING = 1; // By default, iterating over outputs is a blocking call, use this flag to make it non-blocking
public const ITER_KEEP_OUTPUT = 2; // By default, outputs are cleared while iterating, use this flag to keep them in memory
public const ITER_SKIP_OUT = 4; // Use this flag to skip STDOUT while iterating
public const ITER_SKIP_ERR = 8; // Use this flag to skip STDERR while iterating
private $callback;
private $hasCallback = false;
private $commandline;
private $cwd;
private $env;
private $env = [];
private $input;
private $starttime;
private $lastOutputTime;
private $timeout;
private $idleTimeout;
private $exitcode;
private $fallbackStatus = array();
private $fallbackStatus = [];
private $processInformation;
private $outputDisabled = false;
private $stdout;
@@ -69,7 +69,7 @@ class Process implements \IteratorAggregate
private $status = self::STATUS_READY;
private $incrementalOutputOffset = 0;
private $incrementalErrorOutputOffset = 0;
private $tty;
private $tty = false;
private $pty;
private $useFileHandles = false;
@@ -85,7 +85,7 @@ class Process implements \IteratorAggregate
*
* User-defined errors must use exit codes in the 64-113 range.
*/
public static $exitCodes = array(
public static $exitCodes = [
0 => 'OK',
1 => 'General error',
2 => 'Misuse of shell builtins',
@@ -126,16 +126,16 @@ class Process implements \IteratorAggregate
157 => 'Pollable event',
// 158 - not defined
159 => 'Bad syscall',
);
];
/**
* @param array $command The command to run and its arguments listed as separate entries
* @param string|null $cwd The working directory or null to use the working dir of the current PHP process
* @param array|null $env The environment variables or null to use the same environment as the current PHP process
* @param mixed|null $input The input as stream resource, scalar or \Traversable, or null for no input
* @param mixed $input The input as stream resource, scalar or \Traversable, or null for no input
* @param int|float|null $timeout The timeout in seconds or null to disable
*
* @throws RuntimeException When proc_open is not installed
* @throws LogicException When proc_open is not installed
*/
public function __construct($command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
{
@@ -144,7 +144,7 @@ class Process implements \IteratorAggregate
}
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);
@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;
@@ -152,8 +152,8 @@ class Process implements \IteratorAggregate
// on Windows, if the cwd changed via chdir(), proc_open defaults to the dir where PHP was started
// on Gnu/Linux, PHP builds with --enable-maintainer-zts are also affected
// @see : https://bugs.php.net/bug.php?id=51800
// @see : https://bugs.php.net/bug.php?id=50524
// @see : https://bugs.php.net/51800
// @see : https://bugs.php.net/50524
if (null === $this->cwd && (\defined('ZEND_THREAD_SAFE') || '\\' === \DIRECTORY_SEPARATOR)) {
$this->cwd = getcwd();
}
@@ -177,25 +177,40 @@ class Process implements \IteratorAggregate
* In order to inject dynamic values into command-lines, we strongly recommend using placeholders.
* This will save escaping values, which is not portable nor secure anyway:
*
* $process = Process::fromShellCommandline('my_command "$MY_VAR"');
* $process = Process::fromShellCommandline('my_command "${:MY_VAR}"');
* $process->run(null, ['MY_VAR' => $theValue]);
*
* @param string $command The command line to pass to the shell of the OS
* @param string|null $cwd The working directory or null to use the working dir of the current PHP process
* @param array|null $env The environment variables or null to use the same environment as the current PHP process
* @param mixed|null $input The input as stream resource, scalar or \Traversable, or null for no input
* @param mixed $input The input as stream resource, scalar or \Traversable, or null for no input
* @param int|float|null $timeout The timeout in seconds or null to disable
*
* @throws RuntimeException When proc_open is not installed
* @return static
*
* @throws LogicException When proc_open is not installed
*/
public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
{
$process = new static(array(), $cwd, $env, $input, $timeout);
$process = new static([], $cwd, $env, $input, $timeout);
$process->commandline = $command;
return $process;
}
/**
* @return array
*/
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->stop(0);
@@ -218,17 +233,18 @@ class Process implements \IteratorAggregate
*
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
* @param array $env An array of additional env vars to set when running the process
*
* @return int The exit status code
*
* @throws RuntimeException When process can't be launched
* @throws RuntimeException When process stopped after receiving signal
* @throws LogicException In case a callback is provided and output has been disabled
* @throws RuntimeException When process can't be launched
* @throws RuntimeException When process is already running
* @throws ProcessTimedOutException When process timed out
* @throws ProcessSignaledException When process stopped after receiving signal
* @throws LogicException In case a callback is provided and output has been disabled
*
* @final
*/
public function run(callable $callback = null, array $env = array()): int
public function run(callable $callback = null, array $env = []): int
{
$this->start($callback, $env);
@@ -241,16 +257,13 @@ class Process implements \IteratorAggregate
* This is identical to run() except that an exception is thrown if the process
* exits with a non-zero exit code.
*
* @param callable|null $callback
* @param array $env An array of additional env vars to set when running the process
*
* @return self
* @return $this
*
* @throws ProcessFailedException if the process didn't terminate successfully
*
* @final
*/
public function mustRun(callable $callback = null, array $env = array())
public function mustRun(callable $callback = null, array $env = []): self
{
if (0 !== $this->run($callback, $env)) {
throw new ProcessFailedException($this);
@@ -273,16 +286,15 @@ class Process implements \IteratorAggregate
*
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
* @param array $env An array of additional env vars to set when running the process
*
* @throws RuntimeException When process can't be launched
* @throws RuntimeException When process is already running
* @throws LogicException In case a callback is provided and output has been disabled
*/
public function start(callable $callback = null, array $env = array())
public function start(callable $callback = null, array $env = [])
{
if ($this->isRunning()) {
throw new RuntimeException('Process is already running');
throw new RuntimeException('Process is already running.');
}
$this->resetProcessData();
@@ -291,28 +303,31 @@ class Process implements \IteratorAggregate
$this->hasCallback = null !== $callback;
$descriptors = $this->getDescriptors();
if ($this->env) {
$env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->env, $env, 'strcasecmp') : $this->env;
}
$env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->getDefaultEnv(), $env, 'strcasecmp') : $this->getDefaultEnv();
if (\is_array($commandline = $this->commandline)) {
$commandline = implode(' ', array_map(array($this, 'escapeArgument'), $commandline));
$commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline));
if ('\\' !== \DIRECTORY_SEPARATOR) {
// exec is mandatory to deal with sending a signal to the process
$commandline = 'exec '.$commandline;
}
} else {
$commandline = $this->replacePlaceholders($commandline, $env);
}
if ($this->env) {
$env += $this->env;
}
$env += $this->getDefaultEnv();
$options = array('suppress_errors' => true);
$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
$descriptors[3] = array('pipe', 'w');
$descriptors[3] = ['pipe', 'w'];
// See https://unix.stackexchange.com/questions/71205/background-process-pipe-input
$commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
@@ -323,18 +338,18 @@ class Process implements \IteratorAggregate
$ptsWorkaround = fopen(__FILE__, 'r');
}
$envPairs = array();
$envPairs = [];
foreach ($env as $k => $v) {
if (false !== $v) {
if (false !== $v && false === \in_array($k, ['argc', 'argv', 'ARGC', 'ARGV'], true)) {
$envPairs[] = $k.'='.$v;
}
}
if (!is_dir($this->cwd)) {
throw new RuntimeException('The provided cwd does not exist.');
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, $options);
if (!\is_resource($this->process)) {
throw new RuntimeException('Unable to launch a new process.');
@@ -360,9 +375,8 @@ class Process implements \IteratorAggregate
*
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
* @param array $env An array of additional env vars to set when running the process
*
* @return $this
* @return static
*
* @throws RuntimeException When process can't be launched
* @throws RuntimeException When process is already running
@@ -371,10 +385,10 @@ class Process implements \IteratorAggregate
*
* @final
*/
public function restart(callable $callback = null, array $env = array())
public function restart(callable $callback = null, array $env = []): self
{
if ($this->isRunning()) {
throw new RuntimeException('Process is already running');
throw new RuntimeException('Process is already running.');
}
$process = clone $this;
@@ -394,9 +408,9 @@ class Process implements \IteratorAggregate
*
* @return int The exitcode of the process
*
* @throws RuntimeException When process timed out
* @throws RuntimeException When process stopped after receiving signal
* @throws LogicException When process is not yet started
* @throws ProcessTimedOutException When process timed out
* @throws ProcessSignaledException When process stopped after receiving signal
* @throws LogicException When process is not yet started
*/
public function wait(callable $callback = null)
{
@@ -407,7 +421,7 @@ class Process implements \IteratorAggregate
if (null !== $callback) {
if (!$this->processPipes->haveReadSupport()) {
$this->stop(0);
throw new \LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::wait"');
throw new LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::wait".');
}
$this->callback = $this->buildCallback($callback);
}
@@ -419,6 +433,7 @@ class Process implements \IteratorAggregate
} while ($running);
while ($this->isRunning()) {
$this->checkTimeout();
usleep(1000);
}
@@ -436,8 +451,9 @@ class Process implements \IteratorAggregate
* from the output in real-time while writing the standard input to the process.
* It allows to have feedback from the independent process during execution.
*
* @throws RuntimeException When process timed out
* @throws LogicException When process is not yet started
* @throws RuntimeException When process timed out
* @throws LogicException When process is not yet started
* @throws ProcessTimedOutException In case the timeout was reached
*/
public function waitUntil(callable $callback): bool
{
@@ -446,7 +462,7 @@ class Process implements \IteratorAggregate
if (!$this->processPipes->haveReadSupport()) {
$this->stop(0);
throw new \LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::waitUntil".');
throw new LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::waitUntil".');
}
$callback = $this->buildCallback($callback);
@@ -487,7 +503,7 @@ class Process implements \IteratorAggregate
/**
* Sends a POSIX signal to the process.
*
* @param int $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
* @param int $signal A valid POSIX signal (see https://php.net/pcntl.constants)
*
* @return $this
*
@@ -606,6 +622,7 @@ class Process implements \IteratorAggregate
*
* @return \Generator
*/
#[\ReturnTypeWillChange]
public function getIterator($flags = 0)
{
$this->readPipesForOutput(__FUNCTION__, false);
@@ -752,10 +769,10 @@ class Process implements \IteratorAggregate
public function getExitCodeText()
{
if (null === $exitcode = $this->getExitCode()) {
return;
return null;
}
return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
return self::$exitCodes[$exitcode] ?? 'Unknown error';
}
/**
@@ -895,7 +912,7 @@ class Process implements \IteratorAggregate
* @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 The exit-code of the process
* @return int|null The exit-code of the process or null if it's not running
*/
public function stop($timeout = 10, $signal = null)
{
@@ -935,7 +952,7 @@ class Process implements \IteratorAggregate
{
$this->lastOutputTime = microtime(true);
fseek($this->stdout, 0, SEEK_END);
fseek($this->stdout, 0, \SEEK_END);
fwrite($this->stdout, $line);
fseek($this->stdout, $this->incrementalOutputOffset);
}
@@ -949,11 +966,19 @@ class Process implements \IteratorAggregate
{
$this->lastOutputTime = microtime(true);
fseek($this->stderr, 0, SEEK_END);
fseek($this->stderr, 0, \SEEK_END);
fwrite($this->stderr, $line);
fseek($this->stderr, $this->incrementalErrorOutputOffset);
}
/**
* Gets the last output time in seconds.
*/
public function getLastOutputTime(): ?float
{
return $this->lastOutputTime;
}
/**
* Gets the command line to be executed.
*
@@ -961,7 +986,7 @@ class Process implements \IteratorAggregate
*/
public function getCommandLine()
{
return \is_array($this->commandline) ? implode(' ', array_map(array($this, 'escapeArgument'), $this->commandline)) : $this->commandline;
return \is_array($this->commandline) ? implode(' ', array_map([$this, 'escapeArgument'], $this->commandline)) : $this->commandline;
}
/**
@@ -969,13 +994,13 @@ class Process implements \IteratorAggregate
*
* @param string|array $commandline The command to execute
*
* @return self The current Process instance
* @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);
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), \E_USER_DEPRECATED);
$this->commandline = $commandline;
@@ -1003,13 +1028,13 @@ class Process implements \IteratorAggregate
}
/**
* Sets the process timeout (max. runtime).
* 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
*
* @return self The current Process instance
* @return $this
*
* @throws InvalidArgumentException if the timeout is negative
*/
@@ -1027,7 +1052,7 @@ class Process implements \IteratorAggregate
*
* @param int|float|null $timeout The timeout in seconds
*
* @return self The current Process instance
* @return $this
*
* @throws LogicException if the output is disabled
* @throws InvalidArgumentException if the timeout is negative
@@ -1048,7 +1073,7 @@ class Process implements \IteratorAggregate
*
* @param bool $tty True to enabled and false to disable
*
* @return self The current Process instance
* @return $this
*
* @throws RuntimeException In case the TTY mode is not supported
*/
@@ -1082,7 +1107,7 @@ class Process implements \IteratorAggregate
*
* @param bool $bool
*
* @return self
* @return $this
*/
public function setPty($bool)
{
@@ -1122,7 +1147,7 @@ class Process implements \IteratorAggregate
*
* @param string $cwd The new working directory
*
* @return self The current Process instance
* @return $this
*/
public function setWorkingDirectory($cwd)
{
@@ -1144,25 +1169,12 @@ class Process implements \IteratorAggregate
/**
* Sets the environment variables.
*
* Each environment variable value should be a string.
* If it is an array, the variable is ignored.
* If it is false or null, it will be removed when
* env vars are otherwise inherited.
* @param array<string|\Stringable> $env The new environment variables
*
* That happens in PHP when 'argv' is registered into
* the $_ENV array for instance.
*
* @param array $env The new environment variables
*
* @return self The current Process instance
* @return $this
*/
public function setEnv(array $env)
{
// Process can not handle env values that are arrays
$env = array_filter($env, function ($value) {
return !\is_array($value);
});
$this->env = $env;
return $this;
@@ -1185,7 +1197,7 @@ class Process implements \IteratorAggregate
*
* @param string|int|float|bool|resource|\Traversable|null $input The content
*
* @return self The current Process instance
* @return $this
*
* @throws LogicException In case the process is running
*/
@@ -1205,10 +1217,14 @@ class Process implements \IteratorAggregate
*
* @param bool $inheritEnv
*
* @return self The current Process instance
* @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.');
}
@@ -1251,7 +1267,7 @@ class Process implements \IteratorAggregate
static $isTtySupported;
if (null === $isTtySupported) {
$isTtySupported = (bool) @proc_open('echo 1 >/dev/null', array(array('file', '/dev/tty', 'r'), array('file', '/dev/tty', 'w'), array('file', '/dev/tty', 'w')), $pipes);
$isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes);
}
return $isTtySupported;
@@ -1274,7 +1290,7 @@ class Process implements \IteratorAggregate
return $result = false;
}
return $result = (bool) @proc_open('echo 1 >/dev/null', array(array('pty'), array('pty'), array('pty')), $pipes);
return $result = (bool) @proc_open('echo 1 >/dev/null', [['pty'], ['pty'], ['pty']], $pipes);
}
/**
@@ -1307,25 +1323,21 @@ class Process implements \IteratorAggregate
protected function buildCallback(callable $callback = null)
{
if ($this->outputDisabled) {
return function ($type, $data) use ($callback) {
if (null !== $callback) {
return \call_user_func($callback, $type, $data);
}
return function ($type, $data) use ($callback): bool {
return null !== $callback && $callback($type, $data);
};
}
$out = self::OUT;
return function ($type, $data) use ($callback, $out) {
return function ($type, $data) use ($callback, $out): bool {
if ($out == $type) {
$this->addOutput($data);
} else {
$this->addErrorOutput($data);
}
if (null !== $callback) {
return \call_user_func($callback, $type, $data);
}
return null !== $callback && $callback($type, $data);
};
}
@@ -1370,9 +1382,9 @@ class Process implements \IteratorAggregate
}
ob_start();
phpinfo(INFO_GENERAL);
phpinfo(\INFO_GENERAL);
return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
return self::$sigchild = str_contains(ob_get_clean(), '--enable-sigchild');
}
/**
@@ -1472,10 +1484,10 @@ class Process implements \IteratorAggregate
$this->starttime = null;
$this->callback = null;
$this->exitcode = null;
$this->fallbackStatus = array();
$this->fallbackStatus = [];
$this->processInformation = null;
$this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+b');
$this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+b');
$this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+');
$this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+');
$this->process = null;
$this->latestSignal = null;
$this->status = self::STATUS_READY;
@@ -1486,11 +1498,9 @@ class Process implements \IteratorAggregate
/**
* Sends a POSIX signal to the process.
*
* @param int $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
* @param int $signal A valid POSIX signal (see https://php.net/pcntl.constants)
* @param bool $throwException Whether to throw exception in case signal failed
*
* @return bool True if the signal was sent successfully, false otherwise
*
* @throws LogicException In case the process is not running
* @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
* @throws RuntimeException In case of failure
@@ -1519,7 +1529,7 @@ class Process implements \IteratorAggregate
$ok = @proc_terminate($this->process, $signal);
} elseif (\function_exists('posix_kill')) {
$ok = @posix_kill($pid, $signal);
} elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), array(2 => array('pipe', 'w')), $pipes)) {
} elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) {
$ok = false === fgets($pipes[2]);
}
if (!$ok) {
@@ -1539,11 +1549,11 @@ class Process implements \IteratorAggregate
return true;
}
private function prepareWindowsCommandLine(string $cmd, array &$env)
private function prepareWindowsCommandLine(string $cmd, array &$env): string
{
$uid = uniqid('', true);
$varCount = 0;
$varCache = array();
$varCache = [];
$cmd = preg_replace_callback(
'/"(?:(
[^"%!^]*+
@@ -1559,14 +1569,14 @@ class Process implements \IteratorAggregate
if (isset($varCache[$m[0]])) {
return $varCache[$m[0]];
}
if (false !== strpos($value = $m[1], "\0")) {
if (str_contains($value = $m[1], "\0")) {
$value = str_replace("\0", '?', $value);
}
if (false === strpbrk($value, "\"%!\n")) {
return '"'.$value.'"';
}
$value = str_replace(array('!LF!', '"^!"', '"^%"', '"^^"', '""'), array("\n", '!', '%', '^', '"'), $value);
$value = str_replace(['!LF!', '"^!"', '"^%"', '"^^"', '""'], ["\n", '!', '%', '^', '"'], $value);
$value = '"'.preg_replace('/(\\\\*)"/', '$1$1\\"', $value).'"';
$var = $uid.++$varCount;
@@ -1593,7 +1603,7 @@ class Process implements \IteratorAggregate
private function requireProcessIsStarted(string $functionName)
{
if (!$this->isStarted()) {
throw new LogicException(sprintf('Process must be started before calling %s.', $functionName));
throw new LogicException(sprintf('Process must be started before calling "%s()".', $functionName));
}
}
@@ -1605,7 +1615,7 @@ class Process implements \IteratorAggregate
private function requireProcessIsTerminated(string $functionName)
{
if (!$this->isTerminated()) {
throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
throw new LogicException(sprintf('Process must be terminated before calling "%s()".', $functionName));
}
}
@@ -1620,7 +1630,7 @@ class Process implements \IteratorAggregate
if ('\\' !== \DIRECTORY_SEPARATOR) {
return "'".str_replace("'", "'\\''", $argument)."'";
}
if (false !== strpos($argument, "\0")) {
if (str_contains($argument, "\0")) {
$argument = str_replace("\0", '?', $argument);
}
if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) {
@@ -1628,25 +1638,25 @@ class Process implements \IteratorAggregate
}
$argument = preg_replace('/(\\\\+)$/', '$1$1', $argument);
return '"'.str_replace(array('"', '^', '%', '!', "\n"), array('""', '"^^"', '"^%"', '"^!"', '!LF!'), $argument).'"';
return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"';
}
private function getDefaultEnv()
private function replacePlaceholders(string $commandline, array $env)
{
$env = array();
foreach ($_SERVER as $k => $v) {
if (\is_string($v) && false !== $v = getenv($k)) {
$env[$k] = $v;
return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) {
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": ', $matches[1]).$commandline);
}
}
foreach ($_ENV as $k => $v) {
if (\is_string($v)) {
$env[$k] = $v;
}
}
return $this->escapeArgument($env[$matches[1]]);
}, $commandline);
}
return $env;
private function getDefaultEnv(): array
{
$env = getenv();
$env = ('\\' === \DIRECTORY_SEPARATOR ? array_intersect_ukey($env, $_SERVER, 'strcasecmp') : array_intersect_key($env, $_SERVER)) ?: $env;
return $_ENV + ('\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($env, $_ENV, 'strcasecmp') : $env);
}
}

View File

@@ -48,7 +48,7 @@ class ProcessUtils
if (\is_string($input)) {
return $input;
}
if (is_scalar($input)) {
if (\is_scalar($input)) {
return (string) $input;
}
if ($input instanceof Process) {
@@ -61,7 +61,7 @@ class ProcessUtils
return new \IteratorIterator($input);
}
throw new InvalidArgumentException(sprintf('%s only accepts strings, Traversable objects or stream resources.', $caller));
throw new InvalidArgumentException(sprintf('"%s" only accepts strings, Traversable objects or stream resources.', $caller));
}
return $input;

View File

@@ -6,8 +6,8 @@ The Process component executes commands in sub-processes.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/process.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [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)
* [Documentation](https://symfony.com/doc/current/components/process.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [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)

View File

@@ -1,163 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\ExecutableFinder;
/**
* @author Chris Smith <chris@cs278.org>
*/
class ExecutableFinderTest extends TestCase
{
private $path;
protected function tearDown()
{
if ($this->path) {
// Restore path if it was changed.
putenv('PATH='.$this->path);
}
}
private function setPath($path)
{
$this->path = getenv('PATH');
putenv('PATH='.$path);
}
public function testFind()
{
if (ini_get('open_basedir')) {
$this->markTestSkipped('Cannot test when open_basedir is set');
}
$this->setPath(\dirname(PHP_BINARY));
$finder = new ExecutableFinder();
$result = $finder->find($this->getPhpBinaryName());
$this->assertSamePath(PHP_BINARY, $result);
}
public function testFindWithDefault()
{
if (ini_get('open_basedir')) {
$this->markTestSkipped('Cannot test when open_basedir is set');
}
$expected = 'defaultValue';
$this->setPath('');
$finder = new ExecutableFinder();
$result = $finder->find('foo', $expected);
$this->assertEquals($expected, $result);
}
public function testFindWithExtraDirs()
{
if (ini_get('open_basedir')) {
$this->markTestSkipped('Cannot test when open_basedir is set');
}
$this->setPath('');
$extraDirs = array(\dirname(PHP_BINARY));
$finder = new ExecutableFinder();
$result = $finder->find($this->getPhpBinaryName(), null, $extraDirs);
$this->assertSamePath(PHP_BINARY, $result);
}
public function testFindWithOpenBaseDir()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Cannot run test on windows');
}
if (ini_get('open_basedir')) {
$this->markTestSkipped('Cannot test when open_basedir is set');
}
$this->iniSet('open_basedir', \dirname(PHP_BINARY).PATH_SEPARATOR.'/');
$finder = new ExecutableFinder();
$result = $finder->find($this->getPhpBinaryName());
$this->assertSamePath(PHP_BINARY, $result);
}
public function testFindProcessInOpenBasedir()
{
if (ini_get('open_basedir')) {
$this->markTestSkipped('Cannot test when open_basedir is set');
}
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Cannot run test on windows');
}
$this->setPath('');
$this->iniSet('open_basedir', PHP_BINARY.PATH_SEPARATOR.'/');
$finder = new ExecutableFinder();
$result = $finder->find($this->getPhpBinaryName(), false);
$this->assertSamePath(PHP_BINARY, $result);
}
/**
* @requires PHP 5.4
*/
public function testFindBatchExecutableOnWindows()
{
if (ini_get('open_basedir')) {
$this->markTestSkipped('Cannot test when open_basedir is set');
}
if ('\\' !== \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Can be only tested on windows');
}
$target = tempnam(sys_get_temp_dir(), 'example-windows-executable');
touch($target);
touch($target.'.BAT');
$this->assertFalse(is_executable($target));
$this->setPath(sys_get_temp_dir());
$finder = new ExecutableFinder();
$result = $finder->find(basename($target), false);
unlink($target);
unlink($target.'.BAT');
$this->assertSamePath($target.'.BAT', $result);
}
private function assertSamePath($expected, $tested)
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->assertEquals(strtolower($expected), strtolower($tested));
} else {
$this->assertEquals($expected, $tested);
}
}
private function getPhpBinaryName()
{
return basename(PHP_BINARY, '\\' === \DIRECTORY_SEPARATOR ? '.exe' : '');
}
}

View File

@@ -1,26 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
$outputs = array(
'First iteration output',
'Second iteration output',
'One more iteration output',
'This took more time',
'This one was sooooo slow',
);
$iterationTime = 10000;
foreach ($outputs as $output) {
usleep($iterationTime);
$iterationTime *= 10;
echo $output."\n";
}

View File

@@ -1,47 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Runs a PHP script that can be stopped only with a SIGKILL (9) signal for 3 seconds.
*
* @args duration Run this script with a custom duration
*
* @example `php NonStopableProcess.php 42` will run the script for 42 seconds
*/
function handleSignal($signal)
{
switch ($signal) {
case SIGTERM:
$name = 'SIGTERM';
break;
case SIGINT:
$name = 'SIGINT';
break;
default:
$name = $signal.' (unknown)';
break;
}
echo "signal $name\n";
}
pcntl_signal(SIGTERM, 'handleSignal');
pcntl_signal(SIGINT, 'handleSignal');
echo 'received ';
$duration = isset($argv[1]) ? (int) $argv[1] : 3;
$start = microtime(true);
while ($duration > (microtime(true) - $start)) {
usleep(10000);
pcntl_signal_dispatch();
}

View File

@@ -1,49 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\PhpExecutableFinder;
/**
* @author Robert Schönthal <seroscho@googlemail.com>
*/
class PhpExecutableFinderTest extends TestCase
{
/**
* tests find() with the constant PHP_BINARY.
*/
public function testFind()
{
$f = new PhpExecutableFinder();
$current = PHP_BINARY;
$args = 'phpdbg' === \PHP_SAPI ? ' -qrr' : '';
$this->assertEquals($current.$args, $f->find(), '::find() returns the executable PHP');
$this->assertEquals($current, $f->find(false), '::find() returns the executable PHP');
}
/**
* tests find() with the env var PHP_PATH.
*/
public function testFindArguments()
{
$f = new PhpExecutableFinder();
if ('phpdbg' === \PHP_SAPI) {
$this->assertEquals($f->findArguments(), array('-qrr'), '::findArguments() returns phpdbg arguments');
} else {
$this->assertEquals($f->findArguments(), array(), '::findArguments() returns no arguments');
}
}
}

View File

@@ -1,48 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\PhpProcess;
class PhpProcessTest extends TestCase
{
public function testNonBlockingWorks()
{
$expected = 'hello world!';
$process = new PhpProcess(<<<PHP
<?php echo '$expected';
PHP
);
$process->start();
$process->wait();
$this->assertEquals($expected, $process->getOutput());
}
public function testCommandLine()
{
$process = new PhpProcess(<<<'PHP'
<?php echo phpversion().PHP_SAPI;
PHP
);
$commandLine = $process->getCommandLine();
$process->start();
$this->assertContains($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP after start');
$process->wait();
$this->assertContains($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP after wait');
$this->assertSame(PHP_VERSION.\PHP_SAPI, $process->getOutput());
}
}

View File

@@ -1,72 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
define('ERR_SELECT_FAILED', 1);
define('ERR_TIMEOUT', 2);
define('ERR_READ_FAILED', 3);
define('ERR_WRITE_FAILED', 4);
$read = array(STDIN);
$write = array(STDOUT, STDERR);
stream_set_blocking(STDIN, 0);
stream_set_blocking(STDOUT, 0);
stream_set_blocking(STDERR, 0);
$out = $err = '';
while ($read || $write) {
$r = $read;
$w = $write;
$e = null;
$n = stream_select($r, $w, $e, 5);
if (false === $n) {
die(ERR_SELECT_FAILED);
} elseif ($n < 1) {
die(ERR_TIMEOUT);
}
if (in_array(STDOUT, $w) && strlen($out) > 0) {
$written = fwrite(STDOUT, (string) $out, 32768);
if (false === $written) {
die(ERR_WRITE_FAILED);
}
$out = (string) substr($out, $written);
}
if (null === $read && '' === $out) {
$write = array_diff($write, array(STDOUT));
}
if (in_array(STDERR, $w) && strlen($err) > 0) {
$written = fwrite(STDERR, (string) $err, 32768);
if (false === $written) {
die(ERR_WRITE_FAILED);
}
$err = (string) substr($err, $written);
}
if (null === $read && '' === $err) {
$write = array_diff($write, array(STDERR));
}
if ($r) {
$str = fread(STDIN, 32768);
if (false !== $str) {
$out .= $str;
$err .= $str;
}
if (false === $str || feof(STDIN)) {
$read = null;
if (!feof(STDIN)) {
die(ERR_READ_FAILED);
}
}
}
}

View File

@@ -1,137 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\Exception\ProcessFailedException;
/**
* @author Sebastian Marek <proofek@gmail.com>
*/
class ProcessFailedExceptionTest extends TestCase
{
/**
* tests ProcessFailedException throws exception if the process was successful.
*/
public function testProcessFailedExceptionThrowsException()
{
$process = $this->getMockBuilder('Symfony\Component\Process\Process')->setMethods(array('isSuccessful'))->setConstructorArgs(array(array('php')))->getMock();
$process->expects($this->once())
->method('isSuccessful')
->will($this->returnValue(true));
if (method_exists($this, 'expectException')) {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Expected a failed process, but the given process was successful.');
} else {
$this->setExpectedException(\InvalidArgumentException::class, 'Expected a failed process, but the given process was successful.');
}
new ProcessFailedException($process);
}
/**
* tests ProcessFailedException uses information from process output
* to generate exception message.
*/
public function testProcessFailedExceptionPopulatesInformationFromProcessOutput()
{
$cmd = 'php';
$exitCode = 1;
$exitText = 'General error';
$output = 'Command output';
$errorOutput = 'FATAL: Unexpected error';
$workingDirectory = getcwd();
$process = $this->getMockBuilder('Symfony\Component\Process\Process')->setMethods(array('isSuccessful', 'getOutput', 'getErrorOutput', 'getExitCode', 'getExitCodeText', 'isOutputDisabled', 'getWorkingDirectory'))->setConstructorArgs(array(array($cmd)))->getMock();
$process->expects($this->once())
->method('isSuccessful')
->will($this->returnValue(false));
$process->expects($this->once())
->method('getOutput')
->will($this->returnValue($output));
$process->expects($this->once())
->method('getErrorOutput')
->will($this->returnValue($errorOutput));
$process->expects($this->once())
->method('getExitCode')
->will($this->returnValue($exitCode));
$process->expects($this->once())
->method('getExitCodeText')
->will($this->returnValue($exitText));
$process->expects($this->once())
->method('isOutputDisabled')
->will($this->returnValue(false));
$process->expects($this->once())
->method('getWorkingDirectory')
->will($this->returnValue($workingDirectory));
$exception = new ProcessFailedException($process);
$this->assertEquals(
"The command \"$cmd\" failed.\n\nExit Code: $exitCode($exitText)\n\nWorking directory: {$workingDirectory}\n\nOutput:\n================\n{$output}\n\nError Output:\n================\n{$errorOutput}",
str_replace("'php'", 'php', $exception->getMessage())
);
}
/**
* Tests that ProcessFailedException does not extract information from
* process output if it was previously disabled.
*/
public function testDisabledOutputInFailedExceptionDoesNotPopulateOutput()
{
$cmd = 'php';
$exitCode = 1;
$exitText = 'General error';
$workingDirectory = getcwd();
$process = $this->getMockBuilder('Symfony\Component\Process\Process')->setMethods(array('isSuccessful', 'isOutputDisabled', 'getExitCode', 'getExitCodeText', 'getOutput', 'getErrorOutput', 'getWorkingDirectory'))->setConstructorArgs(array(array($cmd)))->getMock();
$process->expects($this->once())
->method('isSuccessful')
->will($this->returnValue(false));
$process->expects($this->never())
->method('getOutput');
$process->expects($this->never())
->method('getErrorOutput');
$process->expects($this->once())
->method('getExitCode')
->will($this->returnValue($exitCode));
$process->expects($this->once())
->method('getExitCodeText')
->will($this->returnValue($exitText));
$process->expects($this->once())
->method('isOutputDisabled')
->will($this->returnValue(true));
$process->expects($this->once())
->method('getWorkingDirectory')
->will($this->returnValue($workingDirectory));
$exception = new ProcessFailedException($process);
$this->assertEquals(
"The command \"$cmd\" failed.\n\nExit Code: $exitCode($exitText)\n\nWorking directory: {$workingDirectory}",
str_replace("'php'", 'php', $exception->getMessage())
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
pcntl_signal(SIGUSR1, function () { echo 'SIGUSR1'; exit; });
echo 'Caught ';
$n = 0;
while ($n++ < 400) {
usleep(10000);
pcntl_signal_dispatch();
}

View File

@@ -1,7 +1,7 @@
{
"name": "symfony/process",
"type": "library",
"description": "Symfony Process Component",
"description": "Executes commands in sub-processes",
"keywords": [],
"homepage": "https://symfony.com",
"license": "MIT",
@@ -16,7 +16,8 @@
}
],
"require": {
"php": "^7.1.3"
"php": ">=7.1.3",
"symfony/polyfill-php80": "^1.16"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Process\\": "" },
@@ -24,10 +25,5 @@
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "4.2-dev"
}
}
"minimum-stability": "dev"
}

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony Process Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>