Removed schema descriptor (as lazy loading of types can work without it now)

This commit is contained in:
Vladimir Razuvaev 2017-08-14 01:42:02 +07:00
parent f9eb14869f
commit 20f8cab943
7 changed files with 58 additions and 188 deletions

View File

@ -21,11 +21,6 @@ class HugeSchemaBench
*/ */
private $schemaBuilder; private $schemaBuilder;
/**
* @var \GraphQL\Type\Descriptor
*/
private $descriptor;
private $schema; private $schema;
private $lazySchema; private $lazySchema;
@ -47,7 +42,6 @@ class HugeSchemaBench
$this->schema = $this->schemaBuilder->buildSchema(); $this->schema = $this->schemaBuilder->buildSchema();
$queryBuilder = new QueryGenerator($this->schema, 0.05); $queryBuilder = new QueryGenerator($this->schema, 0.05);
$this->descriptor = $this->schema->describe();
$this->smallQuery = $queryBuilder->buildQuery(); $this->smallQuery = $queryBuilder->buildQuery();
} }
@ -80,7 +74,6 @@ class HugeSchemaBench
return new Schema( return new Schema(
\GraphQL\Type\SchemaConfig::create() \GraphQL\Type\SchemaConfig::create()
->setQuery($this->schemaBuilder->buildQueryType()) ->setQuery($this->schemaBuilder->buildQueryType())
// ->setDescriptor($this->descriptor)
->setTypeLoader(function($name) { ->setTypeLoader(function($name) {
return $this->schemaBuilder->loadType($name); return $this->schemaBuilder->loadType($name);
}) })

View File

@ -1062,7 +1062,7 @@ class Executor
$runtimeType = $returnType->resolveType($result, $exeContext->contextValue, $info); $runtimeType = $returnType->resolveType($result, $exeContext->contextValue, $info);
if (null === $runtimeType) { if (null === $runtimeType) {
if ($returnType instanceof InterfaceType && !$exeContext->schema->getConfig()->descriptor) { if ($returnType instanceof InterfaceType) {
Warning::warnOnce( Warning::warnOnce(
"GraphQL Interface Type `{$returnType->name}` returned `null` from it`s `resolveType` function ". "GraphQL Interface Type `{$returnType->name}` returned `null` from it`s `resolveType` function ".
'for value: ' . Utils::printSafe($result) . '. Switching to slow resolution method using `isTypeOf` ' . 'for value: ' . Utils::printSafe($result) . '. Switching to slow resolution method using `isTypeOf` ' .

View File

@ -1,61 +0,0 @@
<?php
namespace GraphQL\Type;
use GraphQL\Utils\Utils;
class Descriptor implements \JsonSerializable
{
public $version = '1.0';
public $typeMap = [];
public $possibleTypeMap = [];
public $created;
public static function fromArray(array $array)
{
Utils::invariant(
isset($array['version'], $array['typeMap'], $array['possibleTypeMap'], $array['created']),
__METHOD__ . ' expects array with keys "version", "typeMap", "possibleTypeMap", "created" but got keys: %s',
implode(', ', array_keys($array))
);
Utils::invariant(
is_string($array['version']) && '1.0' === $array['version'],
__METHOD__ . ' expects array where "version" key equals to "1.0"'
);
Utils::invariant(
is_array($array['typeMap']) && is_array($array['possibleTypeMap']),
__METHOD__ . ' expects array where "typeMap" and "possibleTypeMap" keys are arrays'
);
Utils::invariant(
is_int($array['created']),
__METHOD__ . ' expects array where "created" key is integer timestamp'
);
$descriptor = new self();
Utils::assign($descriptor, $array);
}
public function toArray()
{
return [
'typeMap' => $this->typeMap,
'version' => $this->version,
'possibleTypeMap' => $this->possibleTypeMap,
'created' => $this->created
];
}
/**
* Specify data which should be serialized to JSON
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
* @return mixed data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource.
* @since 5.4.0
*/
function jsonSerialize()
{
return $this->toArray();
}
}

View File

@ -49,13 +49,6 @@ class Schema
*/ */
private $config; private $config;
/**
* Contains actual descriptor for this schema
*
* @var Descriptor
*/
private $descriptor;
/** /**
* Contains currently resolved schema types * Contains currently resolved schema types
* *
@ -63,6 +56,11 @@ class Schema
*/ */
private $resolvedTypes = []; private $resolvedTypes = [];
/**
* @var array
*/
private $possibleTypeMap;
/** /**
* True when $resolvedTypes contain all possible schema types * True when $resolvedTypes contain all possible schema types
* *
@ -104,8 +102,7 @@ class Schema
'subscription', 'subscription',
'types', 'types',
'directives', 'directives',
'typeLoader', 'typeLoader'
'descriptor'
]), ]),
Utils::getVariableType($config) Utils::getVariableType($config)
); );
@ -116,6 +113,15 @@ class Schema
); );
$this->config = $config; $this->config = $config;
$this->resolvedTypes[$config->query->name] = $config->query;
if ($config->mutation) {
$this->resolvedTypes[$config->mutation->name] = $config->mutation;
}
if ($config->subscription) {
$this->resolvedTypes[$config->subscription->name] = $config->subscription;
}
} }
/** /**
@ -165,15 +171,7 @@ class Schema
public function getTypeMap() public function getTypeMap()
{ {
if (!$this->fullyLoaded) { if (!$this->fullyLoaded) {
if ($this->config->descriptor && $this->config->typeLoader) { $this->resolvedTypes = $this->collectAllTypes();
// Following is still faster than $this->collectAllTypes() because it won't init fields
$typesToResolve = array_diff_key($this->config->descriptor->typeMap, $this->resolvedTypes);
foreach ($typesToResolve as $typeName => $_) {
$this->resolvedTypes[$typeName] = $this->loadType($typeName);
}
} else {
$this->resolvedTypes = $this->collectAllTypes();
}
$this->fullyLoaded = true; $this->fullyLoaded = true;
} }
return $this->resolvedTypes; return $this->resolvedTypes;
@ -190,51 +188,11 @@ class Schema
return $this->resolveType($name); return $this->resolveType($name);
} }
/**
* Returns serializable schema descriptor which can be passed later
* to Schema config to enable a set of performance optimizations
*
* @return Descriptor
*/
public function describe()
{
if ($this->descriptor) {
return $this->descriptor;
}
$this->resolvedTypes = $this->collectAllTypes();
$this->fullyLoaded = true;
$descriptor = new Descriptor();
$descriptor->version = '1.0';
$descriptor->created = time();
foreach ($this->resolvedTypes as $type) {
if ($type instanceof ObjectType) {
foreach ($type->getInterfaces() as $interface) {
$descriptor->possibleTypeMap[$interface->name][$type->name] = 1;
}
} else if ($type instanceof UnionType) {
foreach ($type->getTypes() as $innerType) {
$descriptor->possibleTypeMap[$type->name][$innerType->name] = 1;
}
}
$descriptor->typeMap[$type->name] = 1;
}
return $this->descriptor = $descriptor;
}
private function collectAllTypes() private function collectAllTypes()
{ {
$initialTypes = array_merge( $initialTypes = array_merge(
[ array_values($this->resolvedTypes),
$this->config->query, [Introspection::_schema()]
$this->config->mutation,
$this->config->subscription,
Introspection::_schema()
],
array_values($this->resolvedTypes)
); );
$typeMap = []; $typeMap = [];
@ -276,20 +234,30 @@ class Schema
*/ */
public function getPossibleTypes(AbstractType $abstractType) public function getPossibleTypes(AbstractType $abstractType)
{ {
if ($abstractType instanceof UnionType) { $possibleTypeMap = $this->getPossibleTypeMap();
return $abstractType->getTypes(); return array_values($possibleTypeMap[$abstractType->name]);
} }
/** @var InterfaceType $abstractType */ /**
$descriptor = $this->config->descriptor ?: $this->describe(); * @return array
*/
$result = []; private function getPossibleTypeMap()
if (isset($descriptor->possibleTypeMap[$abstractType->name])) { {
foreach ($descriptor->possibleTypeMap[$abstractType->name] as $typeName => $_) { if ($this->possibleTypeMap === null) {
$result[] = $this->resolveType($typeName); $this->possibleTypeMap = [];
foreach ($this->getTypeMap() as $type) {
if ($type instanceof ObjectType) {
foreach ($type->getInterfaces() as $interface) {
$this->possibleTypeMap[$interface->name][$type->name] = $type;
}
} else if ($type instanceof UnionType) {
foreach ($type->getTypes() as $innerType) {
$this->possibleTypeMap[$type->name][$innerType->name] = $innerType;
}
}
} }
} }
return $result; return $this->possibleTypeMap;
} }
/** /**
@ -336,10 +304,6 @@ class Schema
*/ */
public function isPossibleType(AbstractType $abstractType, ObjectType $possibleType) public function isPossibleType(AbstractType $abstractType, ObjectType $possibleType)
{ {
if ($this->config->descriptor) {
return !empty($this->config->descriptor->possibleTypeMap[$abstractType->name][$possibleType->name]);
}
if ($abstractType instanceof InterfaceType) { if ($abstractType instanceof InterfaceType) {
return $possibleType->implementsInterface($abstractType); return $possibleType->implementsInterface($abstractType);
} }
@ -381,14 +345,8 @@ class Schema
private function defaultTypeLoader($typeName) private function defaultTypeLoader($typeName)
{ {
// Default type loader simply fallbacks to collecting all types // Default type loader simply fallbacks to collecting all types
if (!$this->fullyLoaded) { $typeMap = $this->getTypeMap();
$this->resolvedTypes = $this->collectAllTypes(); return isset($typeMap[$typeName]) ? $typeMap[$typeName] : null;
$this->fullyLoaded = true;
}
if (!isset($this->resolvedTypes[$typeName])) {
return null;
}
return $this->resolvedTypes[$typeName];
} }

View File

@ -1,8 +1,6 @@
<?php <?php
namespace GraphQL\Type; namespace GraphQL\Type;
use GraphQL\Error\InvariantViolation;
use GraphQL\Type\Descriptor;
use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
@ -41,11 +39,6 @@ class SchemaConfig
*/ */
public $directives; public $directives;
/**
* @var Descriptor
*/
public $descriptor;
/** /**
* @var callable * @var callable
*/ */
@ -105,6 +98,20 @@ class SchemaConfig
$config->setDirectives($options['directives']); $config->setDirectives($options['directives']);
} }
if (isset($options['typeResolution'])) {
trigger_error(
'Type resolution strategies are deprecated. Just pass single option `typeLoader` '.
'to schema constructor instead.',
E_USER_DEPRECATED
);
if ($options['typeResolution'] instanceof Resolution && !isset($options['typeLoader'])) {
$strategy = $options['typeResolution'];
$options['typeLoader'] = function($name) use ($strategy) {
return $strategy->resolveType($name);
};
}
}
if (isset($options['typeLoader'])) { if (isset($options['typeLoader'])) {
Utils::invariant( Utils::invariant(
is_callable($options['typeLoader']), is_callable($options['typeLoader']),
@ -113,15 +120,6 @@ class SchemaConfig
); );
$config->setTypeLoader($options['typeLoader']); $config->setTypeLoader($options['typeLoader']);
} }
if (isset($options['descriptor'])) {
Utils::invariant(
$options['descriptor'] instanceof Descriptor,
'Schema descriptor must be instance of GraphQL\Type\Descriptor but got: %s',
Utils::getVariableType($options['descriptor'])
);
$config->setDescriptor($options['descriptor']);
}
} }
return $config; return $config;
@ -217,24 +215,6 @@ class SchemaConfig
return $this; return $this;
} }
/**
* @return Descriptor
*/
public function getDescriptor()
{
return $this->descriptor;
}
/**
* @param Descriptor $descriptor
* @return SchemaConfig
*/
public function setDescriptor(Descriptor $descriptor)
{
$this->descriptor = $descriptor;
return $this;
}
/** /**
* @return callable * @return callable
*/ */

View File

@ -106,7 +106,7 @@ class TypeInfo
} }
if (!$type instanceof Type) { if (!$type instanceof Type) {
Warning::warnOnce( Warning::warnOnce(
'One of schema types is not a valid type definition instance. Ignoring it. '. 'One of the schema types is not a valid type definition instance. '.
'Try running $schema->assertValid() to find out the cause of this warning.', 'Try running $schema->assertValid() to find out the cause of this warning.',
Warning::NOT_A_TYPE Warning::NOT_A_TYPE
); );

View File

@ -577,7 +577,7 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
]); ]);
$this->assertFalse($called); $this->assertFalse($called);
$schema->getType('Query'); $schema->getType('Blog');
$this->assertTrue($called); $this->assertTrue($called);
$this->assertEquals([$node], $blog->getInterfaces()); $this->assertEquals([$node], $blog->getInterfaces());