From 574fb63bc975af1be8c1ae6783052fa089b1d565 Mon Sep 17 00:00:00 2001 From: Sebastian Bergmann Date: Sat, 20 Jun 2026 11:04:33 +0200 Subject: [PATCH 1/2] Use sebastian/version-requirement --- README.md | 1 + build.xml | 7 ++ composer.json | 2 + composer.lock | 75 ++++++++++++++- src/Metadata/Api/Requirements.php | 6 +- .../InvalidVersionRequirementException.php | 19 ---- src/Metadata/Metadata.php | 2 +- src/Metadata/Parser/AttributeParser.php | 6 +- src/Metadata/RequiresPhp.php | 2 +- src/Metadata/RequiresPhpExtension.php | 2 +- src/Metadata/RequiresPhpunit.php | 2 +- .../Version/ComparisonRequirement.php | 45 --------- .../Version/ConstraintRequirement.php | 55 ----------- .../Version/InvalidVersionRequirement.php | 44 --------- src/Metadata/Version/Requirement.php | 57 ------------ src/TextUI/Configuration/Registry.php | 2 +- .../Configuration/Value/TestDirectory.php | 2 +- src/TextUI/Configuration/Value/TestFile.php | 2 +- src/TextUI/Configuration/Xml/Loader.php | 2 +- .../InvalidVersionOperatorException.php | 31 ------- src/Util/VersionComparisonOperator.php | 57 ------------ .../unit/Metadata/MetadataCollectionTest.php | 4 +- tests/unit/Metadata/MetadataTest.php | 4 +- .../Parser/AttributeParserTestCase.php | 4 +- .../Version/InvalidVersionRequirementTest.php | 35 ------- .../unit/Metadata/Version/RequirementTest.php | 92 ------------------- .../Value/TestDirectoryCollectionTest.php | 2 +- .../Configuration/Value/TestDirectoryTest.php | 2 +- .../Value/TestFileCollectionTest.php | 2 +- .../Configuration/Value/TestFileTest.php | 2 +- .../Util/VersionComparisonOperatorTest.php | 59 ------------ 31 files changed, 107 insertions(+), 520 deletions(-) delete mode 100644 src/Metadata/Exception/InvalidVersionRequirementException.php delete mode 100644 src/Metadata/Version/ComparisonRequirement.php delete mode 100644 src/Metadata/Version/ConstraintRequirement.php delete mode 100644 src/Metadata/Version/InvalidVersionRequirement.php delete mode 100644 src/Metadata/Version/Requirement.php delete mode 100644 src/Util/Exception/InvalidVersionOperatorException.php delete mode 100644 src/Util/VersionComparisonOperator.php delete mode 100644 tests/unit/Metadata/Version/InvalidVersionRequirementTest.php delete mode 100644 tests/unit/Metadata/Version/RequirementTest.php delete mode 100644 tests/unit/Util/VersionComparisonOperatorTest.php diff --git a/README.md b/README.md index d75fb0204c..1768ef1e47 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Here is a list of all components that are primarily developed and maintained by * [sebastian/recursion-context](https://github.com/sebastianbergmann/recursion-context) * [sebastian/type](https://github.com/sebastianbergmann/type) * [sebastian/version](https://github.com/sebastianbergmann/version) +* [sebastian/version-requirement](https://github.com/sebastianbergmann/version-requirement) A very special thanks to everyone who has contributed to the [PHPUnit Manual](https://github.com/sebastianbergmann/phpunit-documentation-english). diff --git a/build.xml b/build.xml index d8b84f68c5..8fc7888927 100644 --- a/build.xml +++ b/build.xml @@ -303,6 +303,13 @@ + + + + + + + diff --git a/composer.json b/composer.json index 58ba7e0d06..68ce9012e5 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "security": "https://github.com/sebastianbergmann/phpunit/security/policy" }, "prefer-stable": true, + "minimum-stability": "dev", "require": { "php": ">=8.4.1", "ext-dom": "*", @@ -55,6 +56,7 @@ "sebastian/recursion-context": "^8.0.0", "sebastian/type": "^7.0.1", "sebastian/version": "^7.0.0", + "sebastian/version-requirement": "^1.0", "staabm/side-effects-detector": "^1.0.5" }, "config": { diff --git a/composer.lock b/composer.lock index 9e7067479c..3ed67ce81d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fdd1cf0b879485ea837501dd977847ec", + "content-hash": "ce29f3be7009692e093af4b0ebb7bc68", "packages": [ { "name": "myclabs/deep-copy", @@ -1732,6 +1732,77 @@ ], "time": "2026-02-06T04:52:52+00:00" }, + { + "name": "sebastian/version-requirement", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version-requirement.git", + "reference": "7662b2811918858a5421fbbaebdfa87f68f39190" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version-requirement/zipball/7662b2811918858a5421fbbaebdfa87f68f39190", + "reference": "7662b2811918858a5421fbbaebdfa87f68f39190", + "shasum": "" + }, + "require": { + "phar-io/version": "^3.2.1", + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.2.1" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for working with version requirements", + "homepage": "https://github.com/sebastianbergmann/version-requirement", + "support": { + "issues": "https://github.com/sebastianbergmann/version-requirement/issues", + "security": "https://github.com/sebastianbergmann/version-requirement/security/policy", + "source": "https://github.com/sebastianbergmann/version-requirement/tree/main" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/version-requirement", + "type": "tidelift" + } + ], + "time": "2026-06-20T08:35:21+00:00" + }, { "name": "staabm/side-effects-detector", "version": "1.0.5", @@ -1837,7 +1908,7 @@ ], "packages-dev": [], "aliases": [], - "minimum-stability": "stable", + "minimum-stability": "dev", "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, diff --git a/src/Metadata/Api/Requirements.php b/src/Metadata/Api/Requirements.php index a1198206ef..c634f5c09a 100644 --- a/src/Metadata/Api/Requirements.php +++ b/src/Metadata/Api/Requirements.php @@ -37,11 +37,11 @@ use PHPUnit\Metadata\RequiresPhpunit; use PHPUnit\Metadata\RequiresPhpunitExtension; use PHPUnit\Metadata\RequiresSetting; -use PHPUnit\Metadata\Version\ComparisonRequirement; -use PHPUnit\Metadata\Version\InvalidVersionRequirement; -use PHPUnit\Metadata\Version\Requirement; use PHPUnit\Runner\Version; use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; +use SebastianBergmann\VersionRequirement\ComparisonRequirement; +use SebastianBergmann\VersionRequirement\InvalidVersionRequirement; +use SebastianBergmann\VersionRequirement\Requirement; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit diff --git a/src/Metadata/Exception/InvalidVersionRequirementException.php b/src/Metadata/Exception/InvalidVersionRequirementException.php deleted file mode 100644 index 359f723c1d..0000000000 --- a/src/Metadata/Exception/InvalidVersionRequirementException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -use RuntimeException; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class InvalidVersionRequirementException extends RuntimeException implements Exception -{ -} diff --git a/src/Metadata/Metadata.php b/src/Metadata/Metadata.php index 9ef0fff102..92b716bf9f 100644 --- a/src/Metadata/Metadata.php +++ b/src/Metadata/Metadata.php @@ -10,8 +10,8 @@ namespace PHPUnit\Metadata; use Closure; -use PHPUnit\Metadata\Version\Requirement; use PHPUnit\Runner\Extension\Extension; +use SebastianBergmann\VersionRequirement\Requirement; /** * @immutable diff --git a/src/Metadata/Parser/AttributeParser.php b/src/Metadata/Parser/AttributeParser.php index d0409eaf16..f3405f05ff 100644 --- a/src/Metadata/Parser/AttributeParser.php +++ b/src/Metadata/Parser/AttributeParser.php @@ -93,13 +93,13 @@ use PHPUnit\Framework\Attributes\UsesTrait; use PHPUnit\Framework\Attributes\WithEnvironmentVariable; use PHPUnit\Framework\Attributes\WithoutErrorHandler; -use PHPUnit\Metadata\InvalidVersionRequirementException; use PHPUnit\Metadata\Metadata; use PHPUnit\Metadata\MetadataCollection; -use PHPUnit\Metadata\Version\InvalidVersionRequirement; -use PHPUnit\Metadata\Version\Requirement; use ReflectionClass; use ReflectionMethod; +use SebastianBergmann\VersionRequirement\InvalidVersionRequirement; +use SebastianBergmann\VersionRequirement\InvalidVersionRequirementException; +use SebastianBergmann\VersionRequirement\Requirement; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit diff --git a/src/Metadata/RequiresPhp.php b/src/Metadata/RequiresPhp.php index a5805013bc..b1c9d4f688 100644 --- a/src/Metadata/RequiresPhp.php +++ b/src/Metadata/RequiresPhp.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\Metadata; -use PHPUnit\Metadata\Version\Requirement; +use SebastianBergmann\VersionRequirement\Requirement; /** * @immutable diff --git a/src/Metadata/RequiresPhpExtension.php b/src/Metadata/RequiresPhpExtension.php index fd7d424358..45932037c5 100644 --- a/src/Metadata/RequiresPhpExtension.php +++ b/src/Metadata/RequiresPhpExtension.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\Metadata; -use PHPUnit\Metadata\Version\Requirement; +use SebastianBergmann\VersionRequirement\Requirement; /** * @immutable diff --git a/src/Metadata/RequiresPhpunit.php b/src/Metadata/RequiresPhpunit.php index f0c08c57fc..fa40f9776c 100644 --- a/src/Metadata/RequiresPhpunit.php +++ b/src/Metadata/RequiresPhpunit.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\Metadata; -use PHPUnit\Metadata\Version\Requirement; +use SebastianBergmann\VersionRequirement\Requirement; /** * @immutable diff --git a/src/Metadata/Version/ComparisonRequirement.php b/src/Metadata/Version/ComparisonRequirement.php deleted file mode 100644 index b87222fd0d..0000000000 --- a/src/Metadata/Version/ComparisonRequirement.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Version; - -use function version_compare; -use PHPUnit\Util\VersionComparisonOperator; - -/** - * @immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class ComparisonRequirement extends Requirement -{ - private string $version; - private VersionComparisonOperator $operator; - - public function __construct(string $version, VersionComparisonOperator $operator) - { - $this->version = $version; - $this->operator = $operator; - } - - public function isSatisfiedBy(string $version): bool - { - return version_compare($version, $this->version, $this->operator->asString()); - } - - public function asString(): string - { - return $this->operator->asString() . ' ' . $this->version; - } - - public function version(): string - { - return $this->version; - } -} diff --git a/src/Metadata/Version/ConstraintRequirement.php b/src/Metadata/Version/ConstraintRequirement.php deleted file mode 100644 index 752fe7b6d1..0000000000 --- a/src/Metadata/Version/ConstraintRequirement.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Version; - -use function assert; -use function preg_replace; -use PharIo\Version\Version; -use PharIo\Version\VersionConstraint; - -/** - * @immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class ConstraintRequirement extends Requirement -{ - private VersionConstraint $constraint; - - public function __construct(VersionConstraint $constraint) - { - $this->constraint = $constraint; - } - - public function isSatisfiedBy(string $version): bool - { - return $this->constraint->complies( - new Version($this->sanitize($version)), - ); - } - - public function asString(): string - { - return $this->constraint->asString(); - } - - private function sanitize(string $version): string - { - $sanitized = preg_replace( - '/^(\d+\.\d+(?:.\d+)?).*$/', - '$1', - $version, - ); - - assert($sanitized !== null); - - return $sanitized; - } -} diff --git a/src/Metadata/Version/InvalidVersionRequirement.php b/src/Metadata/Version/InvalidVersionRequirement.php deleted file mode 100644 index 9f3e370068..0000000000 --- a/src/Metadata/Version/InvalidVersionRequirement.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Version; - -/** - * @immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class InvalidVersionRequirement extends Requirement -{ - /** - * @var non-empty-string - */ - private string $message; - - /** - * @param non-empty-string $message - */ - public function __construct(string $message) - { - $this->message = $message; - } - - public function isSatisfiedBy(string $version): bool - { - return false; - } - - /** - * @return non-empty-string - */ - public function asString(): string - { - return $this->message; - } -} diff --git a/src/Metadata/Version/Requirement.php b/src/Metadata/Version/Requirement.php deleted file mode 100644 index 01f98f7310..0000000000 --- a/src/Metadata/Version/Requirement.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Version; - -use function preg_match; -use PharIo\Version\UnsupportedVersionConstraintException; -use PharIo\Version\VersionConstraintParser; -use PHPUnit\Metadata\InvalidVersionRequirementException; -use PHPUnit\Util\InvalidVersionOperatorException; -use PHPUnit\Util\VersionComparisonOperator; - -/** - * @immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract readonly class Requirement -{ - private const string VERSION_COMPARISON = "/(?P!=|<|<=|<>|=|==|>|>=)?\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m"; - - /** - * @throws InvalidVersionOperatorException - * @throws InvalidVersionRequirementException - */ - public static function from(string $versionRequirement): self - { - try { - return new ConstraintRequirement( - (new VersionConstraintParser)->parse( - $versionRequirement, - ), - ); - } catch (UnsupportedVersionConstraintException) { - if (preg_match(self::VERSION_COMPARISON, $versionRequirement, $matches) > 0) { - return new ComparisonRequirement( - $matches['version'], - new VersionComparisonOperator( - $matches['operator'] !== '' ? $matches['operator'] : '>=', - ), - ); - } - } - - throw new InvalidVersionRequirementException; - } - - abstract public function isSatisfiedBy(string $version): bool; - - abstract public function asString(): string; -} diff --git a/src/TextUI/Configuration/Registry.php b/src/TextUI/Configuration/Registry.php index f29bc80f72..de07aec4d4 100644 --- a/src/TextUI/Configuration/Registry.php +++ b/src/TextUI/Configuration/Registry.php @@ -18,7 +18,7 @@ use PHPUnit\TextUI\CliArguments\Configuration as CliConfiguration; use PHPUnit\TextUI\CliArguments\Exception; use PHPUnit\TextUI\XmlConfiguration\Configuration as XmlConfiguration; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; /** * CLI options and XML configuration are static within a single PHPUnit process. diff --git a/src/TextUI/Configuration/Value/TestDirectory.php b/src/TextUI/Configuration/Value/TestDirectory.php index 2997fd7b47..d037d212aa 100644 --- a/src/TextUI/Configuration/Value/TestDirectory.php +++ b/src/TextUI/Configuration/Value/TestDirectory.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TextUI\Configuration; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit diff --git a/src/TextUI/Configuration/Value/TestFile.php b/src/TextUI/Configuration/Value/TestFile.php index e658ff8843..7b9f548a70 100644 --- a/src/TextUI/Configuration/Value/TestFile.php +++ b/src/TextUI/Configuration/Value/TestFile.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TextUI\Configuration; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit diff --git a/src/TextUI/Configuration/Xml/Loader.php b/src/TextUI/Configuration/Xml/Loader.php index 0b4f464d19..892396b491 100644 --- a/src/TextUI/Configuration/Xml/Loader.php +++ b/src/TextUI/Configuration/Xml/Loader.php @@ -77,11 +77,11 @@ use PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity; use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; -use PHPUnit\Util\VersionComparisonOperator; use PHPUnit\Util\Xml\Loader as XmlLoader; use PHPUnit\Util\Xml\XmlException; use SebastianBergmann\CodeCoverage\Report\Html\Colors; use SebastianBergmann\CodeCoverage\Report\Thresholds; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; use Throwable; /** diff --git a/src/Util/Exception/InvalidVersionOperatorException.php b/src/Util/Exception/InvalidVersionOperatorException.php deleted file mode 100644 index bc2fe9a0ed..0000000000 --- a/src/Util/Exception/InvalidVersionOperatorException.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use function sprintf; -use RuntimeException; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvalidVersionOperatorException extends RuntimeException implements Exception -{ - public function __construct(string $operator) - { - parent::__construct( - sprintf( - '"%s" is not a valid version_compare() operator', - $operator, - ), - ); - } -} diff --git a/src/Util/VersionComparisonOperator.php b/src/Util/VersionComparisonOperator.php deleted file mode 100644 index 9dcba3c32a..0000000000 --- a/src/Util/VersionComparisonOperator.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use function in_array; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @immutable - */ -final readonly class VersionComparisonOperator -{ - /** - * @var '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' - */ - private string $operator; - - /** - * @param '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' $operator - * - * @throws InvalidVersionOperatorException - */ - public function __construct(string $operator) - { - $this->ensureOperatorIsValid($operator); - - $this->operator = $operator; - } - - /** - * @return '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' - */ - public function asString(): string - { - return $this->operator; - } - - /** - * @param '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' $operator - * - * @throws InvalidVersionOperatorException - */ - private function ensureOperatorIsValid(string $operator): void - { - if (!in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], true)) { - throw new InvalidVersionOperatorException($operator); - } - } -} diff --git a/tests/unit/Metadata/MetadataCollectionTest.php b/tests/unit/Metadata/MetadataCollectionTest.php index 591b811d8b..e6ade59213 100644 --- a/tests/unit/Metadata/MetadataCollectionTest.php +++ b/tests/unit/Metadata/MetadataCollectionTest.php @@ -14,8 +14,8 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; -use PHPUnit\Metadata\Version\ComparisonRequirement; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\ComparisonRequirement; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; use stdClass; #[CoversClass(MetadataCollection::class)] diff --git a/tests/unit/Metadata/MetadataTest.php b/tests/unit/Metadata/MetadataTest.php index 5b0f662074..f6e81c4d29 100644 --- a/tests/unit/Metadata/MetadataTest.php +++ b/tests/unit/Metadata/MetadataTest.php @@ -14,9 +14,9 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; -use PHPUnit\Metadata\Version\ComparisonRequirement; use PHPUnit\TestFixture\Metadata\Attribute\ExampleTrait; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\ComparisonRequirement; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; #[CoversClass(Metadata::class)] #[CoversClassesThatExtendClass(Metadata::class)] diff --git a/tests/unit/Metadata/Parser/AttributeParserTestCase.php b/tests/unit/Metadata/Parser/AttributeParserTestCase.php index d6bbeacd24..47265d53c3 100644 --- a/tests/unit/Metadata/Parser/AttributeParserTestCase.php +++ b/tests/unit/Metadata/Parser/AttributeParserTestCase.php @@ -23,8 +23,6 @@ use PHPUnit\Metadata\RequiresPhpunitExtension; use PHPUnit\Metadata\RequiresSetting; use PHPUnit\Metadata\Retry; -use PHPUnit\Metadata\Version\ComparisonRequirement; -use PHPUnit\Metadata\Version\ConstraintRequirement; use PHPUnit\Metadata\WithEnvironmentVariable; use PHPUnit\TestFixture\Metadata\Attribute\AllowMockObjectsWithoutExpectationsOnClassTest; use PHPUnit\TestFixture\Metadata\Attribute\AllowMockObjectsWithoutExpectationsOnMethodTest; @@ -72,6 +70,8 @@ use PHPUnit\TestFixture\Metadata\Attribute\UsesTest; use PHPUnit\TestFixture\Metadata\Attribute\WithEnvironmentVariableTest; use PHPUnit\TestFixture\Metadata\Attribute\WithoutErrorHandlerTest; +use SebastianBergmann\VersionRequirement\ComparisonRequirement; +use SebastianBergmann\VersionRequirement\ConstraintRequirement; abstract class AttributeParserTestCase extends TestCase { diff --git a/tests/unit/Metadata/Version/InvalidVersionRequirementTest.php b/tests/unit/Metadata/Version/InvalidVersionRequirementTest.php deleted file mode 100644 index 975054679a..0000000000 --- a/tests/unit/Metadata/Version/InvalidVersionRequirementTest.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Version; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Group; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; - -#[CoversClass(InvalidVersionRequirement::class)] -#[Small] -#[Group('metadata')] -final class InvalidVersionRequirementTest extends TestCase -{ - public function testCanBeRepresentedAsString(): void - { - $requirement = new InvalidVersionRequirement('message'); - - $this->assertSame('message', $requirement->asString()); - } - - public function testIsNeverSatisfied(): void - { - $requirement = new InvalidVersionRequirement('message'); - - $this->assertFalse($requirement->isSatisfiedBy('1.0.0')); - } -} diff --git a/tests/unit/Metadata/Version/RequirementTest.php b/tests/unit/Metadata/Version/RequirementTest.php deleted file mode 100644 index f94a05bd45..0000000000 --- a/tests/unit/Metadata/Version/RequirementTest.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -use PharIo\Version\VersionConstraintParser; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\Group; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\Attributes\UsesClass; -use PHPUnit\Framework\TestCase; -use PHPUnit\Metadata\Version\ComparisonRequirement; -use PHPUnit\Metadata\Version\ConstraintRequirement; -use PHPUnit\Metadata\Version\Requirement; -use PHPUnit\Util\VersionComparisonOperator; - -#[CoversClass(ComparisonRequirement::class)] -#[CoversClass(ConstraintRequirement::class)] -#[CoversClass(Requirement::class)] -#[UsesClass(VersionComparisonOperator::class)] -#[Small] -#[Group('metadata')] -final class RequirementTest extends TestCase -{ - /** - * @return non-empty-list - */ - public static function constraintProvider(): array - { - return [ - [ - true, - '1.0.0', - new ConstraintRequirement( - (new VersionConstraintParser)->parse('1.0.0'), - ), - ], - ]; - } - - /** - * @return non-empty-list - */ - public static function comparisonProvider(): array - { - return [ - [true, '1.0.0', new ComparisonRequirement('1.0.0', new VersionComparisonOperator('='))], - ]; - } - - public function testCanBeCreatedFromStringWithVersionConstraint(): void - { - $requirement = Requirement::from('^1.0'); - - $this->assertInstanceOf(ConstraintRequirement::class, $requirement); - $this->assertSame('^1.0', $requirement->asString()); - } - - #[DataProvider('constraintProvider')] - public function testVersionRequirementCanBeCheckedUsingVersionConstraint(bool $expected, string $version, ConstraintRequirement $requirement): void - { - $this->assertSame($expected, $requirement->isSatisfiedBy($version)); - } - - public function testCanBeCreatedFromStringWithSimpleComparison(): void - { - $requirement = Requirement::from('>= 1.0'); - - $this->assertInstanceOf(ComparisonRequirement::class, $requirement); - $this->assertSame('>= 1.0', $requirement->asString()); - } - - #[DataProvider('comparisonProvider')] - public function testVersionRequirementCanBeCheckedUsingSimpleComparison(bool $expected, string $version, ComparisonRequirement $requirement): void - { - $this->assertSame($expected, $requirement->isSatisfiedBy($version)); - } - - public function testCannotBeCreatedFromInvalidString(): void - { - $this->expectException(InvalidVersionRequirementException::class); - - Requirement::from('invalid'); - } -} diff --git a/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php b/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php index 928b67dd85..a0f205dfa5 100644 --- a/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php +++ b/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; #[CoversClass(TestDirectoryCollection::class)] #[CoversClass(TestDirectoryCollectionIterator::class)] diff --git a/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php b/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php index e1ebcbfa84..c9437b5829 100644 --- a/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php +++ b/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; #[CoversClass(TestDirectory::class)] #[Small] diff --git a/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php b/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php index 43cd4c56bd..2dda3e971a 100644 --- a/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php +++ b/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; #[CoversClass(TestFileCollection::class)] #[CoversClass(TestFileCollectionIterator::class)] diff --git a/tests/unit/TextUI/Configuration/Value/TestFileTest.php b/tests/unit/TextUI/Configuration/Value/TestFileTest.php index 31db4d9c7c..1cf5523bfb 100644 --- a/tests/unit/TextUI/Configuration/Value/TestFileTest.php +++ b/tests/unit/TextUI/Configuration/Value/TestFileTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; -use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator; #[CoversClass(TestFile::class)] #[Small] diff --git a/tests/unit/Util/VersionComparisonOperatorTest.php b/tests/unit/Util/VersionComparisonOperatorTest.php deleted file mode 100644 index 39745275a9..0000000000 --- a/tests/unit/Util/VersionComparisonOperatorTest.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; - -#[CoversClass(VersionComparisonOperator::class)] -#[CoversClass(InvalidVersionOperatorException::class)] -#[Small] -final class VersionComparisonOperatorTest extends TestCase -{ - /** - * @return non-empty-list> - */ - public static function validValues(): array - { - return [ - ['<'], - ['lt'], - ['<='], - ['le'], - ['>'], - ['gt'], - ['>='], - ['ge'], - ['=='], - ['='], - ['eq'], - ['!='], - ['<>'], - ['ne'], - ]; - } - - #[DataProvider('validValues')] - #[TestDox('Can be created from "$string"')] - public function testCanBeCreatedFromValidString(string $string): void - { - $this->assertSame($string, new VersionComparisonOperator($string)->asString()); - } - - public function testCannotBeCreatedFromInvalidString(): void - { - $this->expectException(InvalidVersionOperatorException::class); - - new VersionComparisonOperator(''); - } -} From 661729203a48e86be148e7ec11ee8e27ccacbdcf Mon Sep 17 00:00:00 2001 From: Sebastian Bergmann Date: Sun, 21 Jun 2026 07:47:17 +0200 Subject: [PATCH 2/2] Use sebastian/version-requirement without leaking it to event consumers The version-requirement parsing, operator validation, and satisfaction checking are delegated to sebastian/version-requirement, but the package is fully encapsulated behind PHPUnit-owned value objects. The value objects exposed through events (Requirement and its subtypes via test metadata, VersionComparisonOperator via the configuration) are now pure data transfer objects: they hold only scalar values, aggregate no objects from the package, and carry no behavior. The satisfaction check (isSatisfiedBy()) has moved to Metadata\Api\Requirements, which is where the package is used. As the value objects no longer hold package objects, they serialize cleanly across separate-process test runs. The package is referenced at exactly three boundaries: Requirement::from() (parsing), VersionComparisonOperator (operator validation), and Requirements::isSatisfiedBy() (satisfaction checking). Package exceptions are converted to PHPUnit exceptions at those boundaries so they do not leak either. --- src/Metadata/Api/Requirements.php | 24 ++++++-- .../InvalidVersionRequirementException.php | 19 ++++++ src/Metadata/Metadata.php | 2 +- src/Metadata/Parser/AttributeParser.php | 6 +- src/Metadata/RequiresPhp.php | 2 +- src/Metadata/RequiresPhpExtension.php | 2 +- src/Metadata/RequiresPhpunit.php | 2 +- .../Version/ComparisonRequirement.php | 39 ++++++++++++ .../Version/ConstraintRequirement.php | 30 ++++++++++ .../Version/InvalidVersionRequirement.php | 39 ++++++++++++ src/Metadata/Version/Requirement.php | 56 ++++++++++++++++++ src/TextUI/Configuration/Registry.php | 2 +- .../Configuration/Value/TestDirectory.php | 2 +- src/TextUI/Configuration/Value/TestFile.php | 2 +- src/TextUI/Configuration/Xml/Loader.php | 2 +- .../InvalidVersionOperatorException.php | 31 ++++++++++ src/Util/VersionComparisonOperator.php | 46 +++++++++++++++ .../unit/Metadata/MetadataCollectionTest.php | 13 +--- tests/unit/Metadata/MetadataTest.php | 33 +++-------- .../Parser/AttributeParserTestCase.php | 4 +- .../Version/InvalidVersionRequirementTest.php | 28 +++++++++ .../unit/Metadata/Version/RequirementTest.php | 53 +++++++++++++++++ .../Value/TestDirectoryCollectionTest.php | 2 +- .../Configuration/Value/TestDirectoryTest.php | 2 +- .../Value/TestFileCollectionTest.php | 2 +- .../Configuration/Value/TestFileTest.php | 2 +- .../Util/VersionComparisonOperatorTest.php | 59 +++++++++++++++++++ 27 files changed, 445 insertions(+), 59 deletions(-) create mode 100644 src/Metadata/Exception/InvalidVersionRequirementException.php create mode 100644 src/Metadata/Version/ComparisonRequirement.php create mode 100644 src/Metadata/Version/ConstraintRequirement.php create mode 100644 src/Metadata/Version/InvalidVersionRequirement.php create mode 100644 src/Metadata/Version/Requirement.php create mode 100644 src/Util/Exception/InvalidVersionOperatorException.php create mode 100644 src/Util/VersionComparisonOperator.php create mode 100644 tests/unit/Metadata/Version/InvalidVersionRequirementTest.php create mode 100644 tests/unit/Metadata/Version/RequirementTest.php create mode 100644 tests/unit/Util/VersionComparisonOperatorTest.php diff --git a/src/Metadata/Api/Requirements.php b/src/Metadata/Api/Requirements.php index c634f5c09a..5f15be4919 100644 --- a/src/Metadata/Api/Requirements.php +++ b/src/Metadata/Api/Requirements.php @@ -37,11 +37,12 @@ use PHPUnit\Metadata\RequiresPhpunit; use PHPUnit\Metadata\RequiresPhpunitExtension; use PHPUnit\Metadata\RequiresSetting; +use PHPUnit\Metadata\Version\ComparisonRequirement; +use PHPUnit\Metadata\Version\InvalidVersionRequirement; +use PHPUnit\Metadata\Version\Requirement; use PHPUnit\Runner\Version; use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; -use SebastianBergmann\VersionRequirement\ComparisonRequirement; -use SebastianBergmann\VersionRequirement\InvalidVersionRequirement; -use SebastianBergmann\VersionRequirement\Requirement; +use SebastianBergmann\VersionRequirement\Requirement as RequirementImplementation; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit @@ -68,7 +69,7 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam $this->warnAboutIncompleteVersion($metadata->versionRequirement(), $className, $methodName); - if (!$versionRequirement->isSatisfiedBy(PHP_VERSION)) { + if (!$this->isSatisfiedBy($versionRequirement, PHP_VERSION)) { $notSatisfied[] = sprintf( 'PHP %s is required.', $versionRequirement->asString(), @@ -91,7 +92,7 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam if (!extension_loaded($metadata->extension()) || ($metadata->hasVersionRequirement() && - !$metadata->versionRequirement()->isSatisfiedBy($extensionVersion))) { + !$this->isSatisfiedBy($metadata->versionRequirement(), $extensionVersion))) { $notSatisfied[] = sprintf( 'PHP extension %s%s is required.', $metadata->extension(), @@ -107,7 +108,7 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam $this->warnAboutIncompleteVersion($metadata->versionRequirement(), $className, $methodName); - if (!$versionRequirement->isSatisfiedBy(Version::id())) { + if (!$this->isSatisfiedBy($versionRequirement, Version::id())) { $notSatisfied[] = sprintf( 'PHPUnit %s is required.', $versionRequirement->asString(), @@ -269,6 +270,17 @@ public function requiresXdebug(string $className, string $methodName): bool return false; } + private function isSatisfiedBy(Requirement $versionRequirement, string $version): bool + { + $requirement = $versionRequirement->asString(); + + if ($versionRequirement instanceof InvalidVersionRequirement || $requirement === '') { + return false; + } + + return RequirementImplementation::from($requirement)->isSatisfiedBy($version); + } + /** * @param class-string $className * @param non-empty-string $methodName diff --git a/src/Metadata/Exception/InvalidVersionRequirementException.php b/src/Metadata/Exception/InvalidVersionRequirementException.php new file mode 100644 index 0000000000..359f723c1d --- /dev/null +++ b/src/Metadata/Exception/InvalidVersionRequirementException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use RuntimeException; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidVersionRequirementException extends RuntimeException implements Exception +{ +} diff --git a/src/Metadata/Metadata.php b/src/Metadata/Metadata.php index 92b716bf9f..9ef0fff102 100644 --- a/src/Metadata/Metadata.php +++ b/src/Metadata/Metadata.php @@ -10,8 +10,8 @@ namespace PHPUnit\Metadata; use Closure; +use PHPUnit\Metadata\Version\Requirement; use PHPUnit\Runner\Extension\Extension; -use SebastianBergmann\VersionRequirement\Requirement; /** * @immutable diff --git a/src/Metadata/Parser/AttributeParser.php b/src/Metadata/Parser/AttributeParser.php index f3405f05ff..d0409eaf16 100644 --- a/src/Metadata/Parser/AttributeParser.php +++ b/src/Metadata/Parser/AttributeParser.php @@ -93,13 +93,13 @@ use PHPUnit\Framework\Attributes\UsesTrait; use PHPUnit\Framework\Attributes\WithEnvironmentVariable; use PHPUnit\Framework\Attributes\WithoutErrorHandler; +use PHPUnit\Metadata\InvalidVersionRequirementException; use PHPUnit\Metadata\Metadata; use PHPUnit\Metadata\MetadataCollection; +use PHPUnit\Metadata\Version\InvalidVersionRequirement; +use PHPUnit\Metadata\Version\Requirement; use ReflectionClass; use ReflectionMethod; -use SebastianBergmann\VersionRequirement\InvalidVersionRequirement; -use SebastianBergmann\VersionRequirement\InvalidVersionRequirementException; -use SebastianBergmann\VersionRequirement\Requirement; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit diff --git a/src/Metadata/RequiresPhp.php b/src/Metadata/RequiresPhp.php index b1c9d4f688..a5805013bc 100644 --- a/src/Metadata/RequiresPhp.php +++ b/src/Metadata/RequiresPhp.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\Metadata; -use SebastianBergmann\VersionRequirement\Requirement; +use PHPUnit\Metadata\Version\Requirement; /** * @immutable diff --git a/src/Metadata/RequiresPhpExtension.php b/src/Metadata/RequiresPhpExtension.php index 45932037c5..fd7d424358 100644 --- a/src/Metadata/RequiresPhpExtension.php +++ b/src/Metadata/RequiresPhpExtension.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\Metadata; -use SebastianBergmann\VersionRequirement\Requirement; +use PHPUnit\Metadata\Version\Requirement; /** * @immutable diff --git a/src/Metadata/RequiresPhpunit.php b/src/Metadata/RequiresPhpunit.php index fa40f9776c..f0c08c57fc 100644 --- a/src/Metadata/RequiresPhpunit.php +++ b/src/Metadata/RequiresPhpunit.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\Metadata; -use SebastianBergmann\VersionRequirement\Requirement; +use PHPUnit\Metadata\Version\Requirement; /** * @immutable diff --git a/src/Metadata/Version/ComparisonRequirement.php b/src/Metadata/Version/ComparisonRequirement.php new file mode 100644 index 0000000000..936080b840 --- /dev/null +++ b/src/Metadata/Version/ComparisonRequirement.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Version; + +use PHPUnit\Util\VersionComparisonOperator; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class ComparisonRequirement extends Requirement +{ + private string $version; + private VersionComparisonOperator $operator; + + public function __construct(string $version, VersionComparisonOperator $operator) + { + $this->version = $version; + $this->operator = $operator; + } + + public function asString(): string + { + return $this->operator->asString() . ' ' . $this->version; + } + + public function version(): string + { + return $this->version; + } +} diff --git a/src/Metadata/Version/ConstraintRequirement.php b/src/Metadata/Version/ConstraintRequirement.php new file mode 100644 index 0000000000..0ba6f50b97 --- /dev/null +++ b/src/Metadata/Version/ConstraintRequirement.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Version; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class ConstraintRequirement extends Requirement +{ + private string $constraint; + + public function __construct(string $constraint) + { + $this->constraint = $constraint; + } + + public function asString(): string + { + return $this->constraint; + } +} diff --git a/src/Metadata/Version/InvalidVersionRequirement.php b/src/Metadata/Version/InvalidVersionRequirement.php new file mode 100644 index 0000000000..deb04fcb54 --- /dev/null +++ b/src/Metadata/Version/InvalidVersionRequirement.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Version; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class InvalidVersionRequirement extends Requirement +{ + /** + * @var non-empty-string + */ + private string $message; + + /** + * @param non-empty-string $message + */ + public function __construct(string $message) + { + $this->message = $message; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + return $this->message; + } +} diff --git a/src/Metadata/Version/Requirement.php b/src/Metadata/Version/Requirement.php new file mode 100644 index 0000000000..321b8b5a18 --- /dev/null +++ b/src/Metadata/Version/Requirement.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Version; + +use function assert; +use function explode; +use PHPUnit\Metadata\InvalidVersionRequirementException; +use PHPUnit\Util\VersionComparisonOperator; +use SebastianBergmann\VersionRequirement\ComparisonRequirement as ComparisonRequirementImplementation; +use SebastianBergmann\VersionRequirement\ConstraintRequirement as ConstraintRequirementImplementation; +use SebastianBergmann\VersionRequirement\Exception; +use SebastianBergmann\VersionRequirement\Requirement as RequirementImplementation; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract readonly class Requirement +{ + /** + * @param non-empty-string $versionRequirement + * + * @throws InvalidVersionRequirementException + */ + public static function from(string $versionRequirement): self + { + try { + $requirement = RequirementImplementation::from($versionRequirement); + } catch (Exception) { + throw new InvalidVersionRequirementException; + } + + if ($requirement instanceof ComparisonRequirementImplementation) { + return new ComparisonRequirement( + $requirement->version(), + new VersionComparisonOperator( + explode(' ', $requirement->asString(), 2)[0], + ), + ); + } + + assert($requirement instanceof ConstraintRequirementImplementation); + + return new ConstraintRequirement($requirement->asString()); + } + + abstract public function asString(): string; +} diff --git a/src/TextUI/Configuration/Registry.php b/src/TextUI/Configuration/Registry.php index de07aec4d4..f29bc80f72 100644 --- a/src/TextUI/Configuration/Registry.php +++ b/src/TextUI/Configuration/Registry.php @@ -18,7 +18,7 @@ use PHPUnit\TextUI\CliArguments\Configuration as CliConfiguration; use PHPUnit\TextUI\CliArguments\Exception; use PHPUnit\TextUI\XmlConfiguration\Configuration as XmlConfiguration; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; +use PHPUnit\Util\VersionComparisonOperator; /** * CLI options and XML configuration are static within a single PHPUnit process. diff --git a/src/TextUI/Configuration/Value/TestDirectory.php b/src/TextUI/Configuration/Value/TestDirectory.php index d037d212aa..2997fd7b47 100644 --- a/src/TextUI/Configuration/Value/TestDirectory.php +++ b/src/TextUI/Configuration/Value/TestDirectory.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TextUI\Configuration; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; +use PHPUnit\Util\VersionComparisonOperator; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit diff --git a/src/TextUI/Configuration/Value/TestFile.php b/src/TextUI/Configuration/Value/TestFile.php index 7b9f548a70..e658ff8843 100644 --- a/src/TextUI/Configuration/Value/TestFile.php +++ b/src/TextUI/Configuration/Value/TestFile.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TextUI\Configuration; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; +use PHPUnit\Util\VersionComparisonOperator; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit diff --git a/src/TextUI/Configuration/Xml/Loader.php b/src/TextUI/Configuration/Xml/Loader.php index 892396b491..0b4f464d19 100644 --- a/src/TextUI/Configuration/Xml/Loader.php +++ b/src/TextUI/Configuration/Xml/Loader.php @@ -77,11 +77,11 @@ use PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity; use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; +use PHPUnit\Util\VersionComparisonOperator; use PHPUnit\Util\Xml\Loader as XmlLoader; use PHPUnit\Util\Xml\XmlException; use SebastianBergmann\CodeCoverage\Report\Html\Colors; use SebastianBergmann\CodeCoverage\Report\Thresholds; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; use Throwable; /** diff --git a/src/Util/Exception/InvalidVersionOperatorException.php b/src/Util/Exception/InvalidVersionOperatorException.php new file mode 100644 index 0000000000..bc2fe9a0ed --- /dev/null +++ b/src/Util/Exception/InvalidVersionOperatorException.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function sprintf; +use RuntimeException; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidVersionOperatorException extends RuntimeException implements Exception +{ + public function __construct(string $operator) + { + parent::__construct( + sprintf( + '"%s" is not a valid version_compare() operator', + $operator, + ), + ); + } +} diff --git a/src/Util/VersionComparisonOperator.php b/src/Util/VersionComparisonOperator.php new file mode 100644 index 0000000000..0b0d7befcb --- /dev/null +++ b/src/Util/VersionComparisonOperator.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use SebastianBergmann\VersionRequirement\InvalidVersionOperatorException as InvalidVersionOperatorExceptionImplementation; +use SebastianBergmann\VersionRequirement\VersionComparisonOperator as VersionComparisonOperatorImplementation; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @immutable + */ +final readonly class VersionComparisonOperator +{ + /** + * @var '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' + */ + private string $operator; + + /** + * @throws InvalidVersionOperatorException + */ + public function __construct(string $operator) + { + try { + $this->operator = new VersionComparisonOperatorImplementation($operator)->asString(); + } catch (InvalidVersionOperatorExceptionImplementation) { + throw new InvalidVersionOperatorException($operator); + } + } + + /** + * @return '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' + */ + public function asString(): string + { + return $this->operator; + } +} diff --git a/tests/unit/Metadata/MetadataCollectionTest.php b/tests/unit/Metadata/MetadataCollectionTest.php index e6ade59213..0df5fdaa91 100644 --- a/tests/unit/Metadata/MetadataCollectionTest.php +++ b/tests/unit/Metadata/MetadataCollectionTest.php @@ -14,8 +14,7 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; -use SebastianBergmann\VersionRequirement\ComparisonRequirement; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; +use PHPUnit\Metadata\Version\Requirement; use stdClass; #[CoversClass(MetadataCollection::class)] @@ -640,16 +639,10 @@ private function collectionWithOneOfEach(): MetadataCollection Metadata::requiresOperatingSystemOnClass(''), Metadata::requiresPhpExtensionOnClass('', null), Metadata::requiresPhpOnClass( - new ComparisonRequirement( - '8.0.0', - new VersionComparisonOperator('>='), - ), + Requirement::from('>= 8.0.0'), ), Metadata::requiresPhpunitOnClass( - new ComparisonRequirement( - '10.0.0', - new VersionComparisonOperator('>='), - ), + Requirement::from('>= 10.0.0'), ), Metadata::requiresPhpunitExtensionOnClass(stdClass::class), Metadata::requiresEnvironmentVariableOnClass('foo', 'bar'), diff --git a/tests/unit/Metadata/MetadataTest.php b/tests/unit/Metadata/MetadataTest.php index f6e81c4d29..28e6878b2e 100644 --- a/tests/unit/Metadata/MetadataTest.php +++ b/tests/unit/Metadata/MetadataTest.php @@ -14,9 +14,8 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; +use PHPUnit\Metadata\Version\Requirement; use PHPUnit\TestFixture\Metadata\Attribute\ExampleTrait; -use SebastianBergmann\VersionRequirement\ComparisonRequirement; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; #[CoversClass(Metadata::class)] #[CoversClassesThatExtendClass(Metadata::class)] @@ -3662,10 +3661,7 @@ public function testCanBeRequiresOperatingSystemFamilyOnMethod(): void public function testCanBeRequiresPhpOnClass(): void { $metadata = Metadata::requiresPhpOnClass( - new ComparisonRequirement( - '8.0.0', - new VersionComparisonOperator('>='), - ), + Requirement::from('>= 8.0.0'), ); $this->assertFalse($metadata->isAfter()); @@ -3736,10 +3732,7 @@ public function testCanBeRequiresPhpOnClass(): void public function testCanBeRequiresPhpOnMethod(): void { $metadata = Metadata::requiresPhpOnMethod( - new ComparisonRequirement( - '8.0.0', - new VersionComparisonOperator('>='), - ), + Requirement::from('>= 8.0.0'), ); $this->assertFalse($metadata->isAfter()); @@ -3884,10 +3877,7 @@ public function testCanBeRequiresPhpExtensionWithVersionOnClass(): void { $metadata = Metadata::requiresPhpExtensionOnClass( 'test', - new ComparisonRequirement( - '1.0.0', - new VersionComparisonOperator('>='), - ), + Requirement::from('>= 1.0.0'), ); $this->assertFalse($metadata->isAfter()); @@ -4034,10 +4024,7 @@ public function testCanBeRequiresPhpExtensionWithVersionOnMethod(): void { $metadata = Metadata::requiresPhpExtensionOnMethod( 'test', - new ComparisonRequirement( - '1.0.0', - new VersionComparisonOperator('>='), - ), + Requirement::from('>= 1.0.0'), ); $this->assertFalse($metadata->isAfter()); @@ -4110,10 +4097,7 @@ public function testCanBeRequiresPhpExtensionWithVersionOnMethod(): void public function testCanBeRequiresPhpunitOnClass(): void { $metadata = Metadata::requiresPhpunitOnClass( - new ComparisonRequirement( - '10.0.0', - new VersionComparisonOperator('>='), - ), + Requirement::from('>= 10.0.0'), ); $this->assertFalse($metadata->isAfter()); @@ -4253,10 +4237,7 @@ public function testCanBeRequiresPhpunitExtensionOnClass(): void public function testCanBeRequiresPhpunitOnMethod(): void { $metadata = Metadata::requiresPhpunitOnMethod( - new ComparisonRequirement( - '10.0.0', - new VersionComparisonOperator('>='), - ), + Requirement::from('>= 10.0.0'), ); $this->assertFalse($metadata->isAfter()); diff --git a/tests/unit/Metadata/Parser/AttributeParserTestCase.php b/tests/unit/Metadata/Parser/AttributeParserTestCase.php index 47265d53c3..d6bbeacd24 100644 --- a/tests/unit/Metadata/Parser/AttributeParserTestCase.php +++ b/tests/unit/Metadata/Parser/AttributeParserTestCase.php @@ -23,6 +23,8 @@ use PHPUnit\Metadata\RequiresPhpunitExtension; use PHPUnit\Metadata\RequiresSetting; use PHPUnit\Metadata\Retry; +use PHPUnit\Metadata\Version\ComparisonRequirement; +use PHPUnit\Metadata\Version\ConstraintRequirement; use PHPUnit\Metadata\WithEnvironmentVariable; use PHPUnit\TestFixture\Metadata\Attribute\AllowMockObjectsWithoutExpectationsOnClassTest; use PHPUnit\TestFixture\Metadata\Attribute\AllowMockObjectsWithoutExpectationsOnMethodTest; @@ -70,8 +72,6 @@ use PHPUnit\TestFixture\Metadata\Attribute\UsesTest; use PHPUnit\TestFixture\Metadata\Attribute\WithEnvironmentVariableTest; use PHPUnit\TestFixture\Metadata\Attribute\WithoutErrorHandlerTest; -use SebastianBergmann\VersionRequirement\ComparisonRequirement; -use SebastianBergmann\VersionRequirement\ConstraintRequirement; abstract class AttributeParserTestCase extends TestCase { diff --git a/tests/unit/Metadata/Version/InvalidVersionRequirementTest.php b/tests/unit/Metadata/Version/InvalidVersionRequirementTest.php new file mode 100644 index 0000000000..8b1742b19b --- /dev/null +++ b/tests/unit/Metadata/Version/InvalidVersionRequirementTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Version; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(InvalidVersionRequirement::class)] +#[Small] +#[Group('metadata')] +final class InvalidVersionRequirementTest extends TestCase +{ + public function testCanBeRepresentedAsString(): void + { + $requirement = new InvalidVersionRequirement('message'); + + $this->assertSame('message', $requirement->asString()); + } +} diff --git a/tests/unit/Metadata/Version/RequirementTest.php b/tests/unit/Metadata/Version/RequirementTest.php new file mode 100644 index 0000000000..6eb4c378e2 --- /dev/null +++ b/tests/unit/Metadata/Version/RequirementTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; +use PHPUnit\Metadata\Version\ComparisonRequirement; +use PHPUnit\Metadata\Version\ConstraintRequirement; +use PHPUnit\Metadata\Version\Requirement; +use PHPUnit\Util\VersionComparisonOperator; + +#[CoversClass(ComparisonRequirement::class)] +#[CoversClass(ConstraintRequirement::class)] +#[CoversClass(Requirement::class)] +#[UsesClass(VersionComparisonOperator::class)] +#[Small] +#[Group('metadata')] +final class RequirementTest extends TestCase +{ + public function testCanBeCreatedFromStringWithVersionConstraint(): void + { + $requirement = Requirement::from('^1.0'); + + $this->assertInstanceOf(ConstraintRequirement::class, $requirement); + $this->assertSame('^1.0', $requirement->asString()); + } + + public function testCanBeCreatedFromStringWithSimpleComparison(): void + { + $requirement = Requirement::from('>= 1.0.0'); + + $this->assertInstanceOf(ComparisonRequirement::class, $requirement); + $this->assertSame('>= 1.0.0', $requirement->asString()); + $this->assertSame('1.0.0', $requirement->version()); + } + + public function testCannotBeCreatedFromInvalidString(): void + { + $this->expectException(InvalidVersionRequirementException::class); + + Requirement::from('invalid'); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php b/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php index a0f205dfa5..928b67dd85 100644 --- a/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php +++ b/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; +use PHPUnit\Util\VersionComparisonOperator; #[CoversClass(TestDirectoryCollection::class)] #[CoversClass(TestDirectoryCollectionIterator::class)] diff --git a/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php b/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php index c9437b5829..e1ebcbfa84 100644 --- a/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php +++ b/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; +use PHPUnit\Util\VersionComparisonOperator; #[CoversClass(TestDirectory::class)] #[Small] diff --git a/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php b/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php index 2dda3e971a..43cd4c56bd 100644 --- a/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php +++ b/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; +use PHPUnit\Util\VersionComparisonOperator; #[CoversClass(TestFileCollection::class)] #[CoversClass(TestFileCollectionIterator::class)] diff --git a/tests/unit/TextUI/Configuration/Value/TestFileTest.php b/tests/unit/TextUI/Configuration/Value/TestFileTest.php index 1cf5523bfb..31db4d9c7c 100644 --- a/tests/unit/TextUI/Configuration/Value/TestFileTest.php +++ b/tests/unit/TextUI/Configuration/Value/TestFileTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; -use SebastianBergmann\VersionRequirement\VersionComparisonOperator; +use PHPUnit\Util\VersionComparisonOperator; #[CoversClass(TestFile::class)] #[Small] diff --git a/tests/unit/Util/VersionComparisonOperatorTest.php b/tests/unit/Util/VersionComparisonOperatorTest.php new file mode 100644 index 0000000000..39745275a9 --- /dev/null +++ b/tests/unit/Util/VersionComparisonOperatorTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(VersionComparisonOperator::class)] +#[CoversClass(InvalidVersionOperatorException::class)] +#[Small] +final class VersionComparisonOperatorTest extends TestCase +{ + /** + * @return non-empty-list> + */ + public static function validValues(): array + { + return [ + ['<'], + ['lt'], + ['<='], + ['le'], + ['>'], + ['gt'], + ['>='], + ['ge'], + ['=='], + ['='], + ['eq'], + ['!='], + ['<>'], + ['ne'], + ]; + } + + #[DataProvider('validValues')] + #[TestDox('Can be created from "$string"')] + public function testCanBeCreatedFromValidString(string $string): void + { + $this->assertSame($string, new VersionComparisonOperator($string)->asString()); + } + + public function testCannotBeCreatedFromInvalidString(): void + { + $this->expectException(InvalidVersionOperatorException::class); + + new VersionComparisonOperator(''); + } +}