From 421ce5bbc398e70905d742d75abcfbc8cd9fab24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=B6ller?= Date: Sun, 24 Dec 2023 16:29:17 +0100 Subject: [PATCH] Enhancement: Implement Version --- .github/workflows/integrate.yaml | 2 +- CHANGELOG.md | 8 + Makefile | 2 +- README.md | 29 ++- psalm-baseline.xml | 9 +- .../InvalidVersion.php} | 18 +- src/Version.php | 49 +++++ test/Unit/ExampleTest.php | 33 ---- test/Unit/Exception/InvalidVersionTest.php | 38 ++++ test/Unit/VersionTest.php | 171 ++++++++++++++++++ 10 files changed, 310 insertions(+), 49 deletions(-) rename src/{Example.php => Exception/InvalidVersion.php} (58%) create mode 100644 src/Version.php delete mode 100644 test/Unit/ExampleTest.php create mode 100644 test/Unit/Exception/InvalidVersionTest.php create mode 100644 test/Unit/VersionTest.php diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index d708c589..5730c68f 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -59,7 +59,7 @@ jobs: dependencies: "${{ matrix.dependencies }}" - name: "Run backward-compatibility analysis with roave/backward-compatibility-check" - run: "vendor/bin/roave-backward-compatibility-check --ansi --format=github-actions --from=64ced12" + run: "vendor/bin/roave-backward-compatibility-check --ansi --format=github-actions --from=6e3389d" code-coverage: name: "Code Coverage" diff --git a/CHANGELOG.md b/CHANGELOG.md index 102c8d7a..3c8c65f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,4 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), For a full diff see [`64ced12...main`][64ced12...main]. +### Added + +- Added `Version` as a value object ([#1]), by [@localheinz] + [64ced12...main]: https://github.com/ergebnis/version/compare/64ced12...main + +[#1]: https://github.com/ergebnis/version/pull/1 + +[@localheinz]: https://github.com/localheinz diff --git a/Makefile b/Makefile index f760fb99..c59444bb 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ it: refactoring coding-standards security-analysis static-code-analysis tests ## .PHONY: backward-compatibility-analysis backward-compatibility-analysis: vendor ## Runs a backward-compatibility analysis with roave/backward-compatibility-check - vendor/bin/roave-backward-compatibility-check --from=64ced12 + vendor/bin/roave-backward-compatibility-check --from=6e3389d .PHONY: code-coverage code-coverage: vendor ## Collects coverage from running unit tests with phpunit/phpunit diff --git a/README.md b/README.md index c435ac53..ce4a59d8 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,34 @@ composer require ergebnis/version ## Usage -💡 This is a great place for showing a few usage examples! +### Create a `Version` from a `string` + +```php +equals($two); // true +$one->equals($three); // false +``` ## Changelog diff --git a/psalm-baseline.xml b/psalm-baseline.xml index eb79571d..cc319aba 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,2 +1,9 @@ - + + + + provideInvalidValue + provideValidValue + + + diff --git a/src/Example.php b/src/Exception/InvalidVersion.php similarity index 58% rename from src/Example.php rename to src/Exception/InvalidVersion.php index 9c9aca62..80806c10 100644 --- a/src/Example.php +++ b/src/Exception/InvalidVersion.php @@ -11,21 +11,15 @@ * @see https://github.com/ergebnis/version */ -namespace Ergebnis\Version; +namespace Ergebnis\Version\Exception; -final class Example +final class InvalidVersion extends \InvalidArgumentException { - private function __construct(private readonly string $value) - { - } - public static function fromString(string $value): self { - return new self($value); - } - - public function toString(): string - { - return $this->value; + return new self(\sprintf( + 'Value "%s" does not appear to be valid.', + $value, + )); } } diff --git a/src/Version.php b/src/Version.php new file mode 100644 index 00000000..b5ee5418 --- /dev/null +++ b/src/Version.php @@ -0,0 +1,49 @@ +0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/'; + + private function __construct(private readonly string $value) + { + } + + /** + * @throws Exception\InvalidVersion + */ + public static function fromString(string $value): self + { + if (1 !== \preg_match(self::REGEX, $value)) { + throw Exception\InvalidVersion::fromString($value); + } + + return new self($value); + } + + public function toString(): string + { + return $this->value; + } + + public function equals(self $other): bool + { + return $this->value === $other->value; + } +} diff --git a/test/Unit/ExampleTest.php b/test/Unit/ExampleTest.php deleted file mode 100644 index bf498abe..00000000 --- a/test/Unit/ExampleTest.php +++ /dev/null @@ -1,33 +0,0 @@ -sentence(); - - $example = Example::fromString($value); - - self::assertSame($value, $example->toString()); - } -} diff --git a/test/Unit/Exception/InvalidVersionTest.php b/test/Unit/Exception/InvalidVersionTest.php new file mode 100644 index 00000000..40ed5b23 --- /dev/null +++ b/test/Unit/Exception/InvalidVersionTest.php @@ -0,0 +1,38 @@ +word(); + + $exception = Exception\InvalidVersion::fromString($value); + + $message = \sprintf( + 'Value "%s" does not appear to be valid.', + $value, + ); + + self::assertSame($message, $exception->getMessage()); + } +} diff --git a/test/Unit/VersionTest.php b/test/Unit/VersionTest.php new file mode 100644 index 00000000..f1467d3e --- /dev/null +++ b/test/Unit/VersionTest.php @@ -0,0 +1,171 @@ +expectException(Exception\InvalidVersion::class); + + Version::fromString($value); + } + + /** + * @see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + * @see https://regex101.com/r/Ly7O1x/3/ + * + * @return \Generator + */ + public static function provideInvalidValue(): \Generator + { + $values = [ + '1', + '1.2', + '1.2.3-0123', + '1.2.3-0123.0123', + '1.1.2+.123', + '+invalid', + '-invalid', + '-invalid+invalid', + '-invalid.01', + 'alpha', + 'alpha.beta', + 'alpha.beta.1', + 'alpha.1', + 'alpha+beta', + 'alpha_beta', + 'alpha.', + 'alpha..', + 'beta', + '1.0.0-alpha_beta', + '-alpha.', + '1.0.0-alpha..', + '1.0.0-alpha..1', + '1.0.0-alpha...1', + '1.0.0-alpha....1', + '1.0.0-alpha.....1', + '1.0.0-alpha......1', + '1.0.0-alpha.......1', + '01.1.1', + '1.01.1', + '1.1.01', + '1.2', + '1.2.3.DEV', + '1.2-SNAPSHOT', + '1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788', + '1.2-RC-SNAPSHOT', + '-1.0.3-gamma+b7718', + '+justmeta', + '9.8.7+meta+meta', + '9.8.7-whatever+meta+meta', + '99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12', + ]; + + foreach ($values as $value) { + yield $value => [ + $value, + ]; + } + } + + #[Framework\Attributes\DataProvider('provideValidValue')] + public function testFromStringReturnsVersion(string $value): void + { + $version = Version::fromString($value); + + self::assertSame($value, $version->toString()); + } + + /** + * @see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + * @see https://regex101.com/r/Ly7O1x/3/ + * + * @return \Generator + */ + public static function provideValidValue(): \Generator + { + $values = [ + '0.0.4', + '1.2.3', + '10.20.30', + '1.1.2-prerelease+meta', + '1.1.2+meta', + '1.1.2+meta-valid', + '1.0.0-alpha', + '1.0.0-beta', + '1.0.0-alpha.beta', + '1.0.0-alpha.beta.1', + '1.0.0-alpha.1', + '1.0.0-alpha0.valid', + '1.0.0-alpha.0valid', + '1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay', + '1.0.0-rc.1+build.1', + '2.0.0-rc.1+build.123', + '1.2.3-beta', + '10.2.3-DEV-SNAPSHOT', + '1.2.3-SNAPSHOT-123', + '1.0.0', + '2.0.0', + '1.1.7', + '2.0.0+build.1848', + '2.0.1-alpha.1227', + '1.0.0-alpha+beta', + '1.2.3----RC-SNAPSHOT.12.9.1--.12+788', + '1.2.3----R-S.12.9.1--.12+meta', + '1.2.3----RC-SNAPSHOT.12.9.1--.12', + '1.0.0+0.build.1-rc.10000aaa-kk-0.1', + '99999999999999999999999.999999999999999999.99999999999999999', + '1.0.0-0A.is.legal', + ]; + + foreach ($values as $value) { + yield $value => [ + $value, + ]; + } + } + + public function testEqualsReturnsFalseWhenValuesAreDifferent(): void + { + $faker = self::faker()->unique(); + + $one = Version::fromString($faker->regexify('(0|[1-9]+)\.(0|[1-9]+)\.(0|[1-9]+)')); + $two = Version::fromString($faker->regexify('(0|[1-9]+)\.(0|[1-9]+)\.(0|[1-9]+)')); + + self::assertFalse($one->equals($two)); + } + + public function testEqualsReturnsTrueWhenValueIsSame(): void + { + $value = self::faker()->regexify('(0|[1-9]+)\.(0|[1-9]+)\.(0|[1-9]+)'); + + $one = Version::fromString($value); + $two = Version::fromString($value); + + self::assertTrue($one->equals($two)); + } +}