diff --git a/src/Runner/CliTester.php b/src/Runner/CliTester.php index b51de53e..d923d101 100644 --- a/src/Runner/CliTester.php +++ b/src/Runner/CliTester.php @@ -267,7 +267,7 @@ private function prepareCodeCoverage(Runner $runner): string { $engines = $this->interpreter->getCodeCoverageEngines(); if (count($engines) < 1) { - throw new \Exception("Code coverage functionality requires Xdebug or PCOV extension or PHPDBG SAPI (used {$this->interpreter->getCommandLine()})"); + throw new \Exception("Code coverage functionality requires Xdebug or PCOV extension or PHPDBG SAPI (used {$this->interpreter->getCommandLineStr()})"); } file_put_contents($this->options['--coverage'], ''); diff --git a/src/Runner/Job.php b/src/Runner/Job.php index 135e5695..e7379a4d 100644 --- a/src/Runner/Job.php +++ b/src/Runner/Job.php @@ -95,7 +95,7 @@ public function run(bool $async = false): void $this->duration = -microtime(as_float: true); $this->proc = proc_open( $this->interpreter - ->withArguments(['-d register_argc_argv=on', $this->test->getFile(), ...$args]) + ->withArguments(['-d', 'register_argc_argv=on', $this->test->getFile(), ...$args]) ->getCommandLine(), [ ['pipe', 'r'], diff --git a/src/Runner/Output/ConsolePrinter.php b/src/Runner/Output/ConsolePrinter.php index ca47254a..311bf7ae 100644 --- a/src/Runner/Output/ConsolePrinter.php +++ b/src/Runner/Output/ConsolePrinter.php @@ -67,7 +67,7 @@ public function begin(): void $this->mode = self::ModeLines; } fwrite($this->file, $this->runner->getInterpreter()->getShortInfo() - . ' | ' . $this->runner->getInterpreter()->getCommandLine() + . ' | ' . $this->runner->getInterpreter()->getCommandLineStr() . " | {$this->runner->threadCount} thread" . ($this->runner->threadCount > 1 ? 's' : '') . "\n\n"); } diff --git a/src/Runner/Output/Logger.php b/src/Runner/Output/Logger.php index a41fbdbc..cab95e8a 100644 --- a/src/Runner/Output/Logger.php +++ b/src/Runner/Output/Logger.php @@ -38,7 +38,7 @@ public function begin(): void $this->count = 0; $this->results = [Test::Passed => 0, Test::Skipped => 0, Test::Failed => 0]; fwrite($this->file, 'PHP ' . $this->runner->getInterpreter()->getVersion() - . ' | ' . $this->runner->getInterpreter()->getCommandLine() + . ' | ' . $this->runner->getInterpreter()->getCommandLineStr() . " | {$this->runner->threadCount} threads\n\n"); } diff --git a/src/Runner/PhpInterpreter.php b/src/Runner/PhpInterpreter.php index 908b1614..b31b9b30 100644 --- a/src/Runner/PhpInterpreter.php +++ b/src/Runner/PhpInterpreter.php @@ -16,7 +16,7 @@ */ class PhpInterpreter { - private string $commandLine; + private array $commandLine; private bool $cgi; private \stdClass $info; private string $error; @@ -25,14 +25,10 @@ class PhpInterpreter /** @param string[] $args */ public function __construct(string $path, array $args = []) { - $this->commandLine = Helpers::escapeArg($path); $proc = @proc_open( // @ is escalated to exception - $this->commandLine . ' --version', + [$path, '--version'], [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipes, - null, - null, - ['bypass_shell' => true], ); if ($proc === false) { throw new \Exception("Cannot run PHP interpreter $path. Use -p option."); @@ -42,20 +38,22 @@ public function __construct(string $path, array $args = []) $output = stream_get_contents($pipes[1]); proc_close($proc); - $args = ' ' . implode(' ', array_map([Helpers::class, 'escapeArg'], $args)); + $this->commandLine = array_merge([$path], $args); if (str_contains($output, 'phpdbg')) { - $args = ' -qrrb -S cli' . $args; + array_push($this->commandLine, '-qrrb', '-S', 'cli'); } - $this->commandLine .= rtrim($args); - $proc = proc_open( - $this->commandLine . ' -d register_argc_argv=on ' . Helpers::escapeArg(__DIR__ . '/info.php') . ' serialized', + array_merge( + $this->commandLine, + [ + '-d', 'register_argc_argv=on', + __DIR__ . '/info.php', + 'serialized', + ], + ), [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipes, - null, - null, - ['bypass_shell' => true], ) ?: throw new \Exception("Unable to run $path."); $output = stream_get_contents($pipes[1]); @@ -88,7 +86,7 @@ public function __construct(string $path, array $args = []) public function withArguments(array $args): static { $me = clone $this; - $me->commandLine .= ' ' . implode(' ', array_map([Helpers::class, 'escapeArg'], $args)); + array_push($me->commandLine, ...$args); return $me; } @@ -98,16 +96,22 @@ public function withArguments(array $args): static */ public function withPhpIniOption(string $name, ?string $value = null): static { - return $this->withArguments(['-d ' . $name . ($value === null ? '' : "=$value")]); + return $this->withArguments(['-d', $name . ($value === null ? '' : "=$value")]); } - public function getCommandLine(): string + public function getCommandLine(): array { return $this->commandLine; } + public function getCommandLineStr(): string + { + return implode(' ', array_map([Helpers::class, 'escapeArg'], $this->commandLine)); + } + + public function getVersion(): string { return $this->info->version; diff --git a/tests/Runner/PhpInterpreter.phpt b/tests/Runner/PhpInterpreter.phpt index 7715a776..19f9b1f9 100644 --- a/tests/Runner/PhpInterpreter.phpt +++ b/tests/Runner/PhpInterpreter.phpt @@ -35,6 +35,6 @@ Assert::count($count, $engines); // createInterpreter() uses same php.ini as parent if (!$interpreter->isCgi()) { - $output = shell_exec($interpreter->withArguments(['-r echo php_ini_loaded_file();'])->getCommandLine()); + $output = shell_exec($interpreter->withArguments(['-r', 'echo php_ini_loaded_file();'])->getCommandLineStr()); Assert::same(php_ini_loaded_file(), $output); }