Completed
Push — master ( 8876be...bb9103 )
by Tobias
05:49 queued 02:55
created

NSA::getConstant()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 9.7
c 0
b 0
f 0
cc 3
nc 4
nop 2
crap 3
1
<?php
2
3
namespace Nyholm;
4
5
use Webmozart\Assert\Assert;
6
7
/**
8
 * Warning: This class should only be used with tests, fixtures or debug.
9
 *
10
 * @author Tobias Nyholm <[email protected]>
11
 */
12
class NSA
13
{
14
    /**
15
     * Get a constant of an object. You may provide the class name (including namespace) instead of an object.
16
     *
17
     * @param object|string $objectOrClass
18
     * @param string        $constantName
19
     *
20
     * @return mixed
21
     *
22
     * @throws \InvalidArgumentException
23
     * @throws \LogicException
24
     */
25 3
    public static function getConstant($objectOrClass, $constantName)
26
    {
27 3
        $class = $objectOrClass;
28
29 3
        if (!is_string($objectOrClass)) {
30 2
            Assert::object($objectOrClass, 'Can not get a constant of a non object. Variable of type "%s" was given.');
31 2
            $class = get_class($objectOrClass);
32
        }
33
34 3
        $refl = static::getReflectionClassWithConstant($class, $constantName);
0 ignored issues
show
Bug introduced by
It seems like $class defined by $objectOrClass on line 27 can also be of type object; however, Nyholm\NSA::getReflectionClassWithConstant() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
35
36 3
        if (null === $refl) {
37 1
            throw new \LogicException(sprintf('The constant %s does not exist on %s or any of its parents.', $constantName, $class));
38
        }
39
40 2
        return $refl->getConstant($constantName);
41
    }
42
43
    /**
44
     * Get a property of an object. If the property is static you may provide the class name (including namespace)
45
     * instead of an object.
46
     *
47
     * @param object|string $objectOrClass
48
     * @param string        $propertyName
49
     *
50
     * @return mixed
51
     *
52
     * @throws \InvalidArgumentException
53
     * @throws \LogicException
54
     */
55 25
    public static function getProperty($objectOrClass, $propertyName)
56
    {
57 25
        return static::getAccessibleReflectionProperty($objectOrClass, $propertyName)->getValue($objectOrClass);
58
    }
59
60
    /**
61
     * Set a property to an object. If the property is static you may provide the class name (including namespace)
62
     * instead of an object.
63
     *
64
     * @param object|string $objectOrClass
65
     * @param string        $propertyName
66
     * @param mixed         $value
67
     *
68
     * @throws \InvalidArgumentException
69
     * @throws \LogicException
70
     */
71 9
    public static function setProperty($objectOrClass, $propertyName, $value)
72
    {
73 9
        static::getAccessibleReflectionProperty($objectOrClass, $propertyName)->setValue($objectOrClass, $value);
74 9
    }
75
76
    /**
77
     * Invoke a method on a object and get the return values. If the method is static you may provide the class
78
     * name (including namespace) instead of an object.
79
     *
80
     * @param object|string $objectOrClass
81
     * @param string        $methodName
82
     * @param mixed         ...$params
83
     *
84
     * @return mixed
85
     *
86
     * @throws \InvalidArgumentException
87
     * @throws \LogicException
88
     */
89 16
    public static function invokeMethod()
90
    {
91 16
        if (func_num_args() < 2) {
92 2
            throw new \LogicException('The method Reflection::invokeMethod need at least two arguments.');
93
        }
94
95 14
        $arguments = func_get_args();
96 14
        $objectOrClass = array_shift($arguments);
97 14
        $methodName = array_shift($arguments);
98
99 14
        Assert::string($methodName, 'Method name has to be a string. Variable of type "%s" was given.');
100 11 View Code Duplication
        if (is_string($objectOrClass)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101 4
            Assert::classExists($objectOrClass, 'Could not find class "%s"');
102
        } else {
103 7
            Assert::notInstanceOf($objectOrClass, '\stdClass', 'Can not get a method of \stdClass.');
104 6
            Assert::object($objectOrClass, 'Can not get a property of a non object. Variable of type "%s" was given.');
105
        }
106
107 8
        $refl = new \ReflectionClass($objectOrClass);
108 8
        if (!$refl->hasMethod($methodName)) {
109 1
            throw new \LogicException(sprintf('The method %s::%s does not exist.', get_class($objectOrClass), $methodName));
110
        }
111
112 7
        $method = $refl->getMethod($methodName);
113 7
        $method->setAccessible(true);
114
115
        // If it is a static call we should pass null as first parameter to \ReflectionMethod::invokeArgs
116 7
        $object = null;
117 7
        if (!$method->isStatic()) {
118 4
            $object = $objectOrClass;
119 4
            Assert::object($object, 'Can not access non-static method without an object.');
120
        }
121
122 6
        return $method->invokeArgs($object, $arguments);
123
    }
124
125
    /**
126
     * Get a reflection class that has this constant.
127
     *
128
     * @param string $class
129
     * @param string $constantName
130
     *
131
     * @return \ReflectionClass|null
132
     *
133
     * @throws \InvalidArgumentException
134
     */
135 3 View Code Duplication
    protected static function getReflectionClassWithConstant($class, $constantName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
136
    {
137 3
        Assert::string($class, 'First argument to Reflection::getReflectionClassWithConstant must be string. Variable of type "%s" was given.');
138 3
        Assert::classExists($class, 'Could not find class "%s"');
139
140 3
        $refl = new \ReflectionClass($class);
141 3
        if ($refl->hasConstant($constantName)) {
142 2
            return $refl;
143
        }
144
145 1
        if (false === $parent = get_parent_class($class)) {
146
            // No more parents
147 1
            return;
148
        }
149
150 1
        return self::getReflectionClassWithConstant($parent, $constantName);
151
    }
152
    /**
153
     * Get a reflection class that has this property.
154
     *
155
     * @param string $class
156
     * @param string $propertyName
157
     *
158
     * @return \ReflectionClass|null
159
     *
160
     * @throws \InvalidArgumentException
161
     */
162 21 View Code Duplication
    protected static function getReflectionClassWithProperty($class, $propertyName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
163
    {
164 21
        Assert::string($class, 'First argument to Reflection::getReflectionClassWithProperty must be string. Variable of type "%s" was given.');
165 21
        Assert::classExists($class, 'Could not find class "%s"');
166
167 20
        $refl = new \ReflectionClass($class);
168 20
        if ($refl->hasProperty($propertyName)) {
169 19
            return $refl;
170
        }
171
172 9
        if (false === $parent = get_parent_class($class)) {
173
            // No more parents
174 1
            return;
175
        }
176
177 9
        return self::getReflectionClassWithProperty($parent, $propertyName);
178
    }
179
180
    /**
181
     * Get an reflection property that you can access directly.
182
     *
183
     * @param object|string $objectOrClass
184
     * @param string        $propertyName
185
     *
186
     * @return \ReflectionProperty
187
     *
188
     * @throws \InvalidArgumentException
189
     * @throws \LogicException           if the property is not found on the object
190
     */
191 25
    protected static function getAccessibleReflectionProperty($objectOrClass, $propertyName)
192
    {
193 25
        Assert::string($propertyName, 'Property name must be a string. Variable of type "%s" was given.');
194
195 23
        $class = $objectOrClass;
196 23 View Code Duplication
        if (!is_string($objectOrClass)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197 18
            Assert::object($objectOrClass, 'Can not get a property of a non object. Variable of type "%s" was given.');
198 16
            Assert::notInstanceOf($objectOrClass, '\stdClass', 'Can not get a property of \stdClass.');
199 15
            $class = get_class($objectOrClass);
200
        }
201
202 20
        if (null === $refl = static::getReflectionClassWithProperty($class, $propertyName)) {
0 ignored issues
show
Bug introduced by
It seems like $class defined by $objectOrClass on line 195 can also be of type object; however, Nyholm\NSA::getReflectionClassWithProperty() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
203 1
            throw new \LogicException(sprintf('The property %s does not exist on %s or any of its parents.', $propertyName, $class));
204
        }
205
206 19
        $property = $refl->getProperty($propertyName);
207 19
        $property->setAccessible(true);
208
209 19
        if (!$property->isStatic()) {
210 11
            Assert::object($objectOrClass, 'Can not access non-static property without an object.');
211
        }
212
213 18
        return $property;
214
    }
215
216
    /**
217
     * Get all property names on a class or object
218
     *
219
     * @param object|string $objectOrClass
220
     *
221
     * @return array of strings
222
     *
223
     * @throws \InvalidArgumentException
224
     */
225 18
    public static function getProperties($objectOrClass)
226
    {
227 18
        $class = $objectOrClass;
228 18 View Code Duplication
        if (!is_string($objectOrClass)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
229 10
            Assert::object($objectOrClass, 'Can not get a property of a non object. Variable of type "%s" was given.');
230 9
            Assert::notInstanceOf($objectOrClass, '\stdClass', 'Can not get a property of \stdClass.');
231 8
            $class = get_class($objectOrClass);
232
        }
233
234 16
        $refl = new \ReflectionClass($class);
235 16
        $properties = $refl->getProperties();
236
237
        // check parents
238 16
        while (false !== $parent = get_parent_class($class)) {
239 16
            $parentRefl = new \ReflectionClass($parent);
240 16
            $properties = array_merge($properties, $parentRefl->getProperties());
241 16
            $class = $parent;
242
        }
243
244 16
        return array_map(function($reflectionProperty) {
245 16
            return $reflectionProperty->name;
246 16
        }, $properties);
247
    }
248
}
249