Passed
Pull Request — master (#23)
by Christoffer
02:07 queued 19s
created

AcceptVisitorTrait::accept()   D

Complexity

Conditions 9
Paths 11

Size

Total Lines 38
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 18
nc 11
nop 3
dl 0
loc 38
rs 4.909
c 0
b 0
f 0
1
<?php
2
3
namespace Digia\GraphQL\Language\AST\Visitor;
4
5
use Digia\GraphQL\Language\AST\Node\NodeInterface;
6
use Digia\GraphQL\Util\SerializationInterface;
7
8
trait AcceptVisitorTrait
9
{
10
11
    /**
12
     * @var VisitorInterface
13
     */
14
    protected $visitor;
15
16
    /**
17
     * @var array
18
     */
19
    protected $path;
20
21
    /**
22
     * @param VisitorInterface $visitor
23
     * @param string|null $key
24
     * @param array $path
25
     * @return NodeInterface|SerializationInterface|null
26
     */
27
    public function accept(VisitorInterface $visitor, ?string $key = null, array $path = []): ?NodeInterface
28
    {
29
        $this->visitor = $visitor;
30
        $this->path = $path;
31
32
        // TODO: Benchmark
33
        /** @var NodeInterface|AcceptVisitorTrait $newNode */
34
        $newNode = clone $this;
35
36
        if (null === ($newNode = $visitor->enterNode($newNode, $key, $this->path))) {
0 ignored issues
show
Bug introduced by
It seems like $newNode can also be of type Digia\GraphQL\Language\A...itor\AcceptVisitorTrait; however, parameter $node of Digia\GraphQL\Language\A...rInterface::enterNode() does only seem to accept Digia\GraphQL\Language\AST\Node\NodeInterface, 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

36
        if (null === ($newNode = $visitor->enterNode(/** @scrutinizer ignore-type */ $newNode, $key, $this->path))) {
Loading history...
37
            return null;
38
        }
39
40
        foreach (self::$kindToNodesToVisitMap[$this->kind] as $name) {
0 ignored issues
show
Bug Best Practice introduced by
The property kind does not exist on Digia\GraphQL\Language\A...itor\AcceptVisitorTrait. Did you maybe forget to declare it?
Loading history...
41
            $nodeOrNodes = $this->getNodeOrNodes($name);
42
43
            if (null === $nodeOrNodes || empty($nodeOrNodes)) {
44
                continue;
45
            }
46
47
            $newNodeOrNodes = $this->visitNodeOrNodes($nodeOrNodes, $name);
48
49
            if (null === $newNodeOrNodes || empty($newNodeOrNodes)) {
50
                continue;
51
            }
52
53
            $setter = 'set' . ucfirst($name);
54
55
            if (method_exists($newNode, $setter)) {
56
                $newNode->{$setter}($newNodeOrNodes);
57
            }
58
        }
59
60
        if (null === ($newNode = $visitor->leaveNode($newNode, $key, $this->path))) {
61
            return null;
62
        }
63
64
        return $newNode;
65
    }
66
67
    /**
68
     * @param string $key
69
     * @return array|NodeInterface|NodeInterface[]|null
70
     */
71
    protected function getNodeOrNodes(string $key)
72
    {
73
        return $this->{$key};
74
    }
75
76
    /**
77
     * @param $nodeOrNodes
78
     * @param string $key
79
     * @return array|NodeInterface|NodeInterface[]|null
80
     */
81
    protected function visitNodeOrNodes($nodeOrNodes, string $key)
82
    {
83
        return \is_array($nodeOrNodes) ? $this->visitNodes($nodeOrNodes, $key) : $this->visitNode($nodeOrNodes, $key);
84
    }
85
86
    /**
87
     * @param NodeInterface[] $nodes
88
     * @param string $key
89
     * @return NodeInterface[]
90
     */
91
    protected function visitNodes(array $nodes, string $key): array
92
    {
93
        $this->addOneToPath($key);
94
95
        $index = 0;
96
        $newNodes = [];
97
98
        foreach ($nodes as $node) {
99
            if (null !== ($newNode = $this->visitNode($node, $index))) {
100
                $newNodes[$index] = $newNode;
101
                $index++;
102
            }
103
        }
104
105
        $this->removeOneFromPath();
106
107
        return $newNodes;
108
    }
109
110
    /**
111
     * @param NodeInterface|AcceptVisitorTrait $node
112
     * @param string $key
113
     * @return NodeInterface|null
114
     */
115
    protected function visitNode(NodeInterface $node, string $key): ?NodeInterface
116
    {
117
        $this->addOneToPath($key);
118
119
        $newNode = $node->accept($this->visitor, $key, $this->path);
0 ignored issues
show
Bug introduced by
The method accept() does not exist on Digia\GraphQL\Language\AST\Node\NodeInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Digia\GraphQL\Language\A...DefinitionNodeInterface or Digia\GraphQL\Language\A...eExtensionNodeInterface or Digia\GraphQL\Language\AST\Node\ValueNodeInterface or Digia\GraphQL\Language\A...\SelectionNodeInterface or Digia\GraphQL\Language\AST\Node\TypeNodeInterface or Digia\GraphQL\Language\A...DefinitionNodeInterface or Digia\GraphQL\Language\A...DefinitionNodeInterface or Digia\GraphQL\Language\A...DefinitionNodeInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

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

119
        /** @scrutinizer ignore-call */ 
120
        $newNode = $node->accept($this->visitor, $key, $this->path);
Loading history...
120
121
        $this->removeOneFromPath();
122
123
        return $newNode;
124
    }
125
126
    /**
127
     * Appends a key to the path.
128
     * @param string $key
129
     */
130
    protected function addOneToPath(string $key)
131
    {
132
        $this->path[] = $key;
133
    }
134
135
    /**
136
     * Removes the last item from the path.
137
     */
138
    protected function removeOneFromPath()
139
    {
140
        $this->path = \array_slice($this->path, 0, count($this->path) - 1);
141
    }
142
143
    /**
144
     * @var array
145
     */
146
    protected static $kindToNodesToVisitMap = [
147
        'Name' => [],
148
149
        'Document' => ['definitions'],
150
        'OperationDefinition' => [
151
            'name',
152
            'variableDefinitions',
153
            'directives',
154
            'selectionSet',
155
        ],
156
        'VariableDefinition' => ['variable', 'type', 'defaultValue'],
157
        'Variable' => ['name'],
158
        'SelectionSet' => ['selections'],
159
        'Field' => ['alias', 'name', 'arguments', 'directives', 'selectionSet'],
160
        'Argument' => ['name', 'value'],
161
162
        'FragmentSpread' => ['name', 'directives'],
163
        'InlineFragment' => ['typeCondition', 'directives', 'selectionSet'],
164
        'FragmentDefinition' => [
165
            'name',
166
            // Note: fragment variable definitions are experimental and may be changed or removed in the future.
167
            'variableDefinitions',
168
            'typeCondition',
169
            'directives',
170
            'selectionSet',
171
        ],
172
173
        'IntValue' => [],
174
        'FloatValue' => [],
175
        'StringValue' => [],
176
        'BooleanValue' => [],
177
        'NullValue' => [],
178
        'EnumValue' => [],
179
        'ListValue' => ['values'],
180
        'ObjectValue' => ['fields'],
181
        'ObjectField' => ['name', 'value'],
182
183
        'Directive' => ['name', 'arguments'],
184
185
        'NamedType' => ['name'],
186
        'ListType' => ['type'],
187
        'NonNullType' => ['type'],
188
189
        'SchemaDefinition' => ['directives', 'operationTypes'],
190
        'OperationTypeDefinition' => ['type'],
191
192
        'ScalarTypeDefinition' => ['description', 'name', 'directives'],
193
        'ObjectTypeDefinition' => [
194
            'description',
195
            'name',
196
            'interfaces',
197
            'directives',
198
            'fields',
199
        ],
200
        'FieldDefinition' => ['description', 'name', 'arguments', 'type', 'directives'],
201
        'InputValueDefinition' => [
202
            'description',
203
            'name',
204
            'type',
205
            'defaultValue',
206
            'directives',
207
        ],
208
        'InterfaceTypeDefinition' => ['description', 'name', 'directives', 'fields'],
209
        'UnionTypeDefinition' => ['description', 'name', 'directives', 'types'],
210
        'EnumTypeDefinition' => ['description', 'name', 'directives', 'values'],
211
        'EnumValueDefinition' => ['description', 'name', 'directives'],
212
        'InputObjectTypeDefinition' => ['description', 'name', 'directives', 'fields'],
213
214
        'ScalarTypeExtension' => ['name', 'directives'],
215
        'ObjectTypeExtension' => ['name', 'interfaces', 'directives', 'fields'],
216
        'InterfaceTypeExtension' => ['name', 'directives', 'fields'],
217
        'UnionTypeExtension' => ['name', 'directives', 'types'],
218
        'EnumTypeExtension' => ['name', 'directives', 'values'],
219
        'InputObjectTypeExtension' => ['name', 'directives', 'fields'],
220
221
        'DirectiveDefinition' => ['description', 'name', 'arguments', 'locations'],
222
    ];
223
}
224