Completed
Push — master ( 253768...f248bf )
by Alexander
02:29
created

ReflectionProperty::collectFromClassNode()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 19
ccs 14
cts 14
cp 1
rs 9.2
cc 4
eloc 11
nc 4
nop 2
crap 4
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\ValueResolver\NodeExpressionResolver;
15
use PhpParser\Node\Stmt\ClassLike;
16
use PhpParser\Node\Stmt\Property;
17
use PhpParser\Node\Stmt\PropertyProperty;
18
use ReflectionProperty as BaseReflectionProperty;
19
20
/**
21
 * AST-based reflection for class property
22
 */
23
class ReflectionProperty extends BaseReflectionProperty
24
{
25
    use InitializationTrait;
26
27
    /**
28
     * Type of property node
29
     *
30
     * @var Property
31
     */
32
    private $propertyTypeNode;
33
34
    /**
35
     * Concrete property node
36
     *
37
     * @var PropertyProperty
38
     */
39
    private $propertyNode;
40
41
    /**
42
     * Name of the class
43
     *
44
     * @var string
45
     */
46
    private $className;
47
48
    /**
49
     * Initializes a reflection for the property
50
     *
51
     * @param string $className Name of the class with properties
52
     * @param string $propertyName Name of the property to reflect
53
     * @param Property $propertyType Property type definition node
54
     * @param PropertyProperty $propertyNode Concrete property definition (value, name)
55
     */
56 6
    public function __construct(
57
        $className,
58
        $propertyName,
59
        Property $propertyType = null,
60
        PropertyProperty $propertyNode = null)
0 ignored issues
show
Coding Style introduced by
The closing parenthesis of a multi-line function declaration must be on a new line
Loading history...
Coding Style introduced by
There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found newline
Loading history...
61
    {
62 6
        $this->className    = $className;
63 6
        if (!$propertyType || !$propertyNode) {
64 2
            list ($propertyType, $propertyNode) = ReflectionEngine::parseClassProperty($className, $propertyName);
65 2
        }
66
67 6
        $this->propertyTypeNode = $propertyType;
68 6
        $this->propertyNode     = $propertyNode;
69 6
    }
70
71
    /**
72
     * Emulating original behaviour of reflection
73
     */
74 1
    public function __debugInfo()
75
    {
76
        return array(
77 1
            'name'  => isset($this->propertyNode) ? $this->propertyNode->name : 'unknown',
78 1
            'class' => $this->className
79 1
        );
80
    }
81
82
    /**
83
     * Return string representation of this little old property.
84
     *
85
     * @return string
86
     */
87 1
    public function __toString()
88
    {
89 1
        return sprintf(
90 1
            "Property [%s %s $%s ]\n",
91 1
            $this->isStatic() ? '' : ($this->isDefault() ? ' <default>' : ' <dynamic>'),
92 1
            join(' ', \Reflection::getModifierNames($this->getModifiers())),
93 1
            $this->getName()
94 1
        );
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100 3
    public function getDeclaringClass()
101
    {
102 3
        return new ReflectionClass($this->className);
103
    }
104
105
    /**
106
     * @inheritDoc
107
     */
108 1
    public function getDocComment()
109
    {
110 1
        $docBlock = $this->propertyTypeNode->getDocComment();
111
112 1
        return $docBlock ? $docBlock->getText() : false;
113
    }
114
115
    /**
116
     * {@inheritDoc}
117
     */
118 1
    public function getModifiers()
119
    {
120 1
        $modifiers = 0;
121 1
        if ($this->isPublic()) {
122 1
            $modifiers += self::IS_PUBLIC;
123 1
        }
124 1
        if ($this->isProtected()) {
125 1
            $modifiers += self::IS_PROTECTED;
126 1
        }
127 1
        if ($this->isPrivate()) {
128 1
            $modifiers += self::IS_PRIVATE;
129 1
        }
130 1
        if ($this->isStatic()) {
131 1
            $modifiers += self::IS_STATIC;
132 1
        }
133
134 1
        return $modifiers;
135
    }
136
137
    /**
138
     * @inheritDoc
139
     */
140 4
    public function getName()
141
    {
142 4
        return $this->propertyNode->name;
143
    }
144
145
    /**
146
     * @inheritDoc
147
     */
148 4
    public function getValue($object = null)
149
    {
150 4
        if (!isset($object)) {
151 3
            $solver = new NodeExpressionResolver($this->getDeclaringClass());
152 3
            $solver->process($this->propertyNode->default);
0 ignored issues
show
Bug introduced by
It seems like $this->propertyNode->default can be null; however, process() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
153
154 3
            return $solver->getValue();
155
        }
156
157 1
        $this->initializeInternalReflection();
158
159 1
        return parent::getValue($object);
160
    }
161
162
    /**
163
     * @inheritDoc
164
     */
165 1
    public function isDefault()
166
    {
167 1
        return isset($this->propertyNode->default);
168
    }
169
170
    /**
171
     * {@inheritDoc}
172
     */
173 1
    public function isPrivate()
174
    {
175 1
        return $this->propertyTypeNode->isPrivate();
176
    }
177
178
    /**
179
     * {@inheritDoc}
180
     */
181 1
    public function isProtected()
182
    {
183 1
        return $this->propertyTypeNode->isProtected();
184
    }
185
186
    /**
187
     * {@inheritDoc}
188
     */
189 1
    public function isPublic()
190
    {
191 1
        return $this->propertyTypeNode->isPublic();
192
    }
193
194
    /**
195
     * {@inheritDoc}
196
     */
197 2
    public function isStatic()
198
    {
199 2
        return $this->propertyTypeNode->isStatic();
200
    }
201
202
    /**
203
     * {@inheritDoc}
204
     */
205 2
    public function setAccessible($accessible)
206
    {
207 2
        $this->initializeInternalReflection();
208
209 2
        parent::setAccessible($accessible);
210 2
    }
211
212
    /**
213
     * @inheritDoc
214
     */
215 1
    public function setValue($object, $value = null)
216
    {
217 1
        $this->initializeInternalReflection();
218
219 1
        parent::setValue($object, $value);
220 1
    }
221
222
    /**
223
     * Parses properties from the concrete class node
224
     *
225
     * @param ClassLike $classLikeNode Class-like node
226
     * @param string    $fullClassName FQN of the class
227
     *
228
     * @return array|ReflectionProperty[]
229
     */
230 5
    public static function collectFromClassNode(ClassLike $classLikeNode, $fullClassName)
231
    {
232 5
        $properties = [];
233
234 5
        foreach ($classLikeNode->stmts as $classLevelNode) {
235 5
            if ($classLevelNode instanceof Property) {
236 4
                foreach ($classLevelNode->props as $classPropertyNode) {
237 4
                    $properties[] = new static(
238 4
                        $fullClassName,
239 4
                        $classPropertyNode->name,
240 4
                        $classLevelNode,
241
                        $classPropertyNode
242 4
                    );
243 4
                }
244 4
            }
245 5
        }
246
247 5
        return $properties;
248
    }
249
250
    /**
251
     * Implementation of internal reflection initialization
252
     *
253
     * @return void
254
     */
255 2
    protected function __initialize()
256
    {
257 2
        parent::__construct($this->className, $this->getName());
258 2
    }
259
}
260