From dba225586361c9aeae83e30dde43b4ca6d256e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Sun, 14 Mar 2021 12:38:10 +0100 Subject: [PATCH] feat: introduce `ForAllPromiseInterface` with `finally` and `suppressErrors` method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/ForAllPromise.php | 78 ++++++++++++++++++++++++++++++++ src/ForAllPromiseInterface.php | 21 +++++++++ src/Map.php | 36 ++++++++------- src/MapInterface.php | 2 +- src/OrderedList.php | 42 +++++++++-------- src/OrderedListInterface.php | 2 +- tests/ForAllPromiseTest.php | 23 ++++++++++ tests/GenericMapTest.php | 35 ++++++++++++++ tests/GenericOrderedListTest.php | 35 ++++++++++++++ 9 files changed, 237 insertions(+), 37 deletions(-) create mode 100644 src/ForAllPromise.php create mode 100644 src/ForAllPromiseInterface.php create mode 100644 tests/ForAllPromiseTest.php diff --git a/src/ForAllPromise.php b/src/ForAllPromise.php new file mode 100644 index 0000000..dfeb39e --- /dev/null +++ b/src/ForAllPromise.php @@ -0,0 +1,78 @@ +task = $task; + } + + public function finally(callable $callback): ForAllPromiseInterface + { + $this->finally = $callback; + + return $this; + } + + public function suppressErrors(): ForAllPromiseInterface + { + $this->suppressErrors = true; + + return $this; + } + + public function __destruct() + { + if ($this->executed) { + return; + } + + $this->execute(); + } + + public function execute(): void + { + $exception = null; + + try { + ($this->task)(); + } catch (RuntimeException $exception) { + } finally { + $this->executed = true; + } + + if ($this->finally !== null) { + ($this->finally)(); + } + + if ($exception === null || $this->suppressErrors === true) { + return; + } + + throw $exception; + } +} diff --git a/src/ForAllPromiseInterface.php b/src/ForAllPromiseInterface.php new file mode 100644 index 0000000..1b538de --- /dev/null +++ b/src/ForAllPromiseInterface.php @@ -0,0 +1,21 @@ +data; } - public function forAll(callable $callback, bool $stopOnError = false): void + public function forAll(callable $callback, bool $stopOnError = false): ForAllPromiseInterface { - /** @var MapInterface $errors */ - $errors = new GenericMap([]); - foreach ($this->data as $key => $value) { - try { - $callback($value, $key); - } catch (Throwable $throwable) { - /** @psalm-suppress ImpureMethodCall */ - $errors = $errors->put($key, $throwable); + $data = $this->data; - if ($stopOnError) { - break; + return new ForAllPromise(static function () use ($data, $callback, $stopOnError): void { + /** @var MapInterface $errors */ + $errors = new GenericMap([]); + foreach ($data as $key => $value) { + try { + $callback($value, $key); + } catch (Throwable $throwable) { + /** @psalm-suppress ImpureMethodCall */ + $errors = $errors->put($key, $throwable); + + if ($stopOnError) { + break; + } } } - } - if ($errors->isEmpty()) { - return; - } + if ($errors->isEmpty()) { + return; + } - throw MappedErrorCollection::create($errors); + throw MappedErrorCollection::create($errors); + }); } } diff --git a/src/MapInterface.php b/src/MapInterface.php index 3924392..0e336a4 100644 --- a/src/MapInterface.php +++ b/src/MapInterface.php @@ -155,5 +155,5 @@ public function slice(int $length): MapInterface; * @param callable(TValue,TKey):void $callback * @throws MappedErrorCollection If an error occured during execution. */ - public function forAll(callable $callback, bool $stopOnError = false): void; + public function forAll(callable $callback, bool $stopOnError = false): ForAllPromiseInterface; } diff --git a/src/OrderedList.php b/src/OrderedList.php index 5ad0a43..c14be72 100644 --- a/src/OrderedList.php +++ b/src/OrderedList.php @@ -369,29 +369,33 @@ public function has(int $index): bool return array_key_exists($index, $this->data); } - public function forAll(callable $callback, bool $stopOnError = false): void + public function forAll(callable $callback, bool $stopOnError = false): ForAllPromiseInterface { - /** @var OrderedListInterface $errors */ - $errors = new GenericOrderedList([]); - $error = false; - foreach ($this->data as $index => $value) { - $throwable = null; - try { - $callback($value, $index); - } catch (Throwable $throwable) { - $error = true; - if ($stopOnError) { - break; + $data = $this->data; + + return new ForAllPromise(static function () use ($data, $callback, $stopOnError): void { + /** @var OrderedListInterface $errors */ + $errors = new GenericOrderedList([]); + $error = false; + foreach ($data as $index => $value) { + $throwable = null; + try { + $callback($value, $index); + } catch (Throwable $throwable) { + $error = true; + if ($stopOnError) { + break; + } + } finally { + $errors = $errors->add($throwable); } - } finally { - $errors = $errors->add($throwable); } - } - if (! $error) { - return; - } + if (! $error) { + return; + } - throw OrderedErrorCollection::create($errors); + throw OrderedErrorCollection::create($errors); + }); } } diff --git a/src/OrderedListInterface.php b/src/OrderedListInterface.php index 1d0d8fe..a2c0414 100644 --- a/src/OrderedListInterface.php +++ b/src/OrderedListInterface.php @@ -132,5 +132,5 @@ public function has(int $index): bool; * @param callable(TValue,int):void $callback * @throws OrderedErrorCollection If an error occured during execution. */ - public function forAll(callable $callback, bool $stopOnError = false): void; + public function forAll(callable $callback, bool $stopOnError = false): ForAllPromiseInterface; } diff --git a/tests/ForAllPromiseTest.php b/tests/ForAllPromiseTest.php new file mode 100644 index 0000000..734fa42 --- /dev/null +++ b/tests/ForAllPromiseTest.php @@ -0,0 +1,23 @@ +execute(); + $this->expectNotToPerformAssertions(); + } +} diff --git a/tests/GenericMapTest.php b/tests/GenericMapTest.php index db44dac..353e65c 100644 --- a/tests/GenericMapTest.php +++ b/tests/GenericMapTest.php @@ -878,4 +878,39 @@ public function testWillGenerateErrorCollectionWhileExecutingForAllButStopsExecu self::assertCount(1, $errors); self::assertInstanceOf(RuntimeException::class, $errors->get('bar')); } + + public function testForAllPromiseWillSuppressErrors(): void + { + $map = new GenericMap(['foo' => 'bar']); + + $callback = static function (): void { + throw new RuntimeException(); + }; + $map->forAll($callback)->suppressErrors(); + + $this->expectException(RuntimeException::class); + $map->forAll($callback); + } + + public function testForAllPromiseWillExecuteFinallyMethodBeforeThrowingException(): void + { + $callbackInvoked = false; + $callback = static function () use (&$callbackInvoked): void { + $callbackInvoked = true; + }; + + $map = new GenericMap(['foo' => 'bar']); + + $runtimeExceptionCaught = false; + try { + $map->forAll(static function (): void { + throw new RuntimeException(); + })->finally($callback); + } catch (RuntimeException $exception) { + $runtimeExceptionCaught = true; + } + + self::assertTrue($runtimeExceptionCaught); + self::assertTrue($callbackInvoked); + } } diff --git a/tests/GenericOrderedListTest.php b/tests/GenericOrderedListTest.php index 52dd933..7e04892 100644 --- a/tests/GenericOrderedListTest.php +++ b/tests/GenericOrderedListTest.php @@ -1283,4 +1283,39 @@ public function testWillGenerateErrorCollectionWhileExecutingForAllButStopsExecu self::assertNull($errors->at(0)); self::assertInstanceOf(RuntimeException::class, $errors->at(1)); } + + public function testForAllPromiseWillSuppressErrors(): void + { + $map = new GenericOrderedList(['bar']); + + $callback = static function (): void { + throw new RuntimeException(); + }; + $map->forAll($callback)->suppressErrors(); + + $this->expectException(RuntimeException::class); + $map->forAll($callback); + } + + public function testForAllPromiseWillExecuteFinallyMethodBeforeThrowingException(): void + { + $callbackInvoked = false; + $callback = static function () use (&$callbackInvoked): void { + $callbackInvoked = true; + }; + + $map = new GenericOrderedList(['bar']); + + $runtimeExceptionCaught = false; + try { + $map->forAll(static function (): void { + throw new RuntimeException(); + })->finally($callback); + } catch (RuntimeException $exception) { + $runtimeExceptionCaught = true; + } + + self::assertTrue($runtimeExceptionCaught); + self::assertTrue($callbackInvoked); + } }