diff --git a/CHANGELOG.md b/CHANGELOG.md index b2bd32d2..4e491476 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [3.17.2] - 2021-01-31 + +### Fixed + +- Fixed issue with standard process during output mode +- Fixed commands not working properly due to missing environment variables like PATH. + ## [3.17.1] - 2021-01-31 ### Fixed diff --git a/src/Process/BackgroundProcess.php b/src/Process/BackgroundProcess.php index bbbce9f9..a3f48105 100644 --- a/src/Process/BackgroundProcess.php +++ b/src/Process/BackgroundProcess.php @@ -59,6 +59,26 @@ class BackgroundProcess extends BaseProcess */ private $timeout = null; + /** + * @var array + */ + protected $env = []; + + /** + * @var string + */ + protected $directory; + + /** + * @var string + */ + protected $command; + + /** + * @var boolean + */ + protected $outputEnabled = false; + /** * @param string|array $stringOrArray * @param array $options The following options are supported @@ -80,6 +100,7 @@ public function __construct($stringOrArray, array $options = []) ]; $this->setDirectory($options['directory']); + $this->setupEnvironment(); $this->setEnv((array) $options['env']); $this->setCommand($stringOrArray, $options['escape']); @@ -322,4 +343,104 @@ private function status(string $key) return $this->status[$key] ?? null; } + + /** + * @param string $directory + * @return void + */ + protected function setDirectory(string $directory): void + { + if (! is_dir($directory)) { + throw new RuntimeException('Invalid directory'); + } + $this->directory = $directory; + } + + /** + * Any value as false will delete a current var + * + * @param array $env + * @return void + */ + protected function setEnv(array $env): void + { + foreach ($env as $key => $value) { + if ($value === false) { + unset($this->env[$key]); + continue; + } + $this->env[$key] = $value; + } + } + + /** + * Copy the ENV vars so commands can work as expected + * + * @return void + */ + protected function setupEnvironment(): void + { + foreach ($_SERVER as $key => $value) { + $found = getenv($key); + if ($found !== false) { + $this->env[$key] = $value; + } + } + + foreach ($_ENV as $key => $value) { + $this->env[$key] = $value; + } + } + + /** + * Escapes a command for use + * + * @param string|array $stringOrArray + * @return string|array + */ + private function escapeCommand($stringOrArray) + { + if (is_string($stringOrArray)) { + return escapeshellcmd($stringOrArray); + } + + return array_map('escapeshellarg', $stringOrArray); + } + + /** + * @param string|array $stringOrArray + * @param boolean $escape + * @return void + */ + protected function setCommand($stringOrArray, bool $escape = true): void + { + if ($escape) { + $stringOrArray = $this->escapeCommand($stringOrArray); + } + + $this->command = is_array($stringOrArray) ? implode(' ', $stringOrArray) : $stringOrArray; + } + + /** + * @see https://www.php.net/manual/en/function.proc-open.php + * + * @param boolean $output + * @return array + */ + protected function descriptorspec(): array + { + if ($this->outputEnabled && $this->isTTY()) { + return [ + ['file', '/dev/tty', 'r'], + ['file', '/dev/tty', 'w'], + ['file', '/dev/tty', 'w'], + ]; + } + + return [ + ['pipe','r'], + ['pipe','w'], + ['pipe','w'] + ]; + } } diff --git a/src/Process/BaseProcess.php b/src/Process/BaseProcess.php index 41c2f70a..a6cc7e0c 100644 --- a/src/Process/BaseProcess.php +++ b/src/Process/BaseProcess.php @@ -14,52 +14,9 @@ declare(strict_types = 1); namespace Origin\Process; -use RuntimeException; - abstract class BaseProcess { - /** - * @var array - */ - protected $env = []; - - /** - * @var string - */ - protected $directory; - - /** - * @var string - */ - protected $command; - - /** - * @var boolean - */ - protected $outputEnabled = false; - - /** - * @param string $directory - * @return void - */ - protected function setDirectory(string $directory): void - { - if (! is_dir($directory)) { - throw new RuntimeException('Invalid directory'); - } - $this->directory = $directory; - } - - /** - * @param array $env - * @return void - */ - protected function setEnv(array $env): void - { - foreach ($env as $key => $value) { - $this->env[$key] = $value; - } - } + /** * @return boolean */ @@ -67,59 +24,7 @@ public function isTTY(): bool { return function_exists('posix_isatty') && posix_isatty(STDOUT); } - - /** - * Escapes a command for use - * - * @param string|array $stringOrArray - * @return string|array - */ - private function escapeCommand($stringOrArray) - { - if (is_string($stringOrArray)) { - return escapeshellcmd($stringOrArray); - } - - return array_map('escapeshellarg', $stringOrArray); - } - - /** - * @param string|array $stringOrArray - * @param boolean $escape - * @return void - */ - protected function setCommand($stringOrArray, bool $escape = true): void - { - if ($escape) { - $stringOrArray = $this->escapeCommand($stringOrArray); - } - - $this->command = is_array($stringOrArray) ? implode(' ', $stringOrArray) : $stringOrArray; - } - - /** - * @see https://www.php.net/manual/en/function.proc-open.php - * - * @param boolean $output - * @return array - */ - protected function descriptorspec(): array - { - if ($this->outputEnabled && $this->isTTY()) { - return [ - ['file', '/dev/tty', 'r'], - ['file', '/dev/tty', 'w'], - ['file', '/dev/tty', 'w'], - ]; - } - - return [ - ['pipe','r'], - ['pipe','w'], - ['pipe','w'] - ]; - } - + /** * Checks if the process ended successfully * diff --git a/src/Process/Process.php b/src/Process/Process.php index f4d58a3c..d5bce11c 100644 --- a/src/Process/Process.php +++ b/src/Process/Process.php @@ -14,6 +14,12 @@ declare(strict_types = 1); namespace Origin\Process; +/** + * Process + * + * @internal refactored to go through background process due to issue with getting output, cant use stream + * so you have to go through a loop, essentially recreating code in the background process + */ class Process extends BaseProcess { /** @@ -32,9 +38,12 @@ class Process extends BaseProcess protected $exitCode = null; /** - * @var integer|null + * Raw command + * + * @var string|array */ - protected $timeout = null; + protected $command; + protected $options; /** * @param string|array $stringOrArray @@ -43,59 +52,36 @@ class Process extends BaseProcess * - env: an array of key values for environment variables * - output: (bool) default if TTY is supported output will be sent to screen * - escape: default: true escapes the command + * - timeout: set the timeout value in seconds * @return void */ public function __construct($stringOrArray, array $options = []) { - $options += [ - 'directory' => getcwd(), - 'env' => [], - 'output' => false, - 'escape' => true - ]; - - $this->setDirectory($options['directory']); - $this->setEnv((array) $options['env']); - $this->setCommand($stringOrArray, $options['escape']); - - $this->outputEnabled = $options['output']; + $this->command = $stringOrArray; + $this->options = $options; } /** * Executes a command * - * @param string|array $stringOrArray - * @param array $options The following options are supported - * - directory: the directory to execute the command in, default is getcwd - * - env: an array of key values for environment variables - * - output: (bool) default if TTY is supported output will be sent to screen - * - escape: default: true escapes the command * @return boolean */ public function execute(): bool { $this->stdout = $this->stderr = ''; - $this->exitCode = null; - - $process = proc_open( - $this->command, $this->descriptorspec($this->outputEnabled), $pipes, $this->directory, $this->env - ); - if (! $process) { - return false; - } - $this->stdout = stream_get_contents($pipes[1]); - $this->stderr = stream_get_contents($pipes[2]); - - fclose($pipes[0]); - fclose($pipes[1]); - fclose($pipes[2]); - - $this->exitCode = proc_close($process); + $process = new BackgroundProcess($this->command, $this->options); + $process->start(); + $process->wait(); + + $this->stdout = $process->output(); + $this->stderr = $process->error(); + + $this->exitCode = $process->exitCode(); return $this->exitCode === 0; } - + /** * Gets the exit code or null if it was not run *