diff --git a/.github/workflows/Tests.yaml b/.github/workflows/Tests.yaml index dc17772..e7c8bef 100644 --- a/.github/workflows/Tests.yaml +++ b/.github/workflows/Tests.yaml @@ -15,8 +15,9 @@ jobs: fail-fast: false matrix: php: - - 8.1 - 8.2 + - 8.1 + - 8.0 dependencies: - hi - lo diff --git a/README.md b/README.md index 794c816..dffad5f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ spirit of the original. Requirements ------------ -- [PHP 8.1](http://php.net/) +- [PHP 8.0](http://php.net/) - [Composer](https://getcomposer.org/) Usage diff --git a/composer.json b/composer.json index 1f66248..35d37fb 100644 --- a/composer.json +++ b/composer.json @@ -13,20 +13,16 @@ } ], "require": { - "php": "^8.1" + "php": "^8" }, "require-dev": { - "amphp/amp": "^3-beta.9", - "phpunit/phpunit": "^9.5", - "revolt/event-loop": "^0.2" + "phpunit/phpunit": "^9.5" }, "autoload": { "files": [ - "src/retry.php" - ], - "psr-4": { - "ScriptFUSION\\Retry\\": "src" - } + "src/retry.php", + "src/FailingTooHardException.php" + ] }, "autoload-dev": { "psr-4": { diff --git a/src/retry.php b/src/retry.php index e17736c..ba19b24 100644 --- a/src/retry.php +++ b/src/retry.php @@ -3,8 +3,6 @@ namespace ScriptFUSION\Retry; -use Amp\Future; - /** * Tries the specified operation up to the specified number of times. If specified, the exception handler will be * called immediately before retrying the operation. If the error handler returns false, the operation will not be @@ -28,24 +26,14 @@ function retry(int $tries, callable $operation, callable $onError = null): mixed try { beginning: - - if (($result = $operation()) instanceof Future) { - // Wait for Future to complete. - $result = $result->await(); - } + $result = $operation(); } catch (\Exception $exception) { if ($tries === ++$attempts) { throw new FailingTooHardException($attempts, $exception); } - if ($onError) { - if (($result = $onError($exception, $attempts, $tries)) instanceof Future) { - $result = $result->await(); - } - - if ($result === false) { - return null; - } + if ($onError && $onError($exception, $attempts, $tries) === false) { + return null; } goto beginning; diff --git a/test/RetryAsyncTest.php b/test/RetryAsyncTest.php deleted file mode 100644 index 4716c8b..0000000 --- a/test/RetryAsyncTest.php +++ /dev/null @@ -1,204 +0,0 @@ -getPrevious()); - self::assertSame($tries, $invocations); - } - - /** - * Tests that the error callback is called before each retry. - */ - public function testErrorCallbackAsync(): void - { - $invocations = $errors = 0; - $outerException = $innerException = null; - - try { - retry($tries = 2, static function () use (&$invocations, &$innerException) { - ++$invocations; - - throw $innerException = new \RuntimeException; - }, static function (\Exception $exception, int $attempts, int $tries) use (&$innerException, &$errors) { - ++$errors; - - self::assertSame($innerException, $exception); - self::assertSame(1, $attempts); - self::assertSame(2, $tries); - }); - } catch (FailingTooHardException $outerException) { - } - - self::assertInstanceOf(FailingTooHardException::class, $outerException); - self::assertSame($tries, $invocations); - self::assertSame($tries - 1, $errors); - } - - /** - * Tests that an error callback that returns a Future has its Future resolved. - */ - public function testFutureErrorCallback(): void - { - $delay = .25; // Quarter of a second. - $start = microtime(true); - - try { - retry($tries = 3, static function () { - throw new \DomainException; - }, fn () => self::delayAndReturn($delay)); - } catch (FailingTooHardException $outerException) { - self::assertInstanceOf(\DomainException::class, $outerException->getPrevious()); - } - - self::assertTrue(isset($outerException)); - self::assertGreaterThan($start + $delay * ($tries - 1), microtime(true)); - } - - /** - * Tests that when error handler that returns false, it aborts retrying. - */ - public function testErrorCallbackHaltAsync(): void - { - $invocations = 0; - - retry(2, static function () use (&$invocations): never { - ++$invocations; - - throw new \RuntimeException; - }, fn () => false); - - self::assertSame(1, $invocations); - } - - /** - * Tests that when an error handler returns a Future that false, it aborts retrying. - */ - public function testFutureErrorCallbackHaltAsync(): void - { - $invocations = 0; - - retry(2, static function () use (&$invocations): never { - ++$invocations; - - throw new \RuntimeException; - }, fn () => Future::complete(false)); - - self::assertSame(1, $invocations); - } - - /** - * Tests that the exception handler can throw an exception that will not be caught. - */ - public function testErrorCallbackCanThrow(): void - { - $this->expectException(\LogicException::class); - - retry(2, static function (): never { - throw new \RuntimeException; - }, static function (): never { - throw new \LogicException; - }); - } - - private static function delayAndReturn(float $delay, string $return = null): Future - { - return async(static function () use ($delay, $return): ?string { - delay($delay); - - return $return; - }); - } -} diff --git a/test/RetryTest.php b/test/RetryTest.php index c50c7ac..5ba8a10 100644 --- a/test/RetryTest.php +++ b/test/RetryTest.php @@ -9,7 +9,10 @@ final class RetryTest extends TestCase { - public function testWithoutFailing() + /** + * Tests that when an operation is successful, its result is returned without retrying. + */ + public function testWithoutFailing(): void { $invocations = 0; @@ -23,7 +26,10 @@ public function testWithoutFailing() self::assertSame(5, $value); } - public function testFailingOnce() + /** + * Tests that when an operation fails once, it is retried. + */ + public function testFailingOnce(): void { $invocations = 0; $failed = false; @@ -45,7 +51,10 @@ public function testFailingOnce() self::assertSame(5, $value); } - public function testZeroTries() + /** + * Tests that when an operation is attempted zero times, the operation is not invoked and returns null. + */ + public function testZeroTries(): void { $invocations = 0; @@ -59,7 +68,10 @@ public function testZeroTries() self::assertNull($value); } - public function testFailingTooHard() + /** + * Tests that when an operation is retried the maximum number of tries, FailingTooHardException is thrown. + */ + public function testFailingTooHard(): void { $invocations = 0; $outerException = $innerException = null; @@ -79,10 +91,10 @@ public function testFailingTooHard() } /** - * Tests that an error callback receives the exception thrown by the operation, the current attempt and maximum - * number of attempts. + * Tests that when an exception is thrown by the operation, the error callback receives that exception, the current + * attempt index and maximum number of attempts. */ - public function testErrorCallback() + public function testErrorCallback(): void { $invocations = $errors = 0; $outerException = $innerException = null; @@ -108,20 +120,28 @@ public function testErrorCallback() } /** - * Tests that an error handler that returns false aborts retrying. + * Tests that when an error handler returns false, retries are aborted. */ - public function testErrorCallbackHalt() + public function testErrorCallbackHalt(): void { $invocations = 0; - retry($tries = 2, static function () use (&$invocations) { + retry(2, static function () use (&$invocations) { ++$invocations; throw new \RuntimeException; - }, static function () { - return false; - }); + }, fn () => false); self::assertSame(1, $invocations); } + + /** + * Tests that when an exception handler throws an exception, the exception is not caught. + */ + public function testErrorCallbackCanThrow(): void + { + $this->expectExceptionObject($exception = new \LogicException); + + retry(2, fn () => throw new \RuntimeException, fn () => throw $exception); + } }