-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
34 changed files
with
720 additions
and
569 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jenky\Atlas\Pool\Client; | ||
|
||
use Psr\Http\Client\ClientInterface; | ||
|
||
interface AsyncClientInterface extends ClientInterface | ||
{ | ||
public const DRIVER_PSL = 1; | ||
public const DRIVER_REACT = 2; | ||
|
||
/** | ||
* Get the underlying async driver type. | ||
*/ | ||
public function driver(): int; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jenky\Atlas\Pool\Client; | ||
|
||
use Jenky\Atlas\Pool\Concurrency\Deferrable; | ||
use Jenky\Atlas\Pool\Concurrency\PslDeferred; | ||
use Jenky\Atlas\Pool\Concurrency\ReactDeferred; | ||
|
||
trait AsyncClientTrait | ||
{ | ||
abstract private function getDeferrable(): Deferrable; | ||
|
||
public function driver(): int | ||
{ | ||
$deferrable = $this->getDeferrable(); | ||
|
||
return match (true) { | ||
$deferrable instanceof PslDeferred => AsyncClientInterface::DRIVER_PSL, | ||
$deferrable instanceof ReactDeferred => AsyncClientInterface::DRIVER_REACT, | ||
default => 0, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jenky\Atlas\Pool\Client; | ||
|
||
use GuzzleHttp\ClientInterface as GuzzleClientInterface; | ||
use Jenky\Atlas\Pool\Concurrency\PslDeferred; | ||
use Jenky\Atlas\Pool\Concurrency\ReactDeferred; | ||
use Jenky\Atlas\Pool\Exception\UnsupportedClientException; | ||
use Jenky\Atlas\Pool\Exception\UnsupportedFeatureException; | ||
use Jenky\Atlas\Pool\Util; | ||
use Psr\Http\Client\ClientInterface; | ||
use Symfony\Component\HttpClient\Psr18Client; | ||
use Symfony\Contracts\HttpClient\HttpClientInterface; | ||
|
||
final class Factory | ||
{ | ||
/** | ||
* Create new async version of the given client. | ||
* | ||
* @throws \Jenky\Atlas\Pool\Exception\UnsupportedClientException | ||
* @throws \Jenky\Atlas\Pool\Exception\UnsupportedFeatureException | ||
*/ | ||
public static function createAsyncClient(ClientInterface $client): AsyncClientInterface | ||
{ | ||
if (Util::isPslInstalled()) { | ||
if ($client instanceof GuzzleClientInterface) { | ||
return new GuzzleClient(new PslDeferred(), $client); | ||
} | ||
|
||
if ($client instanceof Psr18Client) { | ||
return new SymfonyClient(new PslDeferred(), self::getUnderlyingSymfonyHttpClient($client)); | ||
} | ||
|
||
throw new UnsupportedClientException(\sprintf( | ||
'The client %s is not supported. The PSL Pool only supports "guzzlehttp/guzzle" and "symfony/http-client".', | ||
\get_debug_type($client) | ||
)); | ||
} | ||
|
||
if (Util::isReactInstalled()) { | ||
if ($client instanceof GuzzleClientInterface) { | ||
return new GuzzleClient(new ReactDeferred(), $client); | ||
} | ||
|
||
if ($client instanceof Psr18Client) { | ||
return new SymfonyClient(new ReactDeferred(), self::getUnderlyingSymfonyHttpClient($client)); | ||
} | ||
|
||
if (\class_exists(Browser::class)) { | ||
return new ReactClient(); | ||
} | ||
|
||
throw new UnsupportedClientException(\sprintf( | ||
'The concurrent requests feature cannot be used as the client %s is not supported. To utilize this feature, please install package "react/http".', | ||
\get_debug_type($client) | ||
)); | ||
} | ||
|
||
throw new UnsupportedFeatureException('You cannot use the concurrent request pool feature as the required packages are not installed.'); | ||
} | ||
|
||
private static function getUnderlyingSymfonyHttpClient(Psr18Client $client): ?HttpClientInterface | ||
{ | ||
try { | ||
$reflectionProperty = new \ReflectionProperty($client, 'client'); | ||
$reflectionProperty->setAccessible(true); | ||
|
||
return $reflectionProperty->getValue($client); | ||
// @codeCoverageIgnoreStart | ||
} catch (\Throwable) { | ||
return null; | ||
} | ||
// @codeCoverageIgnoreEnd | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jenky\Atlas\Pool\Concurrency; | ||
|
||
interface Deferrable | ||
{ | ||
/** | ||
* @template T | ||
* | ||
* @param callable(\Closure(T), \Closure(\Throwable)) $callback | ||
* @return T | ||
*/ | ||
public function defer(callable $callback): mixed; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jenky\Atlas\Pool\Concurrency; | ||
|
||
use Psl\Async; | ||
|
||
final class PslConcurrency implements Runner | ||
{ | ||
private readonly Async\Semaphore $semaphore; | ||
|
||
/** | ||
* @param int<1, max> $limit | ||
*/ | ||
public function __construct(int $limit = 10, ?\Closure $operation = null) | ||
{ | ||
if ($limit < 1) { | ||
throw new \ValueError('Argument #1 ($limit) must be positive, got '.$limit); | ||
} | ||
|
||
$this->semaphore = new Async\Semaphore( | ||
$limit, $operation ?? static fn ($value) => $value | ||
); | ||
} | ||
|
||
public function run(iterable $tasks): array | ||
{ | ||
$promises = static function (iterable $tasks, Async\Semaphore $semaphore) { | ||
foreach ($tasks as $key => $task) { | ||
if (! \is_callable($task)) { | ||
continue; | ||
} | ||
|
||
yield $key => static fn () => $semaphore->waitFor($task()); | ||
} | ||
}; | ||
|
||
return Async\concurrently($promises($tasks, $this->semaphore)); //@phpstan-ignore-line | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jenky\Atlas\Pool\Concurrency; | ||
|
||
use Psl\Async; | ||
|
||
final class PslDeferred implements Deferrable | ||
{ | ||
public function defer(callable $callback): mixed | ||
{ | ||
$defer = new Async\Deferred(); | ||
|
||
Async\Scheduler::defer(static function () use ($defer, $callback) { | ||
$resolve = static fn (mixed $value) => $defer->complete($value); | ||
$reject = static fn (\Throwable $e) => $defer->error($e); | ||
$callback($resolve, $reject); | ||
}); | ||
|
||
return $defer->getAwaitable()->await(); | ||
} | ||
} |
Oops, something went wrong.