Skip to content

Commit 823b97a

Browse files
committed
Allow specifying multiple exception classes in tryOf() and trap()
1 parent 901d0e4 commit 823b97a

File tree

4 files changed

+60
-13
lines changed

4 files changed

+60
-13
lines changed

src/functions/internal.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace TH\Maybe\Internal;
44

5+
use function TH\Maybe\Option\isOfAnyClass;
6+
57
/**
68
* Call $callback with $exception if it matches one of $exceptionClasses
79
* and return its value, or rethrow it otherwise.
@@ -10,9 +12,9 @@
1012
* @template T
1113
* @param E $error
1214
* @param callable(E): T $callback
13-
* @param class-string<E> $exceptionClasses
14-
* @throws \Throwable
15+
* @param class-string<E> ...$exceptionClasses
1516
* @return T
17+
* @throws \Throwable
1618
* @internal
1719
* @nodoc
1820
*/
@@ -21,10 +23,8 @@ function trap(
2123
callable $callback,
2224
string ...$exceptionClasses,
2325
): mixed {
24-
foreach ($exceptionClasses as $exceptionClass) {
25-
if (\is_a($error, $exceptionClass)) {
26-
return $callback($error);
27-
}
26+
if (isOfAnyClass($error, $exceptionClasses)) {
27+
return $callback($error);
2828
}
2929

3030
throw $error;

src/functions/option.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,20 @@ function of(callable $callback, mixed $noneValue = null, bool $strict = true): O
116116
* @template U
117117
* @template E of \Throwable
118118
* @param callable():U $callback
119-
* @param class-string<E> $exceptionClass
119+
* @param list<class-string<E>>|class-string<E> $exceptionClass
120120
* @return Option<U>
121121
* @throws \Throwable
122122
*/
123123
function tryOf(
124124
callable $callback,
125125
mixed $noneValue = null,
126126
bool $strict = true,
127-
string $exceptionClass = \Exception::class,
127+
array|string $exceptionClass = \Exception::class,
128128
): Option {
129129
try {
130130
return Option\of($callback, $noneValue, $strict);
131131
} catch (\Throwable $th) {
132-
if (\is_a($th, $exceptionClass)) {
132+
if (isOfAnyClass($th, (array) $exceptionClass)) {
133133
return Option\none();
134134
}
135135

@@ -214,3 +214,21 @@ function transpose(Option $option): Result
214214
static fn () => Result\ok(Option\none()),
215215
);
216216
}
217+
218+
/**
219+
* Check if the type of the given value is any of the passed classes.
220+
*
221+
* @param iterable<class-string> $classes
222+
*/
223+
function isOfAnyClass(
224+
object $value,
225+
iterable $classes,
226+
): bool {
227+
foreach ($classes as $class) {
228+
if (\is_a($value, $class)) {
229+
return true;
230+
}
231+
}
232+
233+
return false;
234+
}

src/functions/result.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use TH\Maybe\Option;
77
use TH\Maybe\Result;
88
use TH\Maybe\Tests\Helpers\IgnoreUnusedResults;
9+
use function TH\Maybe\Option\isOfAnyClass;
910

1011
/**
1112
* Return a `Result\Ok` Result containing `$value`.
@@ -78,18 +79,18 @@ function err(mixed $value): Result\Err
7879
* @template U
7980
* @template E of \Throwable
8081
* @param callable(mixed...):U $callback
81-
* @param class-string<E> $exceptionClass
82+
* @param list<class-string<E>>|class-string<E> $exceptionClass
8283
* @return Result<U,E>
8384
* @throws \Throwable
8485
*/
8586
#[ExamplesSetup(IgnoreUnusedResults::class)]
86-
function trap(callable $callback, string $exceptionClass = \Exception::class): Result
87+
function trap(callable $callback, array|string $exceptionClass = \Exception::class): Result
8788
{
8889
try {
8990
/** @var Result<U,E> */
9091
return Result\ok($callback());
9192
} catch (\Throwable $th) {
92-
if (\is_a($th, $exceptionClass)) {
93+
if (isOfAnyClass($th, (array) $exceptionClass)) {
9394
return Result\err($th);
9495
}
9596

tests/Unit/Option/OfTest.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public function testTryOfDefaultToStrict(): void
5757
Assert::assertEquals($o, Option\tryOf(static fn () => $o, (object)[])->unwrap());
5858
}
5959

60-
public function testTryOfExeptions(): void
60+
public function testTryOfExceptions(): void
6161
{
6262
// @phpstan-ignore-next-line
6363
Assert::assertEquals(Option\none(), Option\tryOf(static fn () => new \DateTimeImmutable("nope")));
@@ -69,4 +69,32 @@ public function testTryOfExeptions(): void
6969
} catch (\DivisionByZeroError) {
7070
}
7171
}
72+
73+
public function testTryOfExceptionsWithExpectedClass(): void
74+
{
75+
Assert::assertEquals(
76+
Option\none(),
77+
// @phpstan-ignore-next-line
78+
Option\tryOf(static fn () => 1 / 0, exceptionClass: \ArithmeticError::class),
79+
);
80+
Assert::assertEquals(
81+
Option\none(),
82+
// @phpstan-ignore-next-line
83+
Option\tryOf(static fn () => 1 / 0, exceptionClass: [\LogicException::class, \ArithmeticError::class]),
84+
);
85+
86+
try {
87+
// @phpstan-ignore-next-line
88+
Option\tryOf(static fn () => 1 / 0, exceptionClass: [\LogicException::class, \RuntimeException::class]);
89+
Assert::fail("An exception should have been thrown");
90+
} catch (\DivisionByZeroError) {
91+
}
92+
93+
try {
94+
// @phpstan-ignore-next-line
95+
Option\tryOf(static fn () => 1 / 0, exceptionClass: \LogicException::class);
96+
Assert::fail("An exception should have been thrown");
97+
} catch (\DivisionByZeroError) {
98+
}
99+
}
72100
}

0 commit comments

Comments
 (0)