Test Setup Failed
Push — master ( e8c39a...45e116 )
by Kirill
02:51 queued 12s
created

DirectiveExecutor::applyDefinition()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 15
nc 5
nop 2
dl 0
loc 23
rs 9.4555
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of Railt package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Railt\SDL\Executor\Execution;
13
14
use Railt\SDL\Document;
15
use Phplrt\Visitor\Visitor;
16
use Phplrt\Visitor\Traverser;
17
use Railt\SDL\Ast\DefinitionNode;
18
use Railt\SDL\Ast\Name\IdentifierNode;
19
use Phplrt\Contracts\Ast\NodeInterface;
20
use Railt\SDL\Runtime\DirectiveExecution;
21
use Railt\SDL\Ast\Executable\ArgumentNode;
22
use Railt\SDL\Ast\Executable\DirectiveNode;
23
use Railt\SDL\Ast\Generic\DirectiveCollection;
24
use Railt\SDL\Ast\Extension\TypeExtensionNode;
25
use Railt\SDL\Ast\Definition\TypeDefinitionNode;
26
use GraphQL\Contracts\TypeSystem\FieldInterface;
27
use Railt\SDL\Ast\Extension\SchemaExtensionNode;
28
use Railt\SDL\Ast\Definition\FieldDefinitionNode;
29
use GraphQL\Contracts\TypeSystem\SchemaInterface;
30
use Railt\SDL\Ast\Definition\SchemaDefinitionNode;
31
use Railt\SDL\Ast\Definition\EnumTypeDefinitionNode;
32
use Railt\SDL\Ast\Definition\ArgumentDefinitionNode;
33
use GraphQL\Contracts\TypeSystem\DefinitionInterface;
34
use Railt\SDL\Ast\Definition\EnumValueDefinitionNode;
35
use Railt\SDL\Ast\Definition\ObjectTypeDefinitionNode;
36
use Railt\SDL\Ast\Definition\InputFieldDefinitionNode;
37
use GraphQL\Contracts\TypeSystem\Type\EnumTypeInterface;
38
use GraphQL\Contracts\TypeSystem\Type\NamedTypeInterface;
39
use Railt\SDL\Ast\Definition\InterfaceTypeDefinitionNode;
40
use Railt\SDL\Ast\Definition\InputObjectTypeDefinitionNode;
41
use GraphQL\Contracts\TypeSystem\Common\FieldsAwareInterface;
42
use GraphQL\Contracts\TypeSystem\Type\InputObjectTypeInterface;
43
44
/**
45
 * Class DirectiveExecutor
46
 */
47
class DirectiveExecutor extends Visitor
48
{
49
    /**
50
     * @var Document
51
     */
52
    private Document $document;
53
54
    /**
55
     * DirectiveExecutor constructor.
56
     *
57
     * @param Document $document
58
     */
59
    public function __construct(Document $document)
60
    {
61
        $this->document = $document;
62
    }
63
64
    /**
65
     * @param NodeInterface $node
66
     * @return int|null
67
     */
68
    public function enter(NodeInterface $node): ?int
69
    {
70
        switch (true) {
71
            case $node instanceof TypeDefinitionNode:
72
            case $node instanceof TypeExtensionNode:
73
                $this->applyDefinition($this->getType($node->name), $node);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Phplrt\Contracts\Ast\NodeInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
74
                break;
75
76
            case $node instanceof SchemaDefinitionNode:
77
            case $node instanceof SchemaExtensionNode:
78
                $this->apply($this->getSchema(), $node->directives);
0 ignored issues
show
Bug introduced by
Accessing directives on the interface Phplrt\Contracts\Ast\NodeInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
79
                break;
80
        }
81
82
        return Traverser::DONT_TRAVERSE_CHILDREN;
83
    }
84
85
    /**
86
     * @param NamedTypeInterface $context
87
     * @param TypeDefinitionNode|TypeExtensionNode|DefinitionNode $node
88
     * @return void
89
     */
90
    private function applyDefinition(NamedTypeInterface $context, DefinitionNode $node): void
91
    {
92
        $this->apply($context, $node->directives);
93
94
        switch (true) {
95
            case $node instanceof ObjectTypeDefinitionNode:
96
            case $node instanceof InterfaceTypeDefinitionNode:
97
                \assert($context instanceof FieldsAwareInterface);
98
99
                $this->applyFields($context, $node);
100
                break;
101
102
            case $node instanceof InputObjectTypeDefinitionNode:
103
                \assert($context instanceof InputObjectTypeInterface);
104
105
                $this->applyInputFields($context, $node);
106
                break;
107
108
            case $node instanceof EnumTypeDefinitionNode:
109
                \assert($context instanceof EnumTypeInterface);
110
111
                $this->applyEnumValues($context, $node);
112
                break;
113
        }
114
    }
115
116
    /**
117
     * @param DefinitionInterface $context
118
     * @param DirectiveCollection|null $collection
119
     * @return iterable|DirectiveExecution[]
120
     */
121
    protected function apply(DefinitionInterface $context, ?DirectiveCollection $collection): iterable
122
    {
123
        $result = [];
124
125
        if ($collection && $collection->count()) {
126
            foreach ($collection as $node) {
127
                $result[] = $this->add($context, $node);
128
            }
129
        }
130
131
        return $result;
132
    }
133
134
    /**
135
     * @param DefinitionInterface $context
136
     * @param DirectiveNode $directive
137
     * @return DirectiveExecution
138
     */
139
    private function add(DefinitionInterface $context, DirectiveNode $directive): DirectiveExecution
140
    {
141
        $definition = $this->document->getDirective($directive->name->value);
142
143
        $execution = new DirectiveExecution($definition, $context, $this->fetchArguments($directive));
0 ignored issues
show
Bug introduced by
It seems like $definition can also be of type null; however, parameter $directive of Railt\SDL\Runtime\Direct...xecution::__construct() does only seem to accept GraphQL\Contracts\TypeSystem\DirectiveInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
        $execution = new DirectiveExecution(/** @scrutinizer ignore-type */ $definition, $context, $this->fetchArguments($directive));
Loading history...
144
145
        $this->document->addExecution($execution);
146
147
        return $execution;
148
    }
149
150
    /**
151
     * @param DirectiveNode $node
152
     * @return array
153
     */
154
    private function fetchArguments(DirectiveNode $node): array
155
    {
156
        $result = [];
157
158
        /** @var ArgumentNode $argument */
159
        foreach ($node->arguments ?? [] as $argument) {
160
            $result[$argument->name->value] = $argument->value->toNative();
161
        }
162
163
        return $result;
164
    }
165
166
    /**
167
     * @param FieldsAwareInterface $context
168
     * @param TypeDefinitionNode $node
169
     * @return void
170
     */
171
    private function applyFields(FieldsAwareInterface $context, TypeDefinitionNode $node): void
172
    {
173
        /** @var FieldDefinitionNode $field */
174
        foreach ($node->fields ?? [] as $field) {
175
            /** @var FieldInterface $definition */
176
            $this->apply($definition = $context->getField($field->name->value), $field->directives);
0 ignored issues
show
Bug introduced by
It seems like $definition = $context->...ld($field->name->value) can also be of type null; however, parameter $context of Railt\SDL\Executor\Execu...ectiveExecutor::apply() does only seem to accept GraphQL\Contracts\TypeSystem\DefinitionInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
            $this->apply(/** @scrutinizer ignore-type */ $definition = $context->getField($field->name->value), $field->directives);
Loading history...
177
178
            /** @var ArgumentDefinitionNode $argument */
179
            foreach ($field->arguments ?? [] as $argument) {
180
                $this->apply($definition->getArgument($argument->name->value), $argument->directives);
181
            }
182
        }
183
    }
184
185
    /**
186
     * @param InputObjectTypeInterface $context
187
     * @param InputObjectTypeDefinitionNode $node
188
     * @return void
189
     */
190
    private function applyInputFields(InputObjectTypeInterface $context, InputObjectTypeDefinitionNode $node): void
191
    {
192
        /** @var InputFieldDefinitionNode $field */
193
        foreach ($node->fields ?? [] as $field) {
194
            $this->apply($context->getField($field->name->value), $field->directives);
0 ignored issues
show
Bug introduced by
It seems like $context->getField($field->name->value) can also be of type null; however, parameter $context of Railt\SDL\Executor\Execu...ectiveExecutor::apply() does only seem to accept GraphQL\Contracts\TypeSystem\DefinitionInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

194
            $this->apply(/** @scrutinizer ignore-type */ $context->getField($field->name->value), $field->directives);
Loading history...
195
        }
196
    }
197
198
    /**
199
     * @param EnumTypeInterface $context
200
     * @param EnumTypeDefinitionNode $node
201
     * @return void
202
     */
203
    private function applyEnumValues(EnumTypeInterface $context, EnumTypeDefinitionNode $node): void
204
    {
205
        /** @var EnumValueDefinitionNode $value */
206
        foreach ($node->values ?? [] as $value) {
207
            $this->apply($context->getValue($value->name->value), $value->directives);
0 ignored issues
show
Bug introduced by
It seems like $context->getValue($value->name->value) can also be of type null; however, parameter $context of Railt\SDL\Executor\Execu...ectiveExecutor::apply() does only seem to accept GraphQL\Contracts\TypeSystem\DefinitionInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

207
            $this->apply(/** @scrutinizer ignore-type */ $context->getValue($value->name->value), $value->directives);
Loading history...
208
        }
209
    }
210
211
    /**
212
     * @param IdentifierNode $id
213
     * @return NamedTypeInterface
214
     */
215
    protected function getType(IdentifierNode $id): NamedTypeInterface
216
    {
217
        $context = $this->document->getType($id->value);
218
219
        \assert($context instanceof NamedTypeInterface);
220
221
        return $context;
222
    }
223
224
    /**
225
     * @return SchemaInterface
226
     */
227
    protected function getSchema(): SchemaInterface
228
    {
229
        $context = $this->document->getSchema();
230
231
        \assert($context instanceof SchemaInterface);
232
233
        return $context;
234
    }
235
}
236