Skip to content

Commit 3070a28

Browse files
authored
Improve performance of subtype checks (#860)
1 parent 7ead476 commit 3070a28

File tree

6 files changed

+57
-34
lines changed

6 files changed

+57
-34
lines changed

benchmarks/StarWarsBench.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,29 @@ public function benchQueryWithFragment(): void
9191
);
9292
}
9393

94+
public function benchQueryWithInterfaceFragment(): void
95+
{
96+
$q = '
97+
query UseInterfaceFragment {
98+
luke: human(id: "1000") {
99+
...CharacterFragment
100+
}
101+
leia: human(id: "1003") {
102+
...CharacterFragment
103+
}
104+
}
105+
106+
fragment CharacterFragment on Character {
107+
name
108+
}
109+
';
110+
111+
GraphQL::executeQuery(
112+
StarWarsSchema::build(),
113+
$q
114+
);
115+
}
116+
94117
public function benchStarWarsIntrospectionQuery(): void
95118
{
96119
GraphQL::executeQuery(

benchmarks/shim.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@
2525
$b->benchHeroQuery();
2626
$b->benchNestedQuery();
2727
$b->benchQueryWithFragment();
28+
$b->benchQueryWithInterfaceFragment();
2829
$b->benchStarWarsIntrospectionQuery();

src/Type/Schema.php

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
use GraphQL\Utils\InterfaceImplementations;
2121
use GraphQL\Utils\TypeInfo;
2222
use GraphQL\Utils\Utils;
23+
use InvalidArgumentException;
2324
use Traversable;
2425

2526
use function array_map;
27+
use function get_class;
2628
use function implode;
2729
use function is_array;
2830
use function is_callable;
@@ -60,13 +62,6 @@ class Schema
6062
*/
6163
private $resolvedTypes = [];
6264

63-
/**
64-
* Lazily initialized.
65-
*
66-
* @var array<string, array<string, ObjectType|UnionType>>
67-
*/
68-
private $subTypeMap;
69-
7065
/**
7166
* Lazily initialised.
7267
*
@@ -524,26 +519,15 @@ public function isPossibleType(AbstractType $abstractType, ObjectType $possibleT
524519
*/
525520
public function isSubType(AbstractType $abstractType, ImplementingType $maybeSubType): bool
526521
{
527-
if (! isset($this->subTypeMap[$abstractType->name])) {
528-
$this->subTypeMap[$abstractType->name] = [];
529-
530-
if ($abstractType instanceof UnionType) {
531-
foreach ($abstractType->getTypes() as $type) {
532-
$this->subTypeMap[$abstractType->name][$type->name] = true;
533-
}
534-
} else {
535-
$implementations = $this->getImplementations($abstractType);
536-
foreach ($implementations->objects() as $type) {
537-
$this->subTypeMap[$abstractType->name][$type->name] = true;
538-
}
522+
if ($abstractType instanceof InterfaceType) {
523+
return $maybeSubType->implementsInterface($abstractType);
524+
}
539525

540-
foreach ($implementations->interfaces() as $type) {
541-
$this->subTypeMap[$abstractType->name][$type->name] = true;
542-
}
543-
}
526+
if ($abstractType instanceof UnionType) {
527+
return $abstractType->isPossibleType($maybeSubType);
544528
}
545529

546-
return isset($this->subTypeMap[$abstractType->name][$maybeSubType->name]);
530+
throw new InvalidArgumentException(sprintf('$abstractType must be of type UnionType|InterfaceType got: %s.', get_class($abstractType)));
547531
}
548532

549533
/**

tests/Type/LazyTypeLoaderTest.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,11 +296,6 @@ public function testWorksWithTypeLoader(): void
296296
'Node',
297297
'Content',
298298
'PostStoryMutationInput',
299-
'Query.fields',
300-
'Content.fields',
301-
'Node.fields',
302-
'Mutation.fields',
303-
'BlogStory.fields',
304299
],
305300
$this->calls
306301
);

tests/Type/SchemaTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
namespace GraphQL\Tests\Type;
66

77
use GraphQL\Error\InvariantViolation;
8+
use GraphQL\Type\Definition\AbstractType;
89
use GraphQL\Type\Definition\Directive;
910
use GraphQL\Type\Definition\InputObjectType;
1011
use GraphQL\Type\Definition\InterfaceType;
1112
use GraphQL\Type\Definition\ObjectType;
13+
use GraphQL\Type\Definition\ResolveInfo;
1214
use GraphQL\Type\Definition\Type;
1315
use GraphQL\Type\Schema;
16+
use InvalidArgumentException;
1417
use PHPUnit\Framework\TestCase;
1518

1619
class SchemaTest extends TestCase
@@ -130,4 +133,26 @@ public function testIncludesInputTypesOnlyUsedInDirectives(): void
130133
self::assertArrayHasKey('DirInput', $typeMap);
131134
self::assertArrayHasKey('WrappedDirInput', $typeMap);
132135
}
136+
137+
// Sub Type
138+
139+
/**
140+
* @see it('validates argument to isSubType to be of the correct type')
141+
*/
142+
public function testThrowsInvalidArgumentExceptionWhenInvalidTypeIsPassedToIsSubType(): void
143+
{
144+
$this->expectException(InvalidArgumentException::class);
145+
146+
$anonymousAbstractType = new class implements AbstractType {
147+
public function resolveType($objectValue, $context, ResolveInfo $info)
148+
{
149+
return null;
150+
}
151+
};
152+
153+
$this->schema->isSubType(
154+
$anonymousAbstractType,
155+
new InterfaceType(['name' => 'Interface'])
156+
);
157+
}
133158
}

tests/Type/TypeLoaderTest.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,6 @@ public function testWorksWithTypeLoader(): void
244244
'Node',
245245
'Content',
246246
'PostStoryMutationInput',
247-
'Query.fields',
248-
'Content.fields',
249-
'Node.fields',
250-
'Mutation.fields',
251-
'BlogStory.fields',
252247
],
253248
$this->calls
254249
);

0 commit comments

Comments
 (0)