diff --git a/benchmarks/StarWarsBench.php b/benchmarks/StarWarsBench.php index 2fb2afbba..8ff5b232c 100644 --- a/benchmarks/StarWarsBench.php +++ b/benchmarks/StarWarsBench.php @@ -95,4 +95,27 @@ public function benchStarWarsIntrospectionQuery() $this->introQuery ); } + + public function benchQueryWithInterfaceFragment() + { + $q = ' + query UseInterfaceFragment { + luke: human(id: "1000") { + ...CharacterFragment + } + leia: human(id: "1003") { + ...CharacterFragment + } + } + + fragment CharacterFragment on Character { + name + } + '; + + GraphQL::executeQuery( + StarWarsSchema::build(), + $q + ); + } } diff --git a/benchmarks/shim.php b/benchmarks/shim.php index 6a80fa29d..f75211794 100644 --- a/benchmarks/shim.php +++ b/benchmarks/shim.php @@ -19,4 +19,5 @@ $b->benchHeroQuery(); $b->benchNestedQuery(); $b->benchQueryWithFragment(); +$b->benchQueryWithInterfaceFragment(); $b->benchStarWarsIntrospectionQuery(); diff --git a/src/Type/Schema.php b/src/Type/Schema.php index f6a93309a..c2cf0d58f 100644 --- a/src/Type/Schema.php +++ b/src/Type/Schema.php @@ -20,9 +20,10 @@ use GraphQL\Utils\InterfaceImplementations; use GraphQL\Utils\TypeInfo; use GraphQL\Utils\Utils; +use InvalidArgumentException; use Traversable; use function array_map; -use function array_values; +use function get_class; use function implode; use function is_array; use function is_callable; @@ -60,13 +61,6 @@ class Schema */ private $resolvedTypes = []; - /** - * Lazily initialized. - * - * @var array> - */ - private $subTypeMap; - /** * Lazily initialised. * @@ -507,25 +501,15 @@ public function isPossibleType(AbstractType $abstractType, ObjectType $possibleT */ public function isSubType(AbstractType $abstractType, ImplementingType $maybeSubType) : bool { - if (! isset($this->subTypeMap[$abstractType->name])) { - $this->subTypeMap[$abstractType->name] = []; + if ($abstractType instanceof InterfaceType) { + return $maybeSubType->implementsInterface($abstractType); + } - if ($abstractType instanceof UnionType) { - foreach ($abstractType->getTypes() as $type) { - $this->subTypeMap[$abstractType->name][$type->name] = true; - } - } else { - $implementations = $this->getImplementations($abstractType); - foreach ($implementations->objects() as $type) { - $this->subTypeMap[$abstractType->name][$type->name] = true; - } - foreach ($implementations->interfaces() as $type) { - $this->subTypeMap[$abstractType->name][$type->name] = true; - } - } + if ($abstractType instanceof UnionType) { + return $abstractType->isPossibleType($maybeSubType); } - return isset($this->subTypeMap[$abstractType->name][$maybeSubType->name]); + throw new InvalidArgumentException(sprintf('$abstractType must be of type UnionType|InterfaceType got: %s.', get_class($abstractType))); } /** diff --git a/tests/Type/LazyTypeLoaderTest.php b/tests/Type/LazyTypeLoaderTest.php index 1e208f994..b0ded1f4f 100644 --- a/tests/Type/LazyTypeLoaderTest.php +++ b/tests/Type/LazyTypeLoaderTest.php @@ -290,11 +290,6 @@ public function testWorksWithTypeLoader() : void 'Node', 'Content', 'PostStoryMutationInput', - 'Query.fields', - 'Content.fields', - 'Node.fields', - 'Mutation.fields', - 'BlogStory.fields', ], $this->calls ); diff --git a/tests/Type/SchemaTest.php b/tests/Type/SchemaTest.php index 9b401a49f..20046714c 100644 --- a/tests/Type/SchemaTest.php +++ b/tests/Type/SchemaTest.php @@ -5,12 +5,15 @@ namespace GraphQL\Tests\Type; use GraphQL\Error\InvariantViolation; +use GraphQL\Type\Definition\AbstractType; use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\ObjectType; +use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\Type; use GraphQL\Type\Schema; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; class SchemaTest extends TestCase @@ -130,4 +133,26 @@ public function testIncludesInputTypesOnlyUsedInDirectives() : void self::assertArrayHasKey('DirInput', $typeMap); self::assertArrayHasKey('WrappedDirInput', $typeMap); } + + // Sub Type + + /** + * @see it('validates argument to isSubType to be of the correct type') + */ + public function testThrowsInvalidArgumentExceptionWhenInvalidTypeIsPassedToIsSubType() : void + { + $this->expectException(InvalidArgumentException::class); + + $anonymousAbstractType = new class implements AbstractType { + public function resolveType($objectValue, $context, ResolveInfo $info) + { + return null; + } + }; + + $this->schema->isSubType( + $anonymousAbstractType, + new InterfaceType(['name' => 'Interface']) + ); + } } diff --git a/tests/Type/TypeLoaderTest.php b/tests/Type/TypeLoaderTest.php index 18ae90376..fd12f8308 100644 --- a/tests/Type/TypeLoaderTest.php +++ b/tests/Type/TypeLoaderTest.php @@ -243,11 +243,6 @@ public function testWorksWithTypeLoader() : void 'Node', 'Content', 'PostStoryMutationInput', - 'Query.fields', - 'Content.fields', - 'Node.fields', - 'Mutation.fields', - 'BlogStory.fields', ], $this->calls );