From 1ce306e45a275a36c4adf46487a3b7dad6b21141 Mon Sep 17 00:00:00 2001 From: Petr Knap <8299754+petrknap@users.noreply.github.com> Date: Fri, 10 May 2024 22:47:35 +0200 Subject: [PATCH] feat: implemented simple version of Optional --- README.md | 18 +++++++++- composer.json | 8 ++--- src/Exception/NoSuchElement.php | 11 ++++++ src/Exception/OptionalException.php | 11 ++++++ src/Optional.php | 53 +++++++++++++++++++++++++++++ tests/OptionalTest.php | 38 +++++++++++++++++++++ tests/ReadmeTest.php | 26 ++++++++++++++ 7 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 src/Exception/NoSuchElement.php create mode 100644 src/Exception/OptionalException.php create mode 100644 src/Optional.php create mode 100644 tests/OptionalTest.php create mode 100644 tests/ReadmeTest.php diff --git a/README.md b/README.md index 4e436bf..71f5839 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,20 @@ -# php-optional +# Optional (as in Java Platform SE 8 but in PHP) + +> A container object which may or may not contain a non-null value. If a value is present, `isPresent()` will return `true` and `get()` will return the value. +> +> -- +> [Optional (Java Platform SE 8)](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) + +It is an easy way to make sure that everyone has to check if they have (not) received a `null`. + +```php +namespace PetrKnap\Optional; + +/** @var Optinal $optionalString */ +$optionalString = new Optional('value'); + +echo $optionalString->isPresent() ? $optionalString->get() : 'EMPTY'; +``` --- diff --git a/composer.json b/composer.json index 1e5e1fc..08abeb3 100755 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "allow-plugins": false, "sort-packages": true }, - "description": "php-optional", + "description": "Optional (as in Java Platform SE 8 but in PHP)", "funding": [ { "type": "other", @@ -32,13 +32,13 @@ }, "require-dev": { "nunomaduro/phpinsights": "^2.11", + "petrknap/shorts": "^2.1", "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", "squizlabs/php_codesniffer": "^3.7" }, "scripts": { - "test": [ - "echo TODO" - ], + "test": "phpunit --colors=always --testdox tests", "check-requirements": [ "composer outdated \"petrknap/*\" --major-only --strict --ansi --no-interaction" ], diff --git a/src/Exception/NoSuchElement.php b/src/Exception/NoSuchElement.php new file mode 100644 index 0000000..58c5e02 --- /dev/null +++ b/src/Exception/NoSuchElement.php @@ -0,0 +1,11 @@ + + */ + public static function empty(): self + { + return new self(null); + } + + public function isPresent(): bool + { + return $this->wasPresent = $this->value !== null; + } + + /** + * @return T + * + * @throws Exception\NoSuchElement + */ + public function get(): mixed + { + /** @var T */ + return match ($this->wasPresent) { + true => $this->value, + false => throw new Exception\NoSuchElement(), + null => throw new LogicException('Call `isPresent()` before accessing the value.'), + }; + } +} diff --git a/tests/OptionalTest.php b/tests/OptionalTest.php new file mode 100644 index 0000000..757f529 --- /dev/null +++ b/tests/OptionalTest.php @@ -0,0 +1,38 @@ +isPresent()); + self::assertSame(self::VALUE, $optional->get()); + } + + public function testGetThrowsWhenValueIsNotPresent(): void + { + $optional = Optional::empty(); + + self::assertFalse($optional->isPresent()); + self::expectException(Exception\NoSuchElement::class); + $optional->get(); + } + + public function testGetThrowsWhenCalledSeparately(): void + { + $optional = new Optional(self::VALUE); + + self::expectException(LogicException::class); + $optional->get(); + } +} diff --git a/tests/ReadmeTest.php b/tests/ReadmeTest.php new file mode 100644 index 0000000..5cbe715 --- /dev/null +++ b/tests/ReadmeTest.php @@ -0,0 +1,26 @@ + get()' => 'value', + ]; + } +}