From 8dab5300c29c955f665a2ab5782b0a8d0ed9ff37 Mon Sep 17 00:00:00 2001 From: Petr Knap <8299754+petrknap@users.noreply.github.com> Date: Sun, 12 May 2024 10:23:30 +0200 Subject: [PATCH] refactor: refactored `equals` method to have behavior of Java version and opened `Optional` --- src/Optional.php | 47 +++++++++++++++++++++++++----------------- tests/OptionalTest.php | 26 +++++++++++++++-------- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/Optional.php b/src/Optional.php index f33cb48..3fb9735 100644 --- a/src/Optional.php +++ b/src/Optional.php @@ -12,7 +12,7 @@ * * @see https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html */ -final class Optional +class Optional { private bool|null $wasPresent = null; @@ -21,45 +21,41 @@ final class Optional * * @param T|null $value */ - public function __construct( - private readonly mixed $value, + final public function __construct( + protected readonly mixed $value, ) { + if ($this->value !== null && !static::isSupported($this->value)) { + throw new InvalidArgumentException('Value is not supported.'); + } } - /** - * @return self - */ - public static function empty(): self + public static function empty(): static { - return self::ofNullable(null); + return new static(null); } /** * @param T $value - * - * @return self */ - public static function of(mixed $value): self + public static function of(mixed $value): static { - return $value !== null ? self::ofNullable($value) : throw new InvalidArgumentException('Value must not be null.'); + return $value !== null ? new static($value) : throw new InvalidArgumentException('Value must not be null.'); } /** * @param T|null $value - * - * @return self */ - public static function ofNullable(mixed $value): self + public static function ofNullable(mixed $value): static { - return new self($value); + return new static($value); } public function equals(mixed $obj): bool { - if ($obj instanceof Optional) { + if ($obj instanceof static) { $obj = $obj->isPresent() ? $obj->get() : null; } - return $this->value === $obj; + return ($obj === null || static::isSupported($obj)) && $this->value == $obj; } /** @@ -113,7 +109,8 @@ public function orElseGet(callable $otherSupplier): mixed if ($this->value !== null) { return $this->value; } - return $otherSupplier() ?? throw new InvalidArgumentException('Other supplier must return value.'); + $other = $otherSupplier(); + return static::isSupported($other) ? $other : throw new InvalidArgumentException('Other supplier must return supported other.'); } /** @@ -136,4 +133,16 @@ public function orElseThrow(callable $exceptionSupplier): mixed throw new InvalidArgumentException('Exception supplier must return ' . Throwable::class . '.'); }); } + + /** + * @param T|mixed $value not null + */ + protected static function isSupported(mixed $value): bool + { + trigger_error( + static::class . ' does not check the type of value.', + error_level: E_USER_NOTICE, + ); + return $value !== null; + } } diff --git a/tests/OptionalTest.php b/tests/OptionalTest.php index 5d4d35d..1ad9a96 100644 --- a/tests/OptionalTest.php +++ b/tests/OptionalTest.php @@ -55,16 +55,24 @@ public function testMethodEqualsWorks(Optional $optional, mixed $obj, bool $expe public static function dataMethodEqualsWorks(): array { - $optionalValue = Optional::of(self::VALUE); - $optionalEmpty = Optional::empty(); + $object1 = new \stdClass(); + $object1->property = self::VALUE; + $object2 = new \stdClass(); + $object2->property = self::VALUE; + $object3 = new \stdClass(); + $object3->property = self::OTHER; return [ - 'equal (value)' => [$optionalValue, self::VALUE, true], - 'equal (optional)' => [$optionalValue, Optional::of(self::VALUE), true], - 'equal (empty)' => [$optionalEmpty, Optional::empty(), true], - 'not equal (value)' => [$optionalValue, self::OTHER, false], - 'not equal (optional)' => [$optionalValue, Optional::of(self::OTHER), false], - 'not equal (empty-present)' => [$optionalEmpty, $optionalValue, false], - 'not equal (present-empty)' => [$optionalValue, $optionalEmpty, false], + 'equal (value)' => [Optional::of(self::VALUE), self::VALUE, true], + 'equal (optional)' => [Optional::of(self::VALUE), Optional::of(self::VALUE), true], + 'equal (object)' => [Optional::of($object1), $object2, true], + 'equal (optional)' => [Optional::of($object1), Optional::of($object2), true], + 'equal (empty)' => [Optional::empty(), Optional::empty(), true], + 'not equal (value)' => [Optional::of(self::VALUE), self::OTHER, false], + 'not equal (optional)' => [Optional::of(self::VALUE), Optional::of(self::OTHER), false], + 'not equal (object)' => [Optional::of($object1), $object3, false], + 'not equal (optional)' => [Optional::of($object1), Optional::of($object3), false], + 'not equal (empty-present)' => [Optional::empty(), Optional::of(self::VALUE), false], + 'not equal (present-empty)' => [Optional::of(self::VALUE), Optional::empty(), false], ]; }