Run an endless loop until it returns something different, but limit it by time
composer require rikta/timed-loop
Use Case: We want to check that an endpoint is reachable
Until we get any http-response, @file_get_contents(...)
simply returns false
(maybe a bit hacky? anyway, example!)
which is also the default "continue"-value to keep the loop going.
As soon as the endpoint is up, @file_get_contents(...)
will return the textual representation.
We expect the result in a few milliseconds or seconds, therefore the default timeout of 10 seconds is sufficient.
use Rikta\TimedLoop\TimedLoop;
$loop = new TimedLoop(fn () => @file_get_contents('http://localhost:8080/health.php'));
$healthResult = $loop();
A more verbose example:
use Rikta\TimedLoop\TimedLoop;
class RemoteRepositoryAdapter {
public function getNextItem(Repository $repository, Options $options): ?object { /*...*/ }
public function hasNextItem(Repository $repository, Options $options): bool { /*...*/ }
}
$options = new Options(/*...*/);
$repository = new Repository(/*...*/);
$repositoryAdapter = new RemoteRepositoryAdapter;
// instead of an anonymous function you can also provide callables via array
// the first element is the object/class-string/'self', the second one the method
$loop = new TimedLoop([$repositoryAdapter, 'getNextItem']);
// by default it waits until something else than `false`
$loop->untilItReturnsSomethingElseThan(null);
// by default it retries it after 50000 microseconds (1/1_000_000 second)
$loop->retryingAfterMicroseconds(100_000);
// by default it throws an exception after 10 seconds
$loop->forMaximumSeconds(60);
// run the loop until the callable returns a non-null-value, and return said value
$nextItem = $loop($repository, $options);
// you can also use $loop->invoke($repository, $options) if you don't like invoking variables ;)
all methods are chainable, therefore the following call would be equivalent:
$nextItem = (new TimedLoop([$repositoryAdapter, 'getNextItem']))
->untilItReturnsSomethingElseThan(null)
->retryingAfterMicroseconds(100_000)
->forMaximumSeconds(60)
->invoke($repository, $options);
If you're happy with the defaults, you can also just use the static loop($callable, ...$args)
// loops until hasNextItem() returns true
TimedLoop::loop([$repositoryAdapter, 'hasNextItem'], $repository, $options);