|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* This file is part of Railt package. |
|
4
|
|
|
* |
|
5
|
|
|
* For the full copyright and license information, please view the LICENSE |
|
6
|
|
|
* file that was distributed with this source code. |
|
7
|
|
|
*/ |
|
8
|
|
|
declare(strict_types=1); |
|
9
|
|
|
|
|
10
|
|
|
namespace Railt\SDL\Reflection\Builder\Processable; |
|
11
|
|
|
|
|
12
|
|
|
use Railt\Parser\Ast\NodeInterface; |
|
13
|
|
|
use Railt\SDL\Base\Dependent\Argument\BaseArgumentsContainer; |
|
14
|
|
|
use Railt\SDL\Base\Dependent\BaseArgument; |
|
15
|
|
|
use Railt\SDL\Base\Dependent\BaseField; |
|
16
|
|
|
use Railt\SDL\Base\Dependent\Field\BaseFieldsContainer; |
|
17
|
|
|
use Railt\SDL\Base\Invocations\Directive\BaseDirectivesContainer; |
|
18
|
|
|
use Railt\SDL\Base\Processable\BaseExtend; |
|
19
|
|
|
use Railt\SDL\Contracts\Definitions\Definition; |
|
20
|
|
|
use Railt\SDL\Contracts\Dependent\Argument\HasArguments; |
|
21
|
|
|
use Railt\SDL\Contracts\Dependent\ArgumentDefinition; |
|
22
|
|
|
use Railt\SDL\Contracts\Dependent\Field\HasFields; |
|
23
|
|
|
use Railt\SDL\Contracts\Dependent\FieldDefinition; |
|
24
|
|
|
use Railt\SDL\Contracts\Invocations\Directive\HasDirectives; |
|
25
|
|
|
use Railt\SDL\Contracts\Invocations\DirectiveInvocation; |
|
26
|
|
|
use Railt\SDL\Contracts\Processable\ExtendDefinition; |
|
27
|
|
|
use Railt\SDL\Exceptions\TypeConflictException; |
|
28
|
|
|
use Railt\SDL\Reflection\Builder\DocumentBuilder; |
|
29
|
|
|
use Railt\SDL\Reflection\Builder\Process\Compilable; |
|
30
|
|
|
use Railt\SDL\Reflection\Builder\Process\Compiler; |
|
31
|
|
|
use Railt\SDL\Reflection\Validation\Inheritance; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* Class ExtendBuilder |
|
35
|
|
|
*/ |
|
36
|
|
|
class ExtendBuilder extends BaseExtend implements Compilable |
|
37
|
|
|
{ |
|
38
|
|
|
use Compiler; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* ExtendBuilder constructor. |
|
42
|
|
|
* @param NodeInterface $ast |
|
43
|
|
|
* @param DocumentBuilder $document |
|
44
|
|
|
* @throws TypeConflictException |
|
45
|
|
|
* @throws \Exception |
|
46
|
|
|
*/ |
|
47
|
147 |
|
public function __construct(NodeInterface $ast, DocumentBuilder $document) |
|
48
|
|
|
{ |
|
49
|
147 |
|
$this->boot($ast, $document); |
|
50
|
147 |
|
} |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* @param NodeInterface $ast |
|
54
|
|
|
* @return bool |
|
55
|
|
|
* @throws TypeConflictException |
|
56
|
|
|
* @throws \OutOfBoundsException |
|
57
|
|
|
*/ |
|
58
|
147 |
|
protected function onCompile(NodeInterface $ast): bool |
|
59
|
|
|
{ |
|
60
|
147 |
|
$type = DocumentBuilder::AST_TYPE_MAPPING[$ast->getName()] ?? null; |
|
61
|
|
|
|
|
62
|
147 |
|
if ($type !== null && ! ($type instanceof ExtendDefinition)) { |
|
63
|
|
|
/** @var Compilable $virtualType */ |
|
64
|
147 |
|
$virtualType = new $type($ast, $this->getDocument()); |
|
65
|
|
|
|
|
66
|
147 |
|
$virtualType->compile(); |
|
67
|
|
|
|
|
68
|
147 |
|
$this->applyExtender($virtualType); |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
65 |
|
return false; |
|
72
|
|
|
} |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* @param Definition|Compilable $instance |
|
76
|
|
|
* @return void |
|
77
|
|
|
* @throws TypeConflictException |
|
78
|
|
|
* @throws \OutOfBoundsException |
|
79
|
|
|
*/ |
|
80
|
147 |
|
private function applyExtender(Definition $instance): void |
|
81
|
|
|
{ |
|
82
|
147 |
|
$this->type = $this->load($instance->getName()); |
|
83
|
|
|
|
|
84
|
147 |
|
$this->extend($this->type, $instance); |
|
85
|
65 |
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* @param Definition $original |
|
89
|
|
|
* @param Definition $extend |
|
90
|
|
|
* @return void |
|
91
|
|
|
* @throws TypeConflictException |
|
92
|
|
|
* @throws \OutOfBoundsException |
|
93
|
|
|
*/ |
|
94
|
147 |
|
private function extend(Definition $original, Definition $extend): void |
|
95
|
|
|
{ |
|
96
|
147 |
|
if ($original instanceof HasFields && $extend instanceof HasFields) { |
|
97
|
147 |
|
$this->extendFields($original, $extend); |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
65 |
|
if ($original instanceof HasDirectives && $extend instanceof HasDirectives) { |
|
101
|
65 |
|
$this->extendDirectives($original, $extend); |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
65 |
|
if ($original instanceof HasArguments && $extend instanceof HasArguments) { |
|
105
|
|
|
$this->extendArguments($original, $extend); |
|
106
|
|
|
} |
|
107
|
65 |
|
} |
|
108
|
|
|
|
|
109
|
|
|
/** |
|
110
|
|
|
* @param HasFields $original |
|
111
|
|
|
* @param HasFields $extend |
|
112
|
|
|
* @return void |
|
113
|
|
|
* @throws \OutOfBoundsException |
|
114
|
|
|
* @throws TypeConflictException |
|
115
|
|
|
*/ |
|
116
|
147 |
|
private function extendFields(HasFields $original, HasFields $extend): void |
|
117
|
|
|
{ |
|
118
|
147 |
View Code Duplication |
foreach ($extend->getFields() as $extendField) { |
|
|
|
|
|
|
119
|
147 |
|
if ($original->hasField($extendField->getName())) { |
|
120
|
|
|
/** |
|
121
|
|
|
* Check field type. |
|
122
|
|
|
* @var FieldDefinition $field |
|
123
|
|
|
*/ |
|
124
|
147 |
|
$field = $original->getField($extendField->getName()); |
|
125
|
|
|
|
|
126
|
147 |
|
$this->getValidator(Inheritance::class)->validate($extendField, $field); |
|
|
|
|
|
|
127
|
|
|
|
|
128
|
65 |
|
$this->dataFieldExtender()->call($field, $extendField); |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* Check field arguments |
|
132
|
|
|
*/ |
|
133
|
65 |
|
$this->extendArguments($field, $extendField); |
|
134
|
65 |
|
continue; |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
$callee = function () use ($extendField): void { |
|
138
|
|
|
/** @var BaseFieldsContainer $this */ |
|
139
|
|
|
$this->fields[$extendField->getName()] = $extendField; |
|
140
|
|
|
}; |
|
141
|
|
|
|
|
142
|
|
|
$callee->call($original); |
|
143
|
|
|
} |
|
144
|
65 |
|
} |
|
145
|
|
|
|
|
146
|
|
|
/** |
|
147
|
|
|
* @return \Closure |
|
148
|
|
|
*/ |
|
149
|
|
View Code Duplication |
private function dataFieldExtender(): \Closure |
|
|
|
|
|
|
150
|
|
|
{ |
|
151
|
|
|
/** @var FieldDefinition|BaseField $field */ |
|
152
|
65 |
|
return function (FieldDefinition $field): void { |
|
153
|
|
|
/** @var BaseField $this */ |
|
154
|
|
|
|
|
155
|
|
|
// Extend type |
|
156
|
65 |
|
$this->type = $field->type; |
|
|
|
|
|
|
157
|
|
|
|
|
158
|
|
|
// Extend deprecation reason |
|
159
|
65 |
|
$this->deprecationReason = $field->deprecationReason ?: $this->deprecationReason; |
|
|
|
|
|
|
160
|
|
|
|
|
161
|
|
|
// Extend description |
|
162
|
65 |
|
$this->description = $field->description ?: $this->description; |
|
|
|
|
|
|
163
|
|
|
|
|
164
|
|
|
// NonNull overriding |
|
165
|
65 |
|
$this->isNonNull = $field->isNonNull(); |
|
166
|
65 |
|
$this->isListOfNonNulls = $field->isListOfNonNulls(); |
|
167
|
65 |
|
}; |
|
168
|
|
|
} |
|
169
|
|
|
|
|
170
|
|
|
/** |
|
171
|
|
|
* @param HasArguments|BaseArgumentsContainer|DirectiveInvocation $original |
|
172
|
|
|
* @param HasArguments|DirectiveInvocation $extend |
|
173
|
|
|
* @return void |
|
174
|
|
|
* @throws \OutOfBoundsException |
|
175
|
|
|
* @throws \Railt\SDL\Exceptions\TypeConflictException |
|
176
|
|
|
*/ |
|
177
|
65 |
|
private function extendArguments($original, $extend): void |
|
178
|
|
|
{ |
|
179
|
65 |
View Code Duplication |
foreach ($extend->getArguments() as $extendArgument) { |
|
|
|
|
|
|
180
|
28 |
|
if ($original->hasArgument($extendArgument->getName())) { |
|
|
|
|
|
|
181
|
|
|
/** |
|
182
|
|
|
* Check field type. |
|
183
|
|
|
* @var ArgumentDefinition $argument |
|
184
|
|
|
*/ |
|
185
|
28 |
|
$argument = $original->getArgument($extendArgument->getName()); |
|
|
|
|
|
|
186
|
|
|
|
|
187
|
28 |
|
$this->getValidator(Inheritance::class)->validate($extendArgument, $argument); |
|
|
|
|
|
|
188
|
|
|
|
|
189
|
28 |
|
$this->dataArgumentExtender()->call($argument, $extendArgument); |
|
190
|
|
|
|
|
191
|
28 |
|
continue; |
|
192
|
|
|
} |
|
193
|
|
|
|
|
194
|
|
|
$callee = function () use ($extendArgument): void { |
|
195
|
|
|
/** @var BaseArgumentsContainer $this */ |
|
196
|
|
|
$this->arguments[$extendArgument->getName()] = $extendArgument; |
|
197
|
|
|
}; |
|
198
|
|
|
|
|
199
|
|
|
$callee->call($original); |
|
200
|
|
|
} |
|
201
|
65 |
|
} |
|
202
|
|
|
|
|
203
|
|
|
/** |
|
204
|
|
|
* @return \Closure |
|
205
|
|
|
*/ |
|
206
|
|
View Code Duplication |
private function dataArgumentExtender(): \Closure |
|
|
|
|
|
|
207
|
|
|
{ |
|
208
|
|
|
/** @var ArgumentDefinition|BaseArgument $argument */ |
|
209
|
28 |
|
return function (ArgumentDefinition $argument): void { |
|
210
|
|
|
/** @var BaseArgument $this */ |
|
211
|
|
|
|
|
212
|
|
|
// Extend type |
|
213
|
28 |
|
$this->type = $argument->type; |
|
|
|
|
|
|
214
|
|
|
|
|
215
|
|
|
// Extend deprecation reason |
|
216
|
28 |
|
$this->deprecationReason = $argument->deprecationReason ?: $this->deprecationReason; |
|
|
|
|
|
|
217
|
|
|
|
|
218
|
|
|
// Extend description |
|
219
|
28 |
|
$this->description = $argument->description ?: $this->description; |
|
|
|
|
|
|
220
|
|
|
|
|
221
|
|
|
// NonNull overriding |
|
222
|
28 |
|
$this->isNonNull = $argument->isNonNull(); |
|
223
|
28 |
|
$this->isListOfNonNulls = $argument->isListOfNonNulls(); |
|
224
|
28 |
|
}; |
|
225
|
|
|
} |
|
226
|
|
|
|
|
227
|
|
|
/** |
|
228
|
|
|
* @param HasDirectives|BaseDirectivesContainer $original |
|
229
|
|
|
* @param HasDirectives $extend |
|
230
|
|
|
* @return void |
|
231
|
|
|
* @throws \OutOfBoundsException |
|
232
|
|
|
* @throws TypeConflictException |
|
233
|
|
|
*/ |
|
234
|
65 |
|
private function extendDirectives(HasDirectives $original, HasDirectives $extend): void |
|
235
|
|
|
{ |
|
236
|
65 |
|
foreach ($extend->getDirectives() as $extendDirective) { |
|
237
|
|
|
if ($original->hasDirective($extendDirective->getName())) { |
|
238
|
|
|
/** @var DirectiveInvocation $directive */ |
|
239
|
|
|
$directive = $original->getDirective($extendDirective->getName()); |
|
|
|
|
|
|
240
|
|
|
$this->extendArguments($directive, $extendDirective); |
|
241
|
|
|
continue; |
|
242
|
|
|
} |
|
243
|
|
|
|
|
244
|
|
|
$callee = function () use ($extendDirective): void { |
|
245
|
|
|
/** @var BaseArgumentsContainer $this */ |
|
246
|
|
|
$this->arguments[$extendDirective->getName()] = $extendDirective; |
|
247
|
|
|
}; |
|
248
|
|
|
|
|
249
|
|
|
$callee->call($original); |
|
250
|
|
|
} |
|
251
|
65 |
|
} |
|
252
|
|
|
} |
|
253
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.