2015-07-15 20:05:46 +03:00
|
|
|
<?php
|
|
|
|
namespace GraphQL;
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
use GraphQL\Type\Definition\AbstractType;
|
2015-07-15 20:05:46 +03:00
|
|
|
use GraphQL\Type\Definition\Directive;
|
|
|
|
use GraphQL\Type\Definition\ObjectType;
|
|
|
|
use GraphQL\Type\Definition\Type;
|
2016-12-10 03:49:41 +03:00
|
|
|
use GraphQL\Type\EagerResolution;
|
2015-07-15 20:05:46 +03:00
|
|
|
use GraphQL\Type\Introspection;
|
2016-12-10 03:49:41 +03:00
|
|
|
use GraphQL\Type\Resolution;
|
2015-07-15 20:05:46 +03:00
|
|
|
|
2016-10-17 14:33:47 +03:00
|
|
|
/**
|
2016-10-18 21:34:46 +03:00
|
|
|
* Schema Definition
|
|
|
|
*
|
|
|
|
* A Schema is created by supplying the root types of each type of operation:
|
|
|
|
* query, mutation (optional) and subscription (optional). A schema definition is
|
|
|
|
* then supplied to the validator and executor.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* $schema = new GraphQL\Schema([
|
|
|
|
* 'query' => $MyAppQueryRootType,
|
|
|
|
* 'mutation' => $MyAppMutationRootType,
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* Note: If an array of `directives` are provided to GraphQL\Schema, that will be
|
|
|
|
* the exact list of directives represented and allowed. If `directives` is not
|
|
|
|
* provided then a default set of the specified directives (e.g. @include and
|
|
|
|
* @skip) will be used. If you wish to provide *additional* directives to these
|
|
|
|
* specified directives, you must explicitly declare them. Example:
|
|
|
|
*
|
|
|
|
* $mySchema = new GraphQL\Schema([
|
|
|
|
* ...
|
|
|
|
* 'directives' => array_merge(GraphQL::getInternalDirectives(), [ $myCustomDirective ]),
|
|
|
|
* ])
|
|
|
|
*
|
2016-10-17 14:33:47 +03:00
|
|
|
* @package GraphQL
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
class Schema
|
|
|
|
{
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
2016-12-10 03:49:41 +03:00
|
|
|
* @var array
|
2016-04-25 00:57:09 +03:00
|
|
|
*/
|
2016-12-10 03:49:41 +03:00
|
|
|
private $config;
|
2015-07-15 20:05:46 +03:00
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
2016-12-10 03:49:41 +03:00
|
|
|
* @var array<string, array<string, boolean>>
|
2016-04-25 00:57:09 +03:00
|
|
|
*/
|
2016-12-10 03:49:41 +03:00
|
|
|
private $possibleTypeMap = [];
|
2015-07-15 20:05:46 +03:00
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
2016-12-10 03:49:41 +03:00
|
|
|
* @var Resolution
|
2016-04-25 00:57:09 +03:00
|
|
|
*/
|
2016-12-10 03:49:41 +03:00
|
|
|
private $typeResolutionStrategy;
|
2015-07-15 20:05:46 +03:00
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
2016-12-10 03:49:41 +03:00
|
|
|
* Required for `getTypeMap()` and `getDescriptor()` methods
|
|
|
|
*
|
|
|
|
* @var EagerResolution
|
2016-04-25 00:57:09 +03:00
|
|
|
*/
|
2016-12-10 03:49:41 +03:00
|
|
|
private $eagerTypeResolutionStrategy;
|
2016-04-25 00:57:09 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Schema constructor.
|
|
|
|
* @param array $config
|
|
|
|
*/
|
|
|
|
public function __construct($config = null)
|
2015-07-15 20:05:46 +03:00
|
|
|
{
|
2016-04-25 00:57:09 +03:00
|
|
|
if (func_num_args() > 1 || $config instanceof Type) {
|
|
|
|
trigger_error(
|
|
|
|
'GraphQL\Schema constructor expects config object now instead of types passed as arguments. '.
|
|
|
|
'See https://github.com/webonyx/graphql-php/issues/36',
|
|
|
|
E_USER_DEPRECATED
|
|
|
|
);
|
2016-09-15 13:32:54 +03:00
|
|
|
list($queryType, $mutationType, $subscriptionType) = func_get_args() + [null, null, null];
|
2016-04-25 00:57:09 +03:00
|
|
|
|
|
|
|
$config = [
|
|
|
|
'query' => $queryType,
|
|
|
|
'mutation' => $mutationType,
|
|
|
|
'subscription' => $subscriptionType
|
|
|
|
];
|
|
|
|
}
|
2015-08-17 17:01:55 +03:00
|
|
|
|
2016-12-10 03:49:41 +03:00
|
|
|
$config += [
|
|
|
|
'query' => null,
|
|
|
|
'mutation' => null,
|
|
|
|
'subscription' => null,
|
|
|
|
'types' => [],
|
|
|
|
'directives' => null,
|
|
|
|
'typeResolution' => null
|
|
|
|
];
|
|
|
|
|
2016-10-17 14:33:47 +03:00
|
|
|
$this->init($config);
|
2016-04-25 00:57:09 +03:00
|
|
|
}
|
|
|
|
|
2016-10-17 14:33:47 +03:00
|
|
|
/**
|
|
|
|
* @param array $config
|
|
|
|
*/
|
2016-11-25 12:35:46 +03:00
|
|
|
private function init(array $config)
|
2016-04-25 00:57:09 +03:00
|
|
|
{
|
2016-05-02 00:42:05 +03:00
|
|
|
Utils::invariant(
|
|
|
|
$config['query'] instanceof ObjectType,
|
|
|
|
"Schema query must be Object Type but got: " . Utils::getVariableType($config['query'])
|
|
|
|
);
|
|
|
|
|
|
|
|
Utils::invariant(
|
|
|
|
!$config['mutation'] || $config['mutation'] instanceof ObjectType,
|
|
|
|
"Schema mutation must be Object Type if provided but got: " . Utils::getVariableType($config['mutation'])
|
|
|
|
);
|
|
|
|
|
|
|
|
Utils::invariant(
|
|
|
|
!$config['subscription'] || $config['subscription'] instanceof ObjectType,
|
|
|
|
"Schema subscription must be Object Type if provided but got: " . Utils::getVariableType($config['subscription'])
|
|
|
|
);
|
2016-04-25 00:57:09 +03:00
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
Utils::invariant(
|
|
|
|
!$config['types'] || is_array($config['types']),
|
|
|
|
"Schema types must be Array if provided but got: " . Utils::getVariableType($config['types'])
|
|
|
|
);
|
|
|
|
|
|
|
|
Utils::invariant(
|
|
|
|
!$config['directives'] || (is_array($config['directives']) && Utils::every($config['directives'], function($d) {return $d instanceof Directive;})),
|
|
|
|
"Schema directives must be Directive[] if provided but got " . Utils::getVariableType($config['directives'])
|
|
|
|
);
|
|
|
|
|
2016-12-10 03:49:41 +03:00
|
|
|
Utils::invariant(
|
|
|
|
!$config['typeResolution'] || $config['typeResolution'] instanceof Resolution,
|
|
|
|
"Type resolution strategy is expected to be instance of GraphQL\\Type\\Resolution, but got " .
|
|
|
|
Utils::getVariableType($config['typeResolution'])
|
|
|
|
);
|
2015-08-17 17:01:55 +03:00
|
|
|
|
2016-12-10 03:49:41 +03:00
|
|
|
$this->config = $config;
|
|
|
|
$this->typeResolutionStrategy = $config['typeResolution'] ?: $this->getEagerTypeResolutionStrategy();
|
2015-08-17 17:01:55 +03:00
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @return ObjectType
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function getQueryType()
|
|
|
|
{
|
2016-12-10 03:49:41 +03:00
|
|
|
return $this->config['query'];
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @return ObjectType
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function getMutationType()
|
|
|
|
{
|
2016-12-10 03:49:41 +03:00
|
|
|
return $this->config['mutation'];
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @return ObjectType
|
|
|
|
*/
|
2015-12-21 02:29:29 +03:00
|
|
|
public function getSubscriptionType()
|
|
|
|
{
|
2016-12-10 03:49:41 +03:00
|
|
|
return $this->config['subscription'];
|
2015-12-21 02:29:29 +03:00
|
|
|
}
|
|
|
|
|
2015-07-15 20:05:46 +03:00
|
|
|
/**
|
2016-12-10 03:49:41 +03:00
|
|
|
* Returns full map of types in this schema.
|
|
|
|
* Note: internally it will eager-load all types using GraphQL\Type\EagerResolution strategy
|
|
|
|
*
|
|
|
|
* @return Type[]
|
2015-07-15 20:05:46 +03:00
|
|
|
*/
|
2016-04-25 00:57:09 +03:00
|
|
|
public function getTypeMap()
|
2015-07-15 20:05:46 +03:00
|
|
|
{
|
2016-12-10 03:49:41 +03:00
|
|
|
return $this->getEagerTypeResolutionStrategy()->getTypeMap();
|
2016-04-25 00:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $name
|
|
|
|
* @return Type
|
|
|
|
*/
|
|
|
|
public function getType($name)
|
|
|
|
{
|
2016-12-10 03:49:41 +03:00
|
|
|
return $this->typeResolutionStrategy->resolveType($name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns serializable schema representation suitable for GraphQL\Type\LazyResolution
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getDescriptor()
|
|
|
|
{
|
|
|
|
return $this->getEagerTypeResolutionStrategy()->getDescriptor();
|
2016-04-25 00:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param AbstractType $abstractType
|
|
|
|
* @return ObjectType[]
|
|
|
|
*/
|
|
|
|
public function getPossibleTypes(AbstractType $abstractType)
|
|
|
|
{
|
2016-12-10 03:49:41 +03:00
|
|
|
return $this->typeResolutionStrategy->resolvePossibleTypes($abstractType);
|
2016-04-25 00:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param AbstractType $abstractType
|
|
|
|
* @param ObjectType $possibleType
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function isPossibleType(AbstractType $abstractType, ObjectType $possibleType)
|
|
|
|
{
|
2016-10-17 14:33:47 +03:00
|
|
|
if (!isset($this->possibleTypeMap[$abstractType->name])) {
|
2016-04-25 00:57:09 +03:00
|
|
|
$tmp = [];
|
|
|
|
foreach ($this->getPossibleTypes($abstractType) as $type) {
|
2016-12-10 03:49:41 +03:00
|
|
|
$tmp[$type->name] = 1;
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
2016-10-18 21:34:46 +03:00
|
|
|
|
|
|
|
Utils::invariant(
|
|
|
|
!empty($tmp),
|
|
|
|
'Could not find possible implementing types for $%s ' .
|
|
|
|
'in schema. Check that schema.types is defined and is an array of ' .
|
|
|
|
'all possible types in the schema.',
|
|
|
|
$abstractType->name
|
|
|
|
);
|
|
|
|
|
2016-10-17 14:33:47 +03:00
|
|
|
$this->possibleTypeMap[$abstractType->name] = $tmp;
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
2016-10-17 14:33:47 +03:00
|
|
|
return !empty($this->possibleTypeMap[$abstractType->name][$possibleType->name]);
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-04-25 00:57:09 +03:00
|
|
|
* @return Directive[]
|
2015-07-15 20:05:46 +03:00
|
|
|
*/
|
|
|
|
public function getDirectives()
|
|
|
|
{
|
2016-12-10 03:49:41 +03:00
|
|
|
return isset($this->config['directives']) ? $this->config['directives'] : GraphQL::getInternalDirectives();
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @param $name
|
|
|
|
* @return Directive
|
|
|
|
*/
|
|
|
|
public function getDirective($name)
|
2015-07-15 20:05:46 +03:00
|
|
|
{
|
2016-04-25 00:57:09 +03:00
|
|
|
foreach ($this->getDirectives() as $directive) {
|
|
|
|
if ($directive->name === $name) {
|
|
|
|
return $directive;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-12-10 03:49:41 +03:00
|
|
|
private function getEagerTypeResolutionStrategy()
|
2015-07-15 20:05:46 +03:00
|
|
|
{
|
2016-12-10 03:49:41 +03:00
|
|
|
if (!$this->eagerTypeResolutionStrategy) {
|
|
|
|
if ($this->typeResolutionStrategy instanceof EagerResolution) {
|
|
|
|
$this->eagerTypeResolutionStrategy = $this->typeResolutionStrategy;
|
|
|
|
} else {
|
|
|
|
// Build type map now to detect any errors within this schema.
|
|
|
|
$initialTypes = [
|
|
|
|
$this->config['query'],
|
|
|
|
$this->config['mutation'],
|
|
|
|
$this->config['subscription'],
|
|
|
|
Introspection::_schema()
|
|
|
|
];
|
|
|
|
if (!empty($this->config['types'])) {
|
|
|
|
$initialTypes = array_merge($initialTypes, $this->config['types']);
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
2016-12-10 03:49:41 +03:00
|
|
|
$this->eagerTypeResolutionStrategy = new EagerResolution($initialTypes);
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
}
|
2016-12-10 03:49:41 +03:00
|
|
|
return $this->eagerTypeResolutionStrategy;
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
}
|