Failed Conditions
Push — new-parser-ast-metadata ( cfd696...cc9a79 )
by Michael
02:12
created

bler/Assembler.php$0   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 215
Duplicated Lines 0 %

Test Coverage

Coverage 93.14%

Importance

Changes 0
Metric Value
wmc 25
dl 0
loc 215
rs 10
c 0
b 0
f 0
ccs 95
cts 102
cp 0.9314
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Annotations\Assembler;
6
7
use Doctrine\Annotations\Assembler\Acceptor\ReferenceAcceptor;
8
use Doctrine\Annotations\Constructor\Constructor;
9
use Doctrine\Annotations\Metadata\MetadataCollection;
10
use Doctrine\Annotations\Metadata\Reflection\ClassReflectionProvider;
11
use Doctrine\Annotations\Parser\Ast\Annotation;
12
use Doctrine\Annotations\Parser\Ast\Annotations;
13
use Doctrine\Annotations\Parser\Ast\ClassConstantFetch;
14
use Doctrine\Annotations\Parser\Ast\Collection\ListCollection;
15
use Doctrine\Annotations\Parser\Ast\Collection\MapCollection;
16
use Doctrine\Annotations\Parser\Ast\ConstantFetch;
17
use Doctrine\Annotations\Parser\Ast\Pair;
18
use Doctrine\Annotations\Parser\Ast\Parameter\NamedParameter;
19
use Doctrine\Annotations\Parser\Ast\Parameter\UnnamedParameter;
20
use Doctrine\Annotations\Parser\Ast\Parameters;
21
use Doctrine\Annotations\Parser\Ast\Reference;
22
use Doctrine\Annotations\Parser\Ast\Scalar\BooleanScalar;
23
use Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar;
24
use Doctrine\Annotations\Parser\Ast\Scalar\Identifier;
25
use Doctrine\Annotations\Parser\Ast\Scalar\IntegerScalar;
26
use Doctrine\Annotations\Parser\Ast\Scalar\NullScalar;
27
use Doctrine\Annotations\Parser\Ast\Scalar\StringScalar;
28
use Doctrine\Annotations\Parser\Reference\ReferenceResolver;
29
use Doctrine\Annotations\Parser\Scope;
30
use Doctrine\Annotations\Parser\Visitor\Visitor;
31
use SplObjectStorage;
32
use SplStack;
33
use function array_key_exists;
34
use function assert;
35
use function constant;
36
use function strtolower;
37
38
final class Assembler
39
{
40
    /** @var MetadataCollection */
41
    private $metadataCollection;
42
43
    /** @var ReferenceResolver */
44
    private $referenceResolver;
45
46
    /** @var Constructor */
47
    private $constructor;
48
49
    /** @var ClassReflectionProvider */
50
    private $classReflectionProvider;
51
52
    /** @var ReferenceAcceptor */
53
    private $referenceAcceptor;
54
55 9
    public function __construct(
56
        MetadataCollection $metadataCollection,
57
        ReferenceResolver $referenceResolver,
58
        Constructor $constructor,
59
        ClassReflectionProvider $classReflectionProvider,
60
        ReferenceAcceptor $referenceAcceptor
61
    ) {
62 9
        $this->metadataCollection      = $metadataCollection;
63 9
        $this->referenceResolver       = $referenceResolver;
64 9
        $this->constructor             = $constructor;
65 9
        $this->classReflectionProvider = $classReflectionProvider;
66 9
        $this->referenceAcceptor       = $referenceAcceptor;
67 9
    }
68
69
    /**
70
     * @return object[]
71
     */
72 9
    public function collect(Annotations $node, Scope $scope) : iterable
73
    {
74 9
        $storage = new SplObjectStorage();
75
76 9
        $node->dispatch($this->createInternalVisitor($storage, $scope));
77
78 9
        return yield from $storage;
0 ignored issues
show
Bug Best Practice introduced by
The expression YieldFromNode returns the type Generator which is incompatible with the documented return type array<mixed,object>.
Loading history...
79
    }
80
81
    private function createInternalVisitor(SplObjectStorage $storage, Scope $scope) : Visitor
82
    {
83
        return new class (
84
            $this->metadataCollection,
85
            $this->referenceResolver,
86
            $this->constructor,
87
            $this->classReflectionProvider,
88
            $this->referenceAcceptor,
89
            $scope,
90
            $storage
91
        ) implements Visitor {
92
            /** @var MetadataCollection */
93
            private $metadataCollection;
94
95
            /** @var ReferenceResolver */
96
            private $referenceResolver;
97
98
            /** @var Constructor */
99
            private $constructor;
100
101
            /** @var ClassReflectionProvider */
102
            private $classReflectionProvider;
103
104
            /** @var ReferenceAcceptor */
105
            private $referenceAcceptor;
106
107
            /** @var Scope */
108
            private $scope;
109
110
            /** @var SplObjectStorage<object> */
111
            private $storage;
112
113
            /** @var SplStack<mixed> */
114
            private $stack;
115
116 9
            public function __construct(
117
                MetadataCollection $metadataCollection,
118
                ReferenceResolver $referenceResolver,
119
                Constructor $constructor,
120
                ClassReflectionProvider $classReflectionProvider,
121
                ReferenceAcceptor $referenceAcceptor,
122
                Scope $scope,
123
                SplObjectStorage $storage
124
            ) {
125 9
                $this->metadataCollection      = $metadataCollection;
126 9
                $this->referenceResolver       = $referenceResolver;
127 9
                $this->constructor             = $constructor;
128 9
                $this->classReflectionProvider = $classReflectionProvider;
129 9
                $this->referenceAcceptor       = $referenceAcceptor;
130 9
                $this->scope                   = $scope;
131 9
                $this->storage                 = $storage;
132 9
                $this->stack                   = new SplStack();
133 9
            }
134
135 9
            public function visitAnnotations(Annotations $annotations) : void
136
            {
137 9
                foreach ($annotations as $annotation) {
138 9
                    $stackSize = $this->stack->count();
139
140 9
                    $annotation->dispatch($this);
141
142 9
                    if ($this->stack->count() === $stackSize) {
143 5
                        continue;
144
                    }
145
146 9
                    $this->storage->attach($this->stack->pop());
147
                }
148
149 9
                assert($this->stack->isEmpty());
150 9
            }
151
152 9
            public function visitAnnotation(Annotation $annotation) : void
153
            {
154 9
                if (! $this->referenceAcceptor->accepts($annotation->getName(), $this->scope)) {
155 5
                    return;
156
                }
157
158 9
                $this->scope->increaseNestingLevel();
159
160 9
                $annotation->getParameters()->dispatch($this);
161 9
                $annotation->getName()->dispatch($this);
162
163 9
                $this->stack->push(
164 9
                    $this->constructor->construct(
165 9
                        $this->metadataCollection[$this->stack->pop()],
166 9
                        $this->scope,
167 9
                        $this->stack->pop()
168
                    )
169
                );
170
171 9
                $this->scope->decreaseNestingLevel();
172 9
            }
173
174 9
            public function visitReference(Reference $reference) : void
175
            {
176 9
                $this->stack->push($this->referenceResolver->resolve($reference, $this->scope));
177 9
            }
178
179 9
            public function visitParameters(Parameters $parameters) : void
180
            {
181 9
                $new = [];
182
183 9
                foreach ($parameters as $parameter) {
184 9
                    $parameter->dispatch($this);
185
186 9
                    assert(! array_key_exists($this->stack->current(), $new));
187
188 9
                    $new[$this->stack->pop()] = $this->stack->pop();
189
                }
190
191 9
                $this->stack->push($new);
192 9
            }
193
194 6
            public function visitUnnamedParameter(UnnamedParameter $parameter) : void
195
            {
196 6
                $parameter->getValue()->dispatch($this);
197
198 6
                $this->stack->push($this->stack->pop());
199 6
                $this->stack->push(null);
200 6
            }
201
202 4
            public function visitNamedParameter(NamedParameter $parameter) : void
203
            {
204 4
                $parameter->getValue()->dispatch($this);
205 4
                $parameter->getName()->dispatch($this);
206
                // pass through
207 4
            }
208
209 4
            public function visitIdentifier(Identifier $identifier) : void
210
            {
211 4
                $this->stack->push($identifier->getValue());
212 4
            }
213
214 1
            public function visitPair(Pair $pair) : void
215
            {
216 1
                $pair->getValue()->dispatch($this);
217 1
                $pair->getKey()->dispatch($this);
218
                // pass through
219 1
            }
220
221 1
            public function visitBooleanScalar(BooleanScalar $booleanScalar) : void
222
            {
223 1
                $this->stack->push($booleanScalar->getValue());
224 1
            }
225
226 3
            public function visitIntegerScalar(IntegerScalar $integerScalar) : void
227
            {
228 3
                $this->stack->push($integerScalar->getValue());
229 3
            }
230
231 1
            public function visitFloatScalar(FloatScalar $floatScalar) : void
232
            {
233 1
                $this->stack->push($floatScalar->getValue());
234 1
            }
235
236 7
            public function visitStringScalar(StringScalar $stringScalar) : void
237
            {
238 7
                $this->stack->push($stringScalar->getValue());
239 7
            }
240
241 1
            public function visitNullScalar(NullScalar $nullScalar) : void
242
            {
243 1
                $this->stack->push($nullScalar->getValue());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $nullScalar->getValue() targeting Doctrine\Annotations\Par...\NullScalar::getValue() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
244 1
            }
245
246 3
            public function visitListCollection(ListCollection $listCollection) : void
247
            {
248 3
                $list = [];
249
250 3
                foreach ($listCollection as $listItem) {
251 3
                    $listItem->dispatch($this);
252
253 3
                    $list[] = $this->stack->pop();
254
                }
255
256 3
                $this->stack->push($list);
257 3
            }
258
259 1
            public function visitMapCollection(MapCollection $mapCollection) : void
260
            {
261 1
                $map = [];
262
263 1
                foreach ($mapCollection as $mapItem) {
264 1
                    $mapItem->dispatch($this);
265 1
                    $map[$this->stack->pop()] = $this->stack->pop();
266
                }
267
268 1
                $this->stack->push($map);
269 1
            }
270
271
            public function visitConstantFetch(ConstantFetch $constantFetch) : void
272
            {
273
                $constantFetch->getName()->dispatch($this);
274
275
                // TODO refactor out
276
277
                $constantName = $this->stack->pop();
278
279
                $this->stack->push(constant($constantName));
280
            }
281
282 1
            public function visitClassConstantFetch(ClassConstantFetch $classConstantFetch) : void
283
            {
284 1
                $classConstantFetch->getName()->dispatch($this);
285 1
                $classConstantFetch->getClass()->dispatch($this);
286
287
                // TODO refactor out
288
289 1
                $className    = $this->stack->pop();
290 1
                $constantName = $this->stack->pop();
291
292 1
                if (strtolower($constantName) === 'class') {
293
                    $this->stack->push($className);
294
                    return;
295
                }
296
297 1
                $this->stack->push(constant($className . '::' . $constantName));
298 1
            }
299
        };
300
    }
301
}
302