1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Fubhy\GraphQL; |
4
|
|
|
|
5
|
|
|
use Fubhy\GraphQL\Type\Definition\FieldArgument; |
6
|
|
|
use Fubhy\GraphQL\Type\Definition\Types\InterfaceType; |
7
|
|
|
use Fubhy\GraphQL\Type\Definition\Types\ModifierInterface; |
8
|
|
|
use Fubhy\GraphQL\Type\Definition\Types\ObjectType; |
9
|
|
|
use Fubhy\GraphQL\Type\Definition\Types\Type; |
10
|
|
|
use Fubhy\GraphQL\Type\Definition\Types\TypeInterface; |
11
|
|
|
use Fubhy\GraphQL\Type\Definition\Types\UnionType; |
12
|
|
|
use Fubhy\GraphQL\Type\Directives\Directive; |
13
|
|
|
use Fubhy\GraphQL\Type\Introspection; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Schema Definition |
17
|
|
|
* |
18
|
|
|
* A Schema is created by supplying the root types of each type of operation, |
19
|
|
|
* query and mutation (optional). A schema definition is then supplied to the |
20
|
|
|
* validator and executor. |
21
|
|
|
*/ |
22
|
|
|
class Schema |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* @var \Fubhy\GraphQL\Type\Definition\Types\ObjectType|null |
26
|
|
|
*/ |
27
|
|
|
protected $mutationType; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var \Fubhy\GraphQL\Type\Definition\Types\ObjectType |
31
|
|
|
*/ |
32
|
|
|
protected $queryType; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var \Fubhy\GraphQL\Type\Directives\DirectiveInterface[] |
36
|
|
|
*/ |
37
|
|
|
protected $directives; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var \Fubhy\GraphQL\Type\Definition\Types\TypeInterface[] |
41
|
|
|
*/ |
42
|
|
|
protected $typeMap; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Constructor. |
46
|
|
|
* |
47
|
|
|
* @param \Fubhy\GraphQL\Type\Definition\Types\TypeInterface $queryType |
48
|
|
|
* @param \Fubhy\GraphQL\Type\Definition\Types\TypeInterface|null $mutationType |
49
|
|
|
*/ |
50
|
309 |
|
public function __construct(TypeInterface $queryType, TypeInterface $mutationType = NULL) |
51
|
|
|
{ |
52
|
309 |
|
$this->queryType = $queryType; |
|
|
|
|
53
|
309 |
|
$this->mutationType = $mutationType; |
|
|
|
|
54
|
309 |
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @return \Fubhy\GraphQL\Type\Definition\Types\ObjectType |
58
|
|
|
*/ |
59
|
297 |
|
public function getQueryType() |
60
|
|
|
{ |
61
|
297 |
|
return $this->queryType; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @return \Fubhy\GraphQL\Type\Definition\Types\ObjectType|null |
66
|
|
|
*/ |
67
|
162 |
|
public function getMutationType() |
68
|
|
|
{ |
69
|
162 |
|
return $this->mutationType; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @param string $name |
74
|
|
|
* |
75
|
|
|
* @return \Fubhy\GraphQL\Type\Directives\DirectiveInterface|null |
76
|
|
|
*/ |
77
|
3 |
|
public function getDirective($name) |
78
|
|
|
{ |
79
|
|
|
foreach ($this->getDirectives() as $directive) { |
80
|
|
|
if ($directive->getName() === $name) { |
81
|
3 |
|
return $directive; |
82
|
|
|
} |
83
|
|
|
} |
84
|
|
|
return NULL; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @return \Fubhy\GraphQL\Type\Directives\DirectiveInterface[] |
89
|
|
|
*/ |
90
|
3 |
|
public function getDirectives() |
91
|
|
|
{ |
92
|
3 |
|
if (!isset($this->directives)) { |
93
|
3 |
|
$include = Directive::includeDirective(); |
94
|
3 |
|
$skip = Directive::skipDirective(); |
95
|
|
|
|
96
|
3 |
|
$this->directives = [ |
|
|
|
|
97
|
3 |
|
$include->getName() => $include, |
98
|
3 |
|
$skip->getName() => $skip, |
99
|
|
|
]; |
100
|
3 |
|
} |
101
|
|
|
|
102
|
3 |
|
return $this->directives; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* @return \Fubhy\GraphQL\Type\Definition\Types\TypeInterface[] |
107
|
|
|
*/ |
108
|
153 |
|
public function getTypeMap() |
109
|
|
|
{ |
110
|
153 |
|
if (!isset($this->typeMap)) { |
111
|
153 |
|
$input = [$this->getQueryType(), $this->getMutationType(), Introspection::schema()]; |
112
|
153 |
|
$this->typeMap = array_reduce($input, [$this, 'typeMapReducer'], []); |
|
|
|
|
113
|
|
|
|
114
|
153 |
|
$this->typeMap['Boolean'] = Type::booleanType(); |
115
|
153 |
|
$this->typeMap['Float'] = Type::floatType(); |
116
|
153 |
|
$this->typeMap['Id'] = Type::idType(); |
117
|
153 |
|
$this->typeMap['Integer'] = Type::intType(); |
118
|
153 |
|
$this->typeMap['String'] = Type::stringType(); |
119
|
153 |
|
} |
120
|
|
|
|
121
|
153 |
|
return $this->typeMap; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* @param string $name |
126
|
|
|
* |
127
|
|
|
* @return \Fubhy\GraphQL\Type\Definition\Types\TypeInterface |
128
|
|
|
*/ |
129
|
147 |
|
public function getType($name) |
130
|
|
|
{ |
131
|
147 |
|
$map = $this->getTypeMap(); |
132
|
147 |
|
return isset($map[$name]) ? $map[$name] : NULL; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @param \Fubhy\GraphQL\Type\Definition\Types\TypeInterface[] $map |
137
|
|
|
* @param mixed $type |
138
|
|
|
* |
139
|
|
|
* @return \Fubhy\GraphQL\Type\Definition\Types\TypeInterface[] |
140
|
|
|
*/ |
141
|
153 |
|
protected function typeMapReducer(array $map, $type) |
142
|
|
|
{ |
143
|
153 |
|
if ($type instanceof ModifierInterface) { |
144
|
153 |
|
return $this->typeMapReducer($map, $type->getWrappedType()); |
145
|
|
|
} |
146
|
|
|
|
147
|
153 |
|
if (!$type instanceof TypeInterface || !empty($map[$type->getName()])) { |
148
|
153 |
|
return $map; |
149
|
|
|
} |
150
|
|
|
|
151
|
153 |
|
$reducedMap = array_merge($map, [$type->getName() => $type]); |
152
|
153 |
|
if ($type instanceof InterfaceType || $type instanceof UnionType) { |
153
|
48 |
|
$reducedMap = array_reduce( |
154
|
48 |
|
$type->getPossibleTypes(), [$this, 'typeMapReducer'], $reducedMap |
155
|
48 |
|
); |
156
|
48 |
|
} |
157
|
|
|
|
158
|
153 |
|
if ($type instanceof ObjectType) { |
159
|
153 |
|
$reducedMap = array_reduce( |
160
|
153 |
|
$type->getInterfaces(), [$this, 'typeMapReducer'], $reducedMap |
161
|
153 |
|
); |
162
|
153 |
|
} |
163
|
|
|
|
164
|
153 |
|
if ($type instanceof ObjectType || $type instanceof InterfaceType) { |
165
|
153 |
|
foreach ($type->getFields() as $fieldName => $field) { |
166
|
153 |
|
$args = $field->getArguments(); |
167
|
153 |
|
$reducedMap = array_reduce(array_map(function (FieldArgument $arg) { |
168
|
153 |
|
return $arg->getType(); |
169
|
153 |
|
}, $args), [$this, 'typeMapReducer'], $reducedMap); |
170
|
|
|
|
171
|
153 |
|
$reducedMap = $this->typeMapReducer($reducedMap, $field->getType()); |
172
|
153 |
|
} |
173
|
153 |
|
} |
174
|
|
|
|
175
|
153 |
|
return $reducedMap; |
176
|
|
|
} |
177
|
|
|
} |
178
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.
Either this assignment is in error or an instanceof check should be added for that assignment.