Passed
Pull Request — master (#23)
by Christoffer
01:53
created

AcceptVisitorTrait::removeOneFromPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
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
            $newNode->{$setter}($newNodeOrNodes);
55
        }
56
57
        if (null === ($newNode = $visitor->leaveNode($newNode, $key, $this->path))) {
58
            return null;
59
        }
60
61
        return $newNode;
62
    }
63
64
    /**
65
     * @param string $key
66
     * @return array|NodeInterface|NodeInterface[]|null
67
     */
68
    protected function getNodeOrNodes(string $key)
69
    {
70
        return $this->{$key};
71
    }
72
73
    /**
74
     * @param $nodeOrNodes
75
     * @param string $key
76
     * @return array|NodeInterface|NodeInterface[]|null
77
     */
78
    protected function visitNodeOrNodes($nodeOrNodes, string $key)
79
    {
80
        return \is_array($nodeOrNodes) ? $this->visitNodes($nodeOrNodes, $key) : $this->visitNode($nodeOrNodes, $key);
81
    }
82
83
    /**
84
     * @param NodeInterface[] $nodes
85
     * @param string $key
86
     * @return NodeInterface[]
87
     */
88
    protected function visitNodes(array $nodes, string $key): array
89
    {
90
        $this->addOneToPath($key);
91
92
        $index = 0;
93
        $newNodes = [];
94
95
        foreach ($nodes as $node) {
96
            if (null !== ($newNode = $this->visitNode($node, $index))) {
97
                $newNodes[$index] = $newNode;
98
                $index++;
99
            }
100
        }
101
102
        $this->removeOneFromPath();
103
104
        return $newNodes;
105
    }
106
107
    /**
108
     * @param NodeInterface|AcceptVisitorTrait $node
109
     * @param string $key
110
     * @return NodeInterface|null
111
     */
112
    protected function visitNode(NodeInterface $node, string $key): ?NodeInterface
113
    {
114
        $this->addOneToPath($key);
115
116
        $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

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