diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5ea0e04a8f9..8b2753f04dc 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -41,3 +41,12 @@ jobs: tag: ${{ env.RELEASE_TAG }} name: PHPUnit ${{ env.RELEASE_TAG }} bodyFile: release-notes.md + + - name: Announce release + id: mastodon + uses: cbrgm/mastodon-github-action@v2 + with: + access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} + url: ${{ secrets.MASTODON_URL }} + language: "en" + message: "#PHPUnit ${{ env.RELEASE_TAG }} has been released: https://github.com/sebastianbergmann/phpunit/releases/tag/${{ env.RELEASE_TAG }}" diff --git a/.phive/phars.xml b/.phive/phars.xml index a9ebfc41d03..fc0e4272d92 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,8 +1,8 @@ - - + + - + diff --git a/ChangeLog-11.0.md b/ChangeLog-11.0.md index 9b41942bbd4..2306e7a4e36 100644 --- a/ChangeLog-11.0.md +++ b/ChangeLog-11.0.md @@ -2,6 +2,17 @@ All notable changes of the PHPUnit 11.0 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. +## [11.0.9] - 2024-03-28 + +### Changed + +* [#5766](https://github.com/sebastianbergmann/phpunit/pull/5766): Do not use a shell in `proc_open()` if not really needed +* [#5772](https://github.com/sebastianbergmann/phpunit/pull/5772): Cleanup process handling after dropping temp-file handling + +### Fixed + +* [#5570](https://github.com/sebastianbergmann/phpunit/pull/5570): Windows does not support exclusive locks on stdout + ## [11.0.8] - 2024-03-22 ### Fixed @@ -149,6 +160,7 @@ All notable changes of the PHPUnit 11.0 release series are documented in this fi * `PHPUnit\TextUI\Configuration\Configuration::registerMockObjectsFromTestArgumentsRecursively()` * `PHPUnit\Framework\Constraint\Constraint::exporter()` +[11.0.9]: https://github.com/sebastianbergmann/phpunit/compare/11.0.8...11.0.9 [11.0.8]: https://github.com/sebastianbergmann/phpunit/compare/11.0.7...11.0.8 [11.0.7]: https://github.com/sebastianbergmann/phpunit/compare/11.0.6...11.0.7 [11.0.6]: https://github.com/sebastianbergmann/phpunit/compare/11.0.5...11.0.6 diff --git a/composer.lock b/composer.lock index bb4c3c334e2..347e59d84ae 100644 --- a/composer.lock +++ b/composer.lock @@ -938,16 +938,16 @@ }, { "name": "sebastian/environment", - "version": "7.0.0", + "version": "7.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "100d8b855d7180f79f9a9a5c483f2d960581c3ea" + "reference": "4eb3a442574d0e9d141aab209cd4aaf25701b09a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/100d8b855d7180f79f9a9a5c483f2d960581c3ea", - "reference": "100d8b855d7180f79f9a9a5c483f2d960581c3ea", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4eb3a442574d0e9d141aab209cd4aaf25701b09a", + "reference": "4eb3a442574d0e9d141aab209cd4aaf25701b09a", "shasum": "" }, "require": { @@ -962,7 +962,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "7.1-dev" } }, "autoload": { @@ -990,7 +990,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/environment/tree/7.1.0" }, "funding": [ { @@ -998,7 +998,7 @@ "type": "github" } ], - "time": "2024-02-02T05:57:54+00:00" + "time": "2024-03-23T08:56:34+00:00" }, { "name": "sebastian/exporter", diff --git a/src/Event/Value/TestSuite/TestSuiteBuilder.php b/src/Event/Value/TestSuite/TestSuiteBuilder.php index 1e4b4a8480e..19b8a18973c 100644 --- a/src/Event/Value/TestSuite/TestSuiteBuilder.php +++ b/src/Event/Value/TestSuite/TestSuiteBuilder.php @@ -31,18 +31,6 @@ */ public static function from(FrameworkTestSuite $testSuite): TestSuite { - $groups = []; - - foreach ($testSuite->groupDetails() as $groupName => $tests) { - if (!isset($groups[$groupName])) { - $groups[$groupName] = []; - } - - foreach ($tests as $test) { - $groups[$groupName][] = $test::class; - } - } - $tests = []; self::process($testSuite, $tests); diff --git a/src/Logging/EventLogger.php b/src/Logging/EventLogger.php index 4f6ae06a2b1..1f4085b7a3c 100644 --- a/src/Logging/EventLogger.php +++ b/src/Logging/EventLogger.php @@ -40,10 +40,16 @@ public function trace(Event $event): void $indentation = PHP_EOL . str_repeat(' ', strlen($telemetryInfo)); $lines = preg_split('/\r\n|\r|\n/', $event->asString()); + $flags = FILE_APPEND; + + if (PHP_OS_FAMILY !== 'Windows' || $this->path !== 'php://stdout') { + $flags |= LOCK_EX; + } + file_put_contents( $this->path, $telemetryInfo . implode($indentation, $lines) . PHP_EOL, - FILE_APPEND | LOCK_EX, + $flags, ); } diff --git a/src/Runner/Version.php b/src/Runner/Version.php index 9ee65a8f273..5005cbd34e4 100644 --- a/src/Runner/Version.php +++ b/src/Runner/Version.php @@ -34,7 +34,7 @@ public static function id(): string } if (self::$version === '') { - self::$version = (new VersionId('11.0.8', dirname(__DIR__, 2)))->asString(); + self::$version = (new VersionId('11.0.9', dirname(__DIR__, 2)))->asString(); } return self::$version; diff --git a/src/Util/PHP/AbstractPhpProcess.php b/src/Util/PHP/AbstractPhpProcess.php index 221508057a6..7d76d73c4c9 100644 --- a/src/Util/PHP/AbstractPhpProcess.php +++ b/src/Util/PHP/AbstractPhpProcess.php @@ -9,11 +9,12 @@ */ namespace PHPUnit\Util\PHP; +use const PHP_BINARY; use const PHP_SAPI; use function array_keys; use function array_merge; use function assert; -use function escapeshellarg; +use function explode; use function file_exists; use function file_get_contents; use function ini_get_all; @@ -51,10 +52,6 @@ abstract class AbstractPhpProcess public static function factory(): self { - if (PHP_OS_FAMILY === 'Windows') { - return new WindowsPhpProcess; - } - return new DefaultPhpProcess; } @@ -154,12 +151,15 @@ public function runTestJob(string $job, Test $test, string $processResultFile): /** * Returns the command based into the configurations. + * + * @return string[] */ - public function getCommand(array $settings, ?string $file = null): string + public function getCommand(array $settings, ?string $file = null): array { $runtime = new Runtime; - $command = $runtime->getBinary(); + $command = []; + $command[] = PHP_BINARY; if ($runtime->hasPCOV()) { $settings = array_merge( @@ -177,29 +177,29 @@ public function getCommand(array $settings, ?string $file = null): string ); } - $command .= $this->settingsToParameters($settings); + $command = array_merge($command, $this->settingsToParameters($settings)); if (PHP_SAPI === 'phpdbg') { - $command .= ' -qrr'; + $command[] = '-qrr'; if (!$file) { - $command .= 's='; + $command[] = 's='; } } if ($file) { - $command .= ' ' . escapeshellarg($file); + $command[] = '-f'; + $command[] = $file; } if ($this->arguments) { if (!$file) { - $command .= ' --'; + $command[] = '--'; } - $command .= ' ' . $this->arguments; - } - if ($this->stderrRedirection) { - $command .= ' 2>&1'; + foreach (explode(' ', $this->arguments) as $arg) { + $command[] = trim($arg); + } } return $command; @@ -210,12 +210,16 @@ public function getCommand(array $settings, ?string $file = null): string */ abstract public function runJob(string $job, array $settings = []): array; - protected function settingsToParameters(array $settings): string + /** + * @return list + */ + protected function settingsToParameters(array $settings): array { - $buffer = ''; + $buffer = []; foreach ($settings as $setting) { - $buffer .= ' -d ' . escapeshellarg($setting); + $buffer[] = '-d'; + $buffer[] = $setting; } return $buffer; diff --git a/src/Util/PHP/DefaultPhpProcess.php b/src/Util/PHP/DefaultPhpProcess.php index 658c0ce7fba..963109dee42 100644 --- a/src/Util/PHP/DefaultPhpProcess.php +++ b/src/Util/PHP/DefaultPhpProcess.php @@ -17,7 +17,6 @@ use function is_resource; use function proc_close; use function proc_open; -use function rewind; use function stream_get_contents; use function sys_get_temp_dir; use function tempnam; @@ -41,7 +40,7 @@ class DefaultPhpProcess extends AbstractPhpProcess */ public function runJob(string $job, array $settings = []): array { - if ($this->stdin || $this->useTemporaryFile()) { + if ($this->stdin) { if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'phpunit_')) || file_put_contents($this->tempFile, $job) === false) { throw new PhpProcessException( @@ -55,14 +54,6 @@ public function runJob(string $job, array $settings = []): array return $this->runProcess($job, $settings); } - /** - * Returns an array of file handles to be used in place of pipes. - */ - protected function getHandles(): array - { - return []; - } - /** * Handles creating the child process and returning the STDOUT and STDERR. * @@ -73,8 +64,6 @@ protected function getHandles(): array */ protected function runProcess(string $job, array $settings): array { - $handles = $this->getHandles(); - $env = null; if ($this->env) { @@ -90,11 +79,15 @@ protected function runProcess(string $job, array $settings): array } $pipeSpec = [ - 0 => $handles[0] ?? ['pipe', 'r'], - 1 => $handles[1] ?? ['pipe', 'w'], - 2 => $handles[2] ?? ['pipe', 'w'], + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], ]; + if ($this->stderrRedirection) { + $pipeSpec[2] = ['redirect', 1]; + } + $process = proc_open( $this->getCommand($settings, $this->tempFile), $pipeSpec, @@ -129,22 +122,6 @@ protected function runProcess(string $job, array $settings): array fclose($pipes[2]); } - if (isset($handles[1])) { - rewind($handles[1]); - - $stdout = stream_get_contents($handles[1]); - - fclose($handles[1]); - } - - if (isset($handles[2])) { - rewind($handles[2]); - - $stderr = stream_get_contents($handles[2]); - - fclose($handles[2]); - } - proc_close($process); $this->cleanup(); @@ -166,9 +143,4 @@ protected function cleanup(): void unlink($this->tempFile); } } - - protected function useTemporaryFile(): bool - { - return false; - } } diff --git a/src/Util/PHP/WindowsPhpProcess.php b/src/Util/PHP/WindowsPhpProcess.php deleted file mode 100644 index 74524996d36..00000000000 --- a/src/Util/PHP/WindowsPhpProcess.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; - -use function tmpfile; -use PHPUnit\Framework\Exception; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @see https://bugs.php.net/bug.php?id=51800 - */ -final class WindowsPhpProcess extends DefaultPhpProcess -{ - /** - * @throws Exception - * @throws PhpProcessException - */ - protected function getHandles(): array - { - if (false === $stdout_handle = tmpfile()) { - throw new PhpProcessException( - 'A temporary file could not be created; verify that your TEMP environment variable is writable', - ); - } - - return [ - 1 => $stdout_handle, - ]; - } - - protected function useTemporaryFile(): bool - { - return true; - } -} diff --git a/tools/composer b/tools/composer index bd5bab8c1f5..e6ba7bbadc9 100755 Binary files a/tools/composer and b/tools/composer differ diff --git a/tools/php-cs-fixer b/tools/php-cs-fixer index 8182b0f73ee..ebf8499e2b3 100755 Binary files a/tools/php-cs-fixer and b/tools/php-cs-fixer differ diff --git a/tools/psalm b/tools/psalm index f3bb498469a..ab78cf689dd 100755 Binary files a/tools/psalm and b/tools/psalm differ