From 724e6c68cc3cb7c618860a824e5cbda91815e374 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Wed, 11 Dec 2024 14:08:35 +0100 Subject: [PATCH 1/4] chore: PHP CS Fixer fixes --- ExecutableFinder.php | 2 +- Process.php | 2 +- Tests/ExecutableFinderTest.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ExecutableFinder.php b/ExecutableFinder.php index 5cc65251..6aa2d4d7 100644 --- a/ExecutableFinder.php +++ b/ExecutableFinder.php @@ -72,7 +72,7 @@ public function find(string $name, ?string $default = null, array $extraDirs = [ $pathExt = getenv('PATHEXT'); $suffixes = array_merge($suffixes, $pathExt ? explode(\PATH_SEPARATOR, $pathExt) : ['.exe', '.bat', '.cmd', '.com']); } - $suffixes = '' !== pathinfo($name, PATHINFO_EXTENSION) ? array_merge([''], $suffixes) : array_merge($suffixes, ['']); + $suffixes = '' !== pathinfo($name, \PATHINFO_EXTENSION) ? array_merge([''], $suffixes) : array_merge($suffixes, ['']); foreach ($suffixes as $suffix) { foreach ($dirs as $dir) { if ('' === $dir) { diff --git a/Process.php b/Process.php index 03ce70e5..f9d4e814 100644 --- a/Process.php +++ b/Process.php @@ -1596,7 +1596,7 @@ function ($m) use (&$env, $uid) { if (!$comSpec && $comSpec = (new ExecutableFinder())->find('cmd.exe')) { // Escape according to CommandLineToArgvW rules - $comSpec = '"'.preg_replace('{(\\\\*+)"}', '$1$1\"', $comSpec) .'"'; + $comSpec = '"'.preg_replace('{(\\\\*+)"}', '$1$1\"', $comSpec).'"'; } $cmd = ($comSpec ?? 'cmd').' /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')'; diff --git a/Tests/ExecutableFinderTest.php b/Tests/ExecutableFinderTest.php index 87288360..cdc60a92 100644 --- a/Tests/ExecutableFinderTest.php +++ b/Tests/ExecutableFinderTest.php @@ -174,7 +174,7 @@ public function testFindBatchExecutableOnWindows() */ public function testEmptyDirInPath() { - putenv(sprintf('PATH=%s%s', \dirname(\PHP_BINARY), \PATH_SEPARATOR)); + putenv(\sprintf('PATH=%s%s', \dirname(\PHP_BINARY), \PATH_SEPARATOR)); try { touch('executable'); @@ -183,7 +183,7 @@ public function testEmptyDirInPath() $finder = new ExecutableFinder(); $result = $finder->find('executable'); - $this->assertSame(sprintf('.%sexecutable', \DIRECTORY_SEPARATOR), $result); + $this->assertSame(\sprintf('.%sexecutable', \DIRECTORY_SEPARATOR), $result); } finally { unlink('executable'); } From 2b42be26326a4fae4313751b40a2ecb60e619311 Mon Sep 17 00:00:00 2001 From: Staormin Date: Thu, 13 Feb 2025 14:38:00 +0100 Subject: [PATCH 2/4] [Messenger][Process] add `fromShellCommandline` to `RunProcessMessage` Allows using the Process::fromShellCommandline when using a RunProcessMessage --- CHANGELOG.md | 6 +++++ Messenger/RunProcessMessage.php | 17 ++++++++++++- Messenger/RunProcessMessageHandler.php | 5 +++- .../RunProcessMessageHandlerTest.php | 24 +++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e33cd0b..d7308566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= + +7.3 +--- + + * Add `RunProcessMessage::fromShellCommandline()` to instantiate a Process via the fromShellCommandline method + 7.1 --- diff --git a/Messenger/RunProcessMessage.php b/Messenger/RunProcessMessage.php index b2c33fe3..d14ac236 100644 --- a/Messenger/RunProcessMessage.php +++ b/Messenger/RunProcessMessage.php @@ -16,6 +16,8 @@ */ class RunProcessMessage implements \Stringable { + public ?string $commandLine = null; + public function __construct( public readonly array $command, public readonly ?string $cwd = null, @@ -27,6 +29,19 @@ public function __construct( public function __toString(): string { - return implode(' ', $this->command); + return $this->commandLine ?? implode(' ', $this->command); + } + + /** + * Create a process message instance that will instantiate a Process using the fromShellCommandline method. + * + * @see Process::fromShellCommandline + */ + public static function fromShellCommandline(string $command, ?string $cwd = null, ?array $env = null, mixed $input = null, ?float $timeout = 60): self + { + $message = new self([], $cwd, $env, $input, $timeout); + $message->commandLine = $command; + + return $message; } } diff --git a/Messenger/RunProcessMessageHandler.php b/Messenger/RunProcessMessageHandler.php index 41c1934c..69bfa6a1 100644 --- a/Messenger/RunProcessMessageHandler.php +++ b/Messenger/RunProcessMessageHandler.php @@ -22,7 +22,10 @@ final class RunProcessMessageHandler { public function __invoke(RunProcessMessage $message): RunProcessContext { - $process = new Process($message->command, $message->cwd, $message->env, $message->input, $message->timeout); + $process = match ($message->commandLine) { + null => new Process($message->command, $message->cwd, $message->env, $message->input, $message->timeout), + default => Process::fromShellCommandline($message->commandLine, $message->cwd, $message->env, $message->input, $message->timeout), + }; try { return new RunProcessContext($message, $process->mustRun()); diff --git a/Tests/Messenger/RunProcessMessageHandlerTest.php b/Tests/Messenger/RunProcessMessageHandlerTest.php index e095fa09..b5b9ab14 100644 --- a/Tests/Messenger/RunProcessMessageHandlerTest.php +++ b/Tests/Messenger/RunProcessMessageHandlerTest.php @@ -44,4 +44,28 @@ public function testRunFailedProcess() $this->fail('Exception not thrown'); } + + public function testRunSuccessfulProcessFromShellCommandline() + { + $context = (new RunProcessMessageHandler())(RunProcessMessage::fromShellCommandline('ls | grep Test', cwd: __DIR__)); + + $this->assertSame('ls | grep Test', $context->message->commandLine); + $this->assertSame(0, $context->exitCode); + $this->assertStringContainsString(basename(__FILE__), $context->output); + } + + public function testRunFailedProcessFromShellCommandline() + { + try { + (new RunProcessMessageHandler())(RunProcessMessage::fromShellCommandline('invalid')); + $this->fail('Exception not thrown'); + } catch (RunProcessFailedException $e) { + $this->assertSame('invalid', $e->context->message->commandLine); + $this->assertContains( + $e->context->exitCode, + [null, '\\' === \DIRECTORY_SEPARATOR ? 1 : 127], + 'Exit code should be 1 on Windows, 127 on other systems, or null', + ); + } + } } From 0596c465ef3aa0452f954c9f442c4f45536b8534 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 2 Mar 2025 16:03:52 +0100 Subject: [PATCH 3/4] replace assertEmpty() with stricter assertions --- Tests/ProcessTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/ProcessTest.php b/Tests/ProcessTest.php index b17bfc7a..77fc2613 100644 --- a/Tests/ProcessTest.php +++ b/Tests/ProcessTest.php @@ -422,7 +422,7 @@ public function testFlushErrorOutput() $p->run(); $p->clearErrorOutput(); - $this->assertEmpty($p->getErrorOutput()); + $this->assertSame('', $p->getErrorOutput()); } /** @@ -475,7 +475,7 @@ public function testFlushOutput() $p->run(); $p->clearOutput(); - $this->assertEmpty($p->getOutput()); + $this->assertSame('', $p->getOutput()); } public function testZeroAsOutput() From 40c295f2deb408d5e9d2d32b8ba1dd61e36f05af Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Thu, 17 Apr 2025 08:03:48 +0200 Subject: [PATCH 4/4] [Process] Narrow `PhpExecutableFinder` return types --- PhpExecutableFinder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PhpExecutableFinder.php b/PhpExecutableFinder.php index 9f9218f9..f9ed79e4 100644 --- a/PhpExecutableFinder.php +++ b/PhpExecutableFinder.php @@ -83,6 +83,8 @@ public function find(bool $includeArgs = true): string|false /** * Finds the PHP executable arguments. + * + * @return list */ public function findArguments(): array {