Completed
Pull Request — master (#82)
by Loren
03:39
created

ReflectionProperty::wasIncluded()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 7
c 0
b 0
f 0
ccs 4
cts 4
cp 1
rs 9.4285
cc 3
eloc 5
nc 3
nop 0
crap 3
1
<?php
2
/**
3
 * Parser Reflection API
4
 *
5
 * @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\ParserReflection;
12
13
use Go\ParserReflection\Traits\InitializationTrait;
14
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
15
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
16
use PhpParser\Node\Stmt\ClassLike;
17
use PhpParser\Node\Stmt\Property;
18
use PhpParser\Node\Stmt\PropertyProperty;
19
use ReflectionProperty as BaseReflectionProperty;
20
use ReflectionClass as BaseReflectionClass;
21
22
/**
23
 * AST-based reflection for class property
24
 */
25
class ReflectionProperty extends BaseReflectionProperty implements ReflectionInterface
26
{
27
    use InitializationTrait, InternalPropertiesEmulationTrait;
28
29
    /**
30
     * Type of property node
31
     *
32
     * @var Property
33
     */
34
    private $propertyTypeNode;
35
36
    /**
37
     * Concrete property node
38
     *
39
     * @var PropertyProperty
40
     */
41
    private $propertyNode;
42
43
    /**
44
     * Name of the class
45
     *
46
     * @var string
47
     */
48
    private $className;
49
50
    /**
51
     * Name of the property
52
     *
53
     * @var string
54
     */
55
    private $propertyName;
56
57
    /**
58
     * Initializes a reflection for the property
59
     *
60
     * @param string $className Name of the class with properties
61
     * @param string $propertyName Name of the property to reflect
62
     * @param Property $propertyType Property type definition node
63
     * @param PropertyProperty $propertyNode Concrete property definition (value, name)
64
     */
65 50
    public function __construct(
66
        $className,
67
        $propertyName,
68
        Property $propertyType = null,
69
        PropertyProperty $propertyNode = null
70
    ) {
71 50
        $this->className        = $className;
72 50
        $this->propertyName     = $propertyName;
73 50
        if (!$propertyType || !$propertyNode) {
74 5
            $oneNodeProvided = $propertyType || $propertyNode;
75 5
            $propertyType    = null;
76 5
            $propertyNode    = null;
77 5
            $isUserDefined   = true;
78
            // If either node is non-null, it must be user-defined.
79 5
            if (!$oneNodeProvided && $this->wasIncluded()) {
80 5
                $nativeRef = new BaseReflectionClass($this->className);
81 5
                $isUserDefined = $nativeRef->isUserDefined();
82
            }
83 5
            if ($isUserDefined) {
84
                list ($propertyType, $propertyNode) =
85 1
                    ReflectionEngine::parseClassProperty($className, $propertyName);
86 1
                if (!isset($propertyNode)) {
87
                    $this->propertyName = null;
88
                }
89
            }
90
        }
91 50
        $this->propertyTypeNode = $propertyType;
92 50
        $this->propertyNode     = $propertyNode;
93 50
        if ($this->propertyNode) {
94 46
            $this->propertyName = $this->propertyNode->name;
95
        }
96
97
        // Let's unset original read-only properties to have a control over them via __get
98 50
        unset($this->name, $this->class);
99 50
    }
100
101
    /**
102
     * Returns an AST-node for property
103
     *
104
     * @return PropertyProperty
105
     */
106
    public function getNode()
107
    {
108
        return $this->propertyNode;
109
    }
110
111
    /**
112
     * Returns an AST-node for property type
113
     *
114
     * @return Property
115
     */
116
    public function getTypeNode()
117
    {
118
        return $this->propertyTypeNode;
119
    }
120
121
    /**
122
     * Emulating original behaviour of reflection
123
     */
124 7
    public function ___debugInfo()
125
    {
126
        return array(
127 7
            'name'  => isset($this->propertyName) ? $this->propertyName : 'unknown',
128 7
            'class' => $this->className
129
        );
130
    }
131
132
    /**
133
     * Return string representation of this little old property.
134
     *
135
     * @return string
136
     */
137 64
    public function __toString()
138
    {
139 64
        return sprintf(
140 64
            "Property [%s %s $%s ]\n",
141 64
            $this->isStatic() ? '' : ($this->isDefault() ? ' <default>' : ' <dynamic>'),
142 64
            join(' ', \Reflection::getModifierNames($this->getModifiers())),
143 64
            $this->getName()
144
        );
145
    }
146
147
    /**
148
     * {@inheritDoc}
149
     */
150 21
    public function getDeclaringClass()
151
    {
152 21
        return new ReflectionClass($this->className);
153
    }
154
155
    /**
156
     * @inheritDoc
157
     * @return string|false Property doc comment if any.
158
     */
159 64
    public function getDocComment()
160
    {
161 64
        if (!$this->propertyNode) {
162 8
            $this->initializeInternalReflection();
163 8
            return parent::getDocComment();
164
        }
165 56
        $docBlock = $this->propertyTypeNode->getDocComment();
166
167 56
        return $docBlock ? $docBlock->getText() : false;
0 ignored issues
show
Comprehensibility Best Practice introduced by Lisachenko Alexander
The expression $docBlock ? $docBlock->getText() : false; of type string|false adds false to the return on line 167 which is incompatible with the return type of the parent method ReflectionProperty::getDocComment of type string. It seems like you forgot to handle an error condition.
Loading history...
168
    }
169
170
    /**
171
     * {@inheritDoc}
172
     */
173 135
    public function getModifiers()
174
    {
175 135
        $modifiers = 0;
176 135
        if ($this->isPublic()) {
177 53
            $modifiers += self::IS_PUBLIC;
178
        }
179 135
        if ($this->isProtected()) {
180 50
            $modifiers += self::IS_PROTECTED;
181
        }
182 135
        if ($this->isPrivate()) {
183 43
            $modifiers += self::IS_PRIVATE;
184
        }
185 135
        if ($this->isStatic()) {
186 65
            $modifiers += self::IS_STATIC;
187
        }
188
189 135
        return $modifiers;
190
    }
191
192
    /**
193
     * @inheritDoc
194
     */
195 598
    public function getName()
196
    {
197 598
        return $this->propertyName;
198
    }
199
200
    /**
201
     * @inheritDoc
202
     */
203 22
    public function getValue($object = null)
204
    {
205 22
        if (!$this->propertyNode) {
206
            $this->initializeInternalReflection();
207
            return parent::getValue($object);
208
        }
209 22
        if (!isset($object)) {
210 21
            $solver = new NodeExpressionResolver($this->getDeclaringClass());
211 21
            if (!isset($this->propertyNode->default)) {
212 3
                return null;
213
            }
214 18
            $solver->process($this->propertyNode->default);
215
216 18
            return $solver->getValue();
217
        }
218
219 1
        $this->initializeInternalReflection();
220
221 1
        return parent::getValue($object);
222
    }
223
224
    /**
225
     * @inheritDoc
226
     */
227 98
    public function isDefault()
228
    {
229 98
        if (!$this->propertyNode) {
230 16
            $this->initializeInternalReflection();
231 16
            return parent::isDefault();
232
        }
233
        // TRUE if the property was declared at compile-time
234
235 82
        return true;
236
    }
237
238
    /**
239
     * {@inheritDoc}
240
     */
241 204
    public function isPrivate()
242
    {
243 204
        if (!$this->propertyNode) {
244 24
            $this->initializeInternalReflection();
245 24
            return parent::isPrivate();
246
        }
247 180
        return $this->propertyTypeNode->isPrivate();
248
    }
249
250
    /**
251
     * {@inheritDoc}
252
     */
253 199
    public function isProtected()
254
    {
255 199
        if (!$this->propertyNode) {
256 24
            $this->initializeInternalReflection();
257 24
            return parent::isProtected();
258
        }
259 175
        return $this->propertyTypeNode->isProtected();
260
    }
261
262
    /**
263
     * {@inheritDoc}
264
     */
265 199
    public function isPublic()
266
    {
267 199
        if (!$this->propertyNode) {
268 24
            $this->initializeInternalReflection();
269 24
            return parent::isPublic();
270
        }
271 175
        return $this->propertyTypeNode->isPublic();
272
    }
273
274
    /**
275
     * {@inheritDoc}
276
     */
277 213
    public function isStatic()
278
    {
279 213
        if (!$this->propertyNode) {
280 24
            $this->initializeInternalReflection();
281 24
            return parent::isStatic();
282
        }
283 189
        return $this->propertyTypeNode->isStatic();
284
    }
285
286
    /**
287
     * {@inheritDoc}
288
     */
289 2
    public function setAccessible($accessible)
290
    {
291 2
        $this->initializeInternalReflection();
292
293 2
        parent::setAccessible($accessible);
294 2
    }
295
296
    /**
297
     * @inheritDoc
298
     */
299 1
    public function setValue($object, $value = null)
300
    {
301 1
        $this->initializeInternalReflection();
302
303 1
        parent::setValue($object, $value);
304 1
    }
305
306
    /**
307
     * Parses properties from the concrete class node
308
     *
309
     * @param ClassLike $classLikeNode Class-like node
310
     * @param string    $fullClassName FQN of the class
311
     *
312
     * @return array|ReflectionProperty[]
313
     */
314 133
    public static function collectFromClassNode(ClassLike $classLikeNode, $fullClassName)
315
    {
316 133
        $properties = [];
317
318 133
        foreach ($classLikeNode->stmts as $classLevelNode) {
319 117
            if ($classLevelNode instanceof Property) {
320 45
                foreach ($classLevelNode->props as $classPropertyNode) {
321 45
                    $propertyName = $classPropertyNode->name;
322 45
                    $properties[$propertyName] = new static(
323 45
                        $fullClassName,
324 45
                        $propertyName,
325 45
                        $classLevelNode,
326 117
                        $classPropertyNode
327
                    );
328
                }
329
            }
330
        }
331
332 133
        return $properties;
333
    }
334
335
    /**
336
     * Implementation of internal reflection initialization
337
     *
338
     * @return void
339
     */
340 10
    protected function __initialize()
341
    {
342 10
        parent::__construct($this->className, $this->getName());
343 10
    }
344
345
    /**
346
     * Has class been loaded by PHP.
347
     *
348
     * @return bool
349
     *     If class file was included.
350
     */
351 5
    public function wasIncluded()
352
    {
353
        return
354 5
            interface_exists($this->className, false) ||
355 5
            trait_exists($this->className, false) ||
356 5
            class_exists($this->className, false);
357
    }
358
}
359