Annotation::get()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 21
rs 9.0534
cc 4
eloc 11
nc 3
nop 1
1
<?php
2
/**
3
 * This file is part of the Stack package.
4
 *
5
 * (c) Andrzej Kostrzewa <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Stack\DI\Definition\Source;
12
13
use Stack\DI\Definition\ObjectDefinition;
14
15
/**
16
 * Reads DI class definitions using reflection.
17
 *
18
 * @author Andrzej Kostrzewa <[email protected]>
19
 */
20
class Annotation extends AbstractDefinitionSource
21
{
22
    /**
23
     * @var PhpDocReader
24
     */
25
    private $phpDocReader;
26
27
    /**
28
     * Returns the DI definition for the entry name.
29
     *
30
     * @param $name
31
     *
32
     * @return mixed|null|object
33
     */
34
    public function get($name)
35
    {
36
        if ($this->has($name)) {
37
            return $this->definitions[$name];
38
        }
39
40
        if (!class_exists($name) && !interface_exists($name)) {
41
            return;
42
        }
43
44
        $class  = new \ReflectionClass($name);
45
        $object = new ObjectDefinition($name);
46
47
        $this->readProperties($class, $object);
48
49
        $object = $this->setMethods($class, $object);
50
51
        $this->set($name, $object);
52
53
        return $object;
54
    }
55
56
    /**
57
     * Get instance of PhpDocReader.
58
     *
59
     * @return PhpDocReader
60
     */
61
    private function getPhpDocReader()
62
    {
63
        if ($this->phpDocReader === null) {
64
            $this->phpDocReader = new PhpDocReader();
65
        }
66
67
        return $this->phpDocReader;
68
    }
69
70
    /**
71
     * Browse the class properties looking for annotated properties.
72
     *
73
     * @param \ReflectionClass $class
74
     * @param ObjectDefinition $object
75
     */
76
    private function readProperties(\ReflectionClass $class, ObjectDefinition $object)
77
    {
78
        $namespace = $class->getNamespaceName();
79
        foreach ($class->getProperties() as $property) {
80
            if ($property->isStatic()) {
81
                continue;
82
            }
83
84
            $this->setProperty($property, $object, $namespace);
85
        }
86
87
        while ($class = $class->getParentClass()) {
88
            foreach ($class->getProperties(\ReflectionProperty::IS_PRIVATE) as $property) {
89
                if ($property->isStatic()) {
90
                    continue;
91
                }
92
93
                $this->setProperty($property, $object, $namespace);
94
            }
95
        }
96
    }
97
98
    /**
99
     * Set Class property instance.
100
     *
101
     * @param \ReflectionProperty $property
102
     * @param ObjectDefinition    $objectDefinition
103
     * @param $namespace
104
     *
105
     * @throws \Stack\DI\Exception\AnnotationException
106
     */
107
    private function setProperty(\ReflectionProperty $property, ObjectDefinition $objectDefinition, $namespace)
108
    {
109
        $this->getPhpDocReader()->setNamespace($namespace);
110
        $propertyClass       = $this->getPhpDocReader()->getPropertyClass($property);
111
        $propertyClassObject = $propertyClass ? $propertyClass->getNewInstance() : null;
112
        $objectDefinition->setPropertyInjection($property, $propertyClassObject);
113
        if ($propertyClass !== null) {
114
            $this->set($propertyClass->getClassName(), $propertyClassObject);
115
        }
116
    }
117
118
    /**
119
     * Browse the object's methods looking for annotated methods.
120
     *
121
     * @param \ReflectionClass $class
122
     * @param ObjectDefinition $objectDefinition
123
     *
124
     * @return object|\ReflectionClass
125
     */
126
    private function setMethods(\ReflectionClass $class, ObjectDefinition $objectDefinition)
127
    {
128
        $isConstructor = false;
129
        $methodName    = [];
130
        foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
131
            if ($method->isStatic()) {
132
                continue;
133
            }
134
135
            $methodParameters = $this->getMethodParameters($method);
136
            if ($methodParameters === null) {
137
                continue;
138
            }
139
140
            $isMethod = true;
141
            if ($method->isConstructor()) {
142
                $objectDefinition->setConstructorInjection($methodParameters);
143
                $isConstructor = true;
144
                $isMethod      = false;
145
            }
146
147
            if ($isMethod) {
148
                $objectDefinition->addMethodInjection($method->getName(), $methodParameters);
149
                $methodName[] = $method->getName();
150
            }
151
        }
152
153
        $object = $objectDefinition->getNewInstance($isConstructor);
154
        foreach ($methodName as $name) {
155
            $methodReflection = new \ReflectionMethod($object, $name);
156
            $args             = $objectDefinition->getMethodParameters($name);
157
            $methodReflection->invokeArgs($object, $args);
158
        }
159
160
        return $object;
161
    }
162
163
    /**
164
     * Get parameters for method.
165
     *
166
     * @param \ReflectionMethod $method
167
     *
168
     * @return array|null
169
     */
170
    private function getMethodParameters(\ReflectionMethod $method)
171
    {
172
        $annotationParameters = $this->getPhpDocReader()->getMethodParameters($method);
173
        if ($annotationParameters !== null) {
174
            $methodParameters = [];
175
            foreach ($annotationParameters as $parameter) {
176
                if ($this->has($parameter)) {
177
                    $parameter = $this->get($parameter);
178
                }
179
                $methodParameters[] = $parameter;
180
            }
181
182
            return $methodParameters;
183
        }
184
    }
185
}
186