Files
IPB/system/3rd_party/graphql-php/Executor/Promise/Adapter/SyncPromiseAdapter.php
T
2025-12-19 05:55:31 -08:00

181 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
namespace GraphQL\Executor\Promise\Adapter;
use GraphQL\Error\InvariantViolation;
use GraphQL\Executor\ExecutionResult;
use GraphQL\Executor\Promise\Promise;
use GraphQL\Executor\Promise\PromiseAdapter;
use GraphQL\Utils\Utils;
use Throwable;
use function count;
/**
* Allows changing order of field resolution even in sync environments
* (by leveraging queue of deferreds and promises)
*/
class SyncPromiseAdapter implements PromiseAdapter
{
/**
* @inheritdoc
*/
public function isThenable($value)
{
return $value instanceof SyncPromise;
}
/**
* @inheritdoc
*/
public function convertThenable($thenable)
{
if (! $thenable instanceof SyncPromise) {
// End-users should always use Deferred (and don't use SyncPromise directly)
throw new InvariantViolation('Expected instance of GraphQL\Deferred, got ' . Utils::printSafe($thenable));
}
return new Promise($thenable, $this);
}
/**
* @inheritdoc
*/
public function then(Promise $promise, ?callable $onFulfilled = null, ?callable $onRejected = null)
{
/** @var SyncPromise $adoptedPromise */
$adoptedPromise = $promise->adoptedPromise;
return new Promise($adoptedPromise->then($onFulfilled, $onRejected), $this);
}
/**
* @inheritdoc
*/
public function create(callable $resolver)
{
$promise = new SyncPromise();
try {
$resolver(
[
$promise,
'resolve',
],
[
$promise,
'reject',
]
);
} catch (Throwable $e) {
$promise->reject($e);
}
return new Promise($promise, $this);
}
/**
* @inheritdoc
*/
public function createFulfilled($value = null)
{
$promise = new SyncPromise();
return new Promise($promise->resolve($value), $this);
}
/**
* @inheritdoc
*/
public function createRejected($reason)
{
$promise = new SyncPromise();
return new Promise($promise->reject($reason), $this);
}
/**
* @inheritdoc
*/
public function all(array $promisesOrValues)
{
$all = new SyncPromise();
$total = count($promisesOrValues);
$count = 0;
$result = [];
foreach ($promisesOrValues as $index => $promiseOrValue) {
if ($promiseOrValue instanceof Promise) {
$result[$index] = null;
$promiseOrValue->then(
static function ($value) use ($index, &$count, $total, &$result, $all) : void {
$result[$index] = $value;
$count++;
if ($count < $total) {
return;
}
$all->resolve($result);
},
[$all, 'reject']
);
} else {
$result[$index] = $promiseOrValue;
$count++;
}
}
if ($count === $total) {
$all->resolve($result);
}
return new Promise($all, $this);
}
/**
* Synchronously wait when promise completes
*
* @return ExecutionResult
*/
public function wait(Promise $promise)
{
$this->beforeWait($promise);
$taskQueue = SyncPromise::getQueue();
while ($promise->adoptedPromise->state === SyncPromise::PENDING &&
! $taskQueue->isEmpty()
) {
SyncPromise::runQueue();
$this->onWait($promise);
}
/** @var SyncPromise $syncPromise */
$syncPromise = $promise->adoptedPromise;
if ($syncPromise->state === SyncPromise::FULFILLED) {
return $syncPromise->result;
}
if ($syncPromise->state === SyncPromise::REJECTED) {
throw $syncPromise->result;
}
throw new InvariantViolation('Could not resolve promise');
}
/**
* Execute just before starting to run promise completion
*/
protected function beforeWait(Promise $promise)
{
}
/**
* Execute while running promise completion
*/
protected function onWait(Promise $promise)
{
}
}