Completed
Push — master ( 555240...fa2155 )
by Alexander
115:41 queued 90:43
created

ReflectionProperty::__debugInfo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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