Completed
Pull Request — master (#107)
by Alexander
03:29
created

ReflectionProperty::__debugInfo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Parser Reflection API
5
 *
6
 * @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
7
 *
8
 * This source file is subject to the license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Go\ParserReflection;
13
14
use Go\ParserReflection\Traits\InitializationTrait;
15
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
16
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
17
use PhpParser\Node\Stmt\ClassLike;
18
use PhpParser\Node\Stmt\Property;
19
use PhpParser\Node\Stmt\PropertyProperty;
20
use Reflection;
21
use ReflectionProperty as BaseReflectionProperty;
22
23
/**
24
 * AST-based reflection for class property
25
 */
26
class ReflectionProperty extends BaseReflectionProperty
27
{
28
    use InitializationTrait;
29
    use InternalPropertiesEmulationTrait;
30
31
    /**
32
     * Type of property node
33
     *
34
     * @var Property
35
     */
36
    private $propertyTypeNode;
37
38
    /**
39
     * Concrete property node
40
     *
41
     * @var PropertyProperty
42
     */
43
    private $propertyNode;
44
45
    /**
46
     * Name of the class
47
     *
48
     * @var string
49
     */
50
    private $className;
51
52
    /**
53
     * Initializes a reflection for the property
54
     *
55
     * @param string            $className    Name of the class with properties
56
     * @param string            $propertyName Name of the property to reflect
57
     * @param ?Property         $propertyType Property type definition node
0 ignored issues
show
Documentation introduced by
The doc-type ?Property could not be parsed: Unknown type name "?Property" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
58
     * @param ?PropertyProperty $propertyNode Concrete property definition (value, name)
0 ignored issues
show
Documentation introduced by
The doc-type ?PropertyProperty could not be parsed: Unknown type name "?PropertyProperty" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
59
     */
60 20
    public function __construct(
61
        $className,
62
        $propertyName,
63
        Property $propertyType = null,
64
        PropertyProperty $propertyNode = null
65
    ) {
66 20
        $this->className = ltrim($className, '\\');
67 20
        if (!$propertyType || !$propertyNode) {
68 1
            [$propertyType, $propertyNode] = ReflectionEngine::parseClassProperty($className, $propertyName);
69
        }
70
71 20
        $this->propertyTypeNode = $propertyType;
72 20
        $this->propertyNode     = $propertyNode;
73
74
        // Let's unset original read-only properties to have a control over them via __get
75 20
        unset($this->name, $this->class);
76 20
    }
77
78
    /**
79
     * Returns an AST-node for property
80
     *
81
     * @return PropertyProperty
82
     */
83
    public function getNode()
84
    {
85
        return $this->propertyNode;
86
    }
87
88
    /**
89
     * Returns an AST-node for property type
90
     *
91
     * @return Property
92
     */
93
    public function getTypeNode()
94
    {
95
        return $this->propertyTypeNode;
96
    }
97
98
    /**
99
     * Emulating original behaviour of reflection
100
     */
101 1
    public function __debugInfo(): array
102
    {
103
        return [
104 1
            'name'  => isset($this->propertyNode) ? $this->propertyNode->name->toString() : 'unknown',
105 1
            'class' => $this->className
106
        ];
107
    }
108
109
    /**
110
     * Return string representation of this little old property.
111
     *
112
     * @return string
113
     */
114 26
    public function __toString()
115
    {
116 26
        return sprintf(
117 26
            "Property [%s %s $%s ]\n",
118 26
            $this->isStatic() ? '' : ($this->isDefault() ? ' <default>' : ' <dynamic>'),
119 26
            implode(' ', Reflection::getModifierNames($this->getModifiers())),
120 26
            $this->getName()
121
        );
122
    }
123
124
    /**
125
     * {@inheritDoc}
126
     */
127 15
    public function getDeclaringClass()
128
    {
129 15
        return new ReflectionClass($this->className);
130
    }
131
132
    /**
133
     * @inheritDoc
134
     */
135 26
    public function getDocComment()
136
    {
137 26
        $docBlock = $this->propertyTypeNode->getDocComment();
138
139 26
        return $docBlock ? $docBlock->getText() : false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $docBlock ? $docBlock->getText() : false; of type string|false adds false to the return on line 139 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...
140
    }
141
142
    /**
143
     * {@inheritDoc}
144
     */
145 60
    public function getModifiers()
146
    {
147 60
        $modifiers = 0;
148 60
        if ($this->isPublic()) {
149 28
            $modifiers += self::IS_PUBLIC;
150
        }
151 60
        if ($this->isProtected()) {
152 22
            $modifiers += self::IS_PROTECTED;
153
        }
154 60
        if ($this->isPrivate()) {
155 22
            $modifiers += self::IS_PRIVATE;
156
        }
157 60
        if ($this->isStatic()) {
158 32
            $modifiers += self::IS_STATIC;
159
        }
160
161 60
        return $modifiers;
162
    }
163
164
    /**
165
     * @inheritDoc
166
     */
167 268
    public function getName()
168
    {
169 268
        return $this->propertyNode->name->toString();
170
    }
171
172
    /**
173
     * @inheritDoc
174
     */
175 16
    public function getValue($object = null)
176
    {
177 16
        if (!isset($object)) {
178 15
            $solver = new NodeExpressionResolver($this->getDeclaringClass());
179 15
            if (!isset($this->propertyNode->default)) {
180 2
                return null;
181
            }
182 13
            $solver->process($this->propertyNode->default);
183
184 13
            return $solver->getValue();
185
        }
186
187 1
        $this->initializeInternalReflection();
188
189 1
        return parent::getValue($object);
190
    }
191
192
    /**
193
     * @inheritDoc
194
     */
195 39
    public function isDefault()
196
    {
197
        // TRUE if the property was declared at compile-time
198
199 39
        return true;
200
    }
201
202
    /**
203
     * {@inheritDoc}
204
     */
205 86
    public function isPrivate()
206
    {
207 86
        return $this->propertyTypeNode->isPrivate();
208
    }
209
210
    /**
211
     * {@inheritDoc}
212
     */
213 86
    public function isProtected()
214
    {
215 86
        return $this->propertyTypeNode->isProtected();
216
    }
217
218
    /**
219
     * {@inheritDoc}
220
     */
221 86
    public function isPublic()
222
    {
223 86
        return $this->propertyTypeNode->isPublic();
224
    }
225
226
    /**
227
     * {@inheritDoc}
228
     */
229 93
    public function isStatic()
230
    {
231 93
        return $this->propertyTypeNode->isStatic();
232
    }
233
234
    /**
235
     * {@inheritDoc}
236
     */
237 2
    public function setAccessible($accessible)
238
    {
239 2
        $this->initializeInternalReflection();
240
241 2
        parent::setAccessible($accessible);
242 2
    }
243
244
    /**
245
     * @inheritDoc
246
     */
247 1
    public function setValue($object, $value = null)
248
    {
249 1
        $this->initializeInternalReflection();
250
251 1
        parent::setValue($object, $value);
252 1
    }
253
254
    /**
255
     * Parses properties from the concrete class node
256
     *
257
     * @param ClassLike $classLikeNode Class-like node
258
     * @param string    $fullClassName FQN of the class
259
     *
260
     * @return array|ReflectionProperty[]
261
     */
262 44
    public static function collectFromClassNode(ClassLike $classLikeNode, $fullClassName)
263
    {
264 44
        $properties = [];
265
266 44
        foreach ($classLikeNode->stmts as $classLevelNode) {
267 40
            if ($classLevelNode instanceof Property) {
268 19
                foreach ($classLevelNode->props as $classPropertyNode) {
269 19
                    $propertyName = $classPropertyNode->name->toString();
270 19
                    $properties[$propertyName] = new static(
271 19
                        $fullClassName,
272
                        $propertyName,
273
                        $classLevelNode,
274
                        $classPropertyNode
275
                    );
276
                }
277
            }
278
        }
279
280 44
        return $properties;
281
    }
282
283
    /**
284
     * Implementation of internal reflection initialization
285
     *
286
     * @return void
287
     */
288 2
    protected function __initialize(): void
289
    {
290 2
        parent::__construct($this->className, $this->getName());
291 2
    }
292
}
293