Reflection::analyzingProperties()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 4
c 1
b 0
f 1
dl 0
loc 9
rs 10
cc 3
nc 4
nop 2
1
<?php
2
/**
3
 * Class reflection
4
 * User: moyo
5
 * Date: 12/10/2017
6
 * Time: 10:32 AM
7
 */
8
9
namespace Carno\Container\Injection;
10
11
use Carno\Container\Container;
12
use Carno\Container\Exception\RequiredConstructValueException;
13
use ReflectionClass;
14
use ReflectionMethod;
15
use ReflectionProperty;
16
17
class Reflection
18
{
19
    /**
20
     * @var Used
21
     */
22
    private $used = null;
23
24
    /**
25
     * @var Container
26
     */
27
    private $container = null;
28
29
    /**
30
     * @var ReflectionClass
31
     */
32
    private $reflection = null;
33
34
    /**
35
     * @var Dependency[]
36
     */
37
    private $constructParams = [];
38
39
    /**
40
     * @var Dependency[]
41
     */
42
    private $propertyInjects = [];
43
44
    /**
45
     * @var ReflectionClass[]
46
     */
47
    private $propertyKeepers = [];
48
49
    /**
50
     * Reflection constructor.
51
     * @param Container $container
52
     * @param string $class
53
     * @param array $args
54
     */
55
    public function __construct(Container $container, string $class, array $args = [])
56
    {
57
        $this->used = new Used();
58
        $this->container = $container;
59
        $this->reflection = $ref = new ReflectionClass($class);
60
61
        if ($init = $ref->getConstructor()) {
62
            $args
63
                ? $this->constructParams = $args
64
                : $this->parsingConstructor($init)
65
            ;
66
        }
67
68
        $this->analyzingProperties($ref);
69
    }
70
71
    /**
72
     * @return bool
73
     */
74
    public function hasConstructor() : bool
75
    {
76
        return !! $this->constructParams;
77
    }
78
79
    /**
80
     * @return array
81
     */
82
    public function getConstructParams() : array
83
    {
84
        return $this->constructParams;
85
    }
86
87
    /**
88
     * @return bool
89
     */
90
    public function hasProperties() : bool
91
    {
92
        return !! $this->propertyInjects;
93
    }
94
95
    /**
96
     * @return array
97
     */
98
    public function getPropertyInjects() : array
99
    {
100
        return $this->propertyInjects;
101
    }
102
103
    /**
104
     * @param object $object
105
     * @param string $name
106
     * @param object $depends
107
     * @return bool
108
     */
109
    public function setProperty($object, string $name, $depends) : bool
110
    {
111
        if ($property = ($this->propertyKeepers[$name] ?? $this->reflection)->getProperty($name)) {
112
            $property->isPublic() || $property->setAccessible(true);
113
            $property->setValue($object, $depends);
114
            return true;
115
        } else {
116
            return false;
117
        }
118
    }
119
120
    /**
121
     * @param ReflectionClass $class
122
     * @param ReflectionClass $master
123
     */
124
    private function analyzingProperties(ReflectionClass $class, ReflectionClass $master = null) : void
125
    {
126
        foreach ($class->getTraits() as $trait) {
127
            $this->analyzingProperties($trait, $class);
128
        }
129
130
        $this->parsingProperties($class->getProperties(), $master);
131
132
        ($parent = $class->getParentClass()) && $this->analyzingProperties($parent);
133
    }
134
135
    /**
136
     * @param ReflectionMethod $method
137
     */
138
    private function parsingConstructor(ReflectionMethod $method) : void
139
    {
140
        foreach ($method->getParameters() as $param) {
141
            if ($class = $param->getClass()) {
142
                $this->constructParams[$param->getPosition()] = new Dependency(
143
                    $this->container,
144
                    $class->getName(),
145
                    $class->isInterface(),
146
                    $param->isOptional()
147
                );
148
                continue;
149
            }
150
151
            if ($param->isOptional()) {
152
                $this->constructParams[$param->getPosition()] = $param->getDefaultValue();
153
                continue;
154
            }
155
156
            throw new RequiredConstructValueException(
157
                "CLASS {$param->getDeclaringClass()->getName()} P#{$param->getPosition()}"
158
            );
159
        }
160
    }
161
162
    /**
163
     * @param ReflectionProperty[] $properties
164
     * @param ReflectionClass $master
165
     */
166
    private function parsingProperties(array $properties, ReflectionClass $master = null) : void
167
    {
168
        foreach ($properties as $property) {
169
            $keeper = $property->getDeclaringClass();
170
            $linker = $master ?? $keeper;
171
            $anno = new Annotation($property->getDocComment());
0 ignored issues
show
Bug introduced by
It seems like $property->getDocComment() can also be of type boolean; however, parameter $doc of Carno\Container\Injectio...notation::__construct() does only seem to accept string, 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

171
            $anno = new Annotation(/** @scrutinizer ignore-type */ $property->getDocComment());
Loading history...
172
            if ($anno->has('inject')) {
173
                if ($link = $anno->get('see') ?? $anno->get('var')) {
174
                    // check exists
175
                    if (isset($this->propertyInjects[$name = $property->getName()])) {
176
                        // skip if keeper also matched
177
                        if (isset($this->propertyKeepers[$name])) {
178
                            continue;
179
                        }
180
                        // renaming
181
                        $name = $keeper->getName() . '::' . $name;
182
                    }
183
                    // check keeper
184
                    $this->reflection->getName() === $linker->getName() || $this->propertyKeepers[$name] = $linker;
185
                    // injector assigning
186
                    $this->propertyInjects[$name] = new Dependency(
187
                        $this->container,
188
                        $this->used->getFullClass($keeper, $link)
189
                    );
190
                }
191
            }
192
        }
193
    }
194
}
195