Skip to content

Commit

Permalink
Merge pull request #42 from boesing/feature/for-all-finally
Browse files Browse the repository at this point in the history
  • Loading branch information
boesing committed Mar 14, 2021
2 parents 27043f1 + dba2255 commit bd8a96b
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 37 deletions.
78 changes: 78 additions & 0 deletions src/ForAllPromise.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

namespace Boesing\TypedArrays;

use RuntimeException;

/**
* @internal
*/
final class ForAllPromise implements ForAllPromiseInterface
{
/** @var callable():void|null */
private $finally;

/** @var callable */
private $task;

/** @var bool */
private $suppressErrors = false;

/** @var bool */
private $executed = false;

/**
* @param callable():void $task
*/
public function __construct(callable $task)
{
$this->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;
}
}
21 changes: 21 additions & 0 deletions src/ForAllPromiseInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Boesing\TypedArrays;

/**
* Promise interface to create post-execution stuff.
* This interface is not meant to be stored in any variable.
*/
interface ForAllPromiseInterface
{
/**
* @param callable():void $callback
*/
public function finally(callable $callback): self;

public function suppressErrors(): self;

public function execute(): void;
}
36 changes: 20 additions & 16 deletions src/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -385,27 +385,31 @@ public function jsonSerialize(): ?array
return $this->data;
}

public function forAll(callable $callback, bool $stopOnError = false): void
public function forAll(callable $callback, bool $stopOnError = false): ForAllPromiseInterface
{
/** @var MapInterface<TKey,Throwable> $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<TKey,Throwable> $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);
});
}
}
2 changes: 1 addition & 1 deletion src/MapInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
42 changes: 23 additions & 19 deletions src/OrderedList.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<Throwable|null> $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<Throwable|null> $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);
});
}
}
2 changes: 1 addition & 1 deletion src/OrderedListInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
23 changes: 23 additions & 0 deletions tests/ForAllPromiseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Boesing\TypedArrays;

use PHPUnit\Framework\TestCase;
use Webmozart\Assert\Assert;

final class ForAllPromiseTest extends TestCase
{
public function testWillNotExecuteTwice(): void
{
$executed = false;
$task = static function () use (&$executed): void {
Assert::false($executed);
$executed = true;
};

(new ForAllPromise($task))->execute();
$this->expectNotToPerformAssertions();
}
}
35 changes: 35 additions & 0 deletions tests/GenericMapTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
35 changes: 35 additions & 0 deletions tests/GenericOrderedListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

0 comments on commit bd8a96b

Please sign in to comment.