feat: make Promise and PromiseAdapter generic over the adopted promise type#1941
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adjusts the GraphQL\Executor\Promise\Promise PHPDoc to reference Amp types via fully-qualified names instead of use imports, preventing static analyzers from failing symbol discovery when only one of Amp\Promise (v2) or Amp\Future (v3) exists (or Amp isn’t installed).
Changes:
- Removed
use Amp\Future/use Amp\Promiseimports that may reference non-installed symbols. - Updated the
$adoptedPromise@varunion to use\Amp\Futureand\Amp\Promiseas FQNs.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This comment was marked as outdated.
This comment was marked as outdated.
91343b7 to
d99661d
Compare
8c5b9aa to
877608b
Compare
3a6c43b to
33111ce
Compare
40cce47 to
b6ed59c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (1)
src/Executor/Promise/Adapter/AmpFutureAdapter.php:52
then()no longer validates that the wrapped value is anAmp\Future. If aPromisefrom a different adapter is passed in,$future->await()will fatal instead of producing a controlledInvariantViolation.
public function then(Promise $promise, ?callable $onFulfilled = null, ?callable $onRejected = null): Promise
{
$future = $promise->adoptedPromise;
$next = async(static function () use ($future, $onFulfilled, $onRejected) {
try {
$value = $future->await();
} catch (\Throwable $reason) {
06f9f38 to
dd92cb6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/Executor/Promise/Promise.php:34
Promiseis generic overTAdopted, but the stored adapter is not typed asPromiseAdapter<TAdopted>. Without binding the adapter to the same template, static analysis can’t verify that$this->adapter->then($this, ...)actually returnsPromise<TAdopted>, and it also permits constructingPromise<TAdopted>with a mismatched adapter type.
private PromiseAdapter $adapter;
/**
* @phpstan-param TAdopted $adoptedPromise
*
* @throws InvariantViolation
*/
public function __construct($adoptedPromise, PromiseAdapter $adapter)
644f403 to
cfd879c
Compare
4543954 to
e605d0c
Compare
e605d0c to
f1f02d7
Compare
f1f02d7 to
6078203
Compare
spawnia
left a comment
There was a problem hiding this comment.
Looks good overall, happy to do a final review and merge once CI passes.
6078203 to
f1f02d7
Compare
|
I tried to fiddle with one more thing but I'll skip it for now. CI is green. |
|
Thanks @simPod, released with https://github.com/webonyx/graphql-php/releases/tag/v15.34.0. |
Problem
GraphQL\Executor\Promise\Promisedocuments$adoptedPromisewith a@varunion naming every supported platform type, which requires importing bothAmp\FutureandAmp\Promise:amphp/amp v2 ships
Amp\Promiseand v3 shipsAmp\Future, so on any single installation one of those classes does not exist. Because the property is read by consumers, static analysis resolves the whole union and crashes on the missing class — e.g. PHPStan:Change
Make
PromiseandPromiseAdaptergeneric overTAdopted, with each adapter binding the concrete platform type:SyncPromiseAdapter→PromiseAdapter<SyncPromise>ReactPromiseAdapter→PromiseAdapter<React\Promise\PromiseInterface<mixed>>AmpFutureAdapter→PromiseAdapter<Amp\Future<mixed>>AmpPromiseAdapter→PromiseAdapter<Amp\Promise<mixed>>Promiseno longer names any optional platform class, so it analyses cleanly regardless of which promise library is installed. The adapters gain@phpstan-return Promise<T>annotations and shed now-redundantassert()/instanceofguards; adapter tests dropassertInstanceOfassertions the generics make statically provable.TAdopteddefaults tomixed, so existing barePromise/PromiseAdapterreferences are unchanged — not a BC break.