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