diff --git a/src/Optional.php b/src/Optional.php index c4602f5..b91e5bd 100644 --- a/src/Optional.php +++ b/src/Optional.php @@ -153,11 +153,32 @@ public function orElseGet(callable $otherSupplier): mixed return static::isSupported($other) ? $other : throw new InvalidArgumentException('Other supplier must return supported other.'); } - public function orElseThrow(?callable $exceptionSupplier = null): mixed + /** + * @template E of Throwable + * + * @param null|class-string|callable(): E $exceptionSupplier + * + * @return T + * + * @throws E + */ + public function orElseThrow(null|string|callable $exceptionSupplier = null): mixed { + if ($exceptionSupplier === null) { + return $this->orElseThrow(static fn () => new Exception\CouldNotGetValueOfEmptyOptional()); + } + + if (is_string($exceptionSupplier)) { + /** @var class-string $exceptionSupplier */ + if (!class_exists($exceptionSupplier, autoload: true)) { + throw new InvalidArgumentException('Exception supplier must be existing class name.'); + } + return $this->orElseThrow(static fn () => new $exceptionSupplier()); + } + return $this->orElseGet(static function () use ($exceptionSupplier): never { /** @var Throwable|mixed $exception */ - $exception = $exceptionSupplier === null ? new Exception\CouldNotGetValueOfEmptyOptional() : $exceptionSupplier(); + $exception = $exceptionSupplier(); if ($exception instanceof Throwable) { throw $exception; } diff --git a/tests/OptionalTest.php b/tests/OptionalTest.php index 1608fe2..d87bd7a 100644 --- a/tests/OptionalTest.php +++ b/tests/OptionalTest.php @@ -4,7 +4,7 @@ namespace PetrKnap\Optional; -use Exception as SomeException; +use DomainException as SomeException; use LogicException; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -213,20 +213,34 @@ public static function dataMethodOrElseGetWorks(): array } #[DataProvider('dataMethodOrElseThrowWorks')] - public function testMethodOrElseThrowWorks(Optional $optional, ?string $expectedValue, ?string $expectedException): void + public function testMethodOrElseThrowWorks(Optional $optional, null|string|callable $exceptionProvider, ?string $expectedValue, ?string $expectedException): void { if ($expectedException) { self::expectException($expectedException); } - self::assertSame($expectedValue, $optional->orElseThrow(static fn(): SomeException => new SomeException())); + self::assertSame($expectedValue, $optional->orElseThrow($exceptionProvider)); } - public static function dataMethodOrElseThrowWorks(): array + public static function dataMethodOrElseThrowWorks(): iterable { - return self::makeDataSet([ - [self::VALUE, null], - [null, SomeException::class], + $dataSet = self::makeDataSet([ + [null, self::VALUE, null], + [null, null, SomeException::class], ]); + foreach ($dataSet as $name => $data) { + $data[3] = $data[3] === null ? null : Exception\CouldNotGetValueOfEmptyOptional::class; + yield "{$name} + null supplier" => $data; + } + $exceptionSupplier = static fn(): SomeException => new SomeException(); + foreach ($dataSet as $name => $data) { + $data[1] = $exceptionSupplier; + yield "{$name} + callable supplier" => $data; + } + $exceptionSupplier = SomeException::class; + foreach ($dataSet as $name => $data) { + $data[1] = $exceptionSupplier; + yield "{$name} + string supplier" => $data; + } } private static function makeDataSet(array $args): array