Completed
Push — master ( 85e87d...33c347 )
by Tobias
03:05
created

NSA::invokeMethod()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 36
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 36
ccs 21
cts 21
cp 1
rs 8.439
cc 5
eloc 23
nc 7
nop 0
crap 5
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 property of an object. If the property is static you may provide the class name (including namespace)
16
     * instead of an object.
17
     *
18
     * @param object|string $objectOrClass
19
     * @param string        $propertyName
20
     *
21
     * @return mixed
22
     *
23
     * @throws \InvalidArgumentException
24
     * @throws \LogicException
25
     */
26 25
    public static function getProperty($objectOrClass, $propertyName)
27
    {
28 25
        return static::getAccessibleReflectionProperty($objectOrClass, $propertyName)->getValue($objectOrClass);
29
    }
30
31
    /**
32
     * Set a property to an object. If the property is static you may provide the class name (including namespace)
33
     * instead of an object.
34
     *
35
     * @param object|string $objectOrClass
36
     * @param string        $propertyName
37
     * @param mixed         $value
38
     *
39
     * @throws \InvalidArgumentException
40
     * @throws \LogicException
41
     */
42 9
    public static function setProperty($objectOrClass, $propertyName, $value)
43
    {
44 9
        static::getAccessibleReflectionProperty($objectOrClass, $propertyName)->setValue($objectOrClass, $value);
45 9
    }
46
47
    /**
48
     * Invoke a method on a object and get the return values. If the method is static you may provide the class
49
     * name (including namespace) instead of an object.
50
     *
51
     * @param object|string $objectOrClass
52
     * @param string        $methodName
53
     * @param mixed         ...$params
54
     *
55
     * @return mixed
56
     *
57
     * @throws \InvalidArgumentException
58
     * @throws \LogicException
59
     */
60 16
    public static function invokeMethod()
61
    {
62 16
        if (func_num_args() < 2) {
63 2
            throw new \LogicException('The method Reflection::invokeMethod need at least two arguments.');
64
        }
65
66 14
        $arguments = func_get_args();
67 14
        $objectOrClass = array_shift($arguments);
68 14
        $methodName = array_shift($arguments);
69
70 14
        Assert::string($methodName, 'Method name has to be a string. Variable of type "%s" was given.');
71 11
        if (is_string($objectOrClass)) {
72 4
            Assert::classExists($objectOrClass, 'Could not find class "%s"');
73
        } else {
74 7
            Assert::notInstanceOf($objectOrClass, '\stdClass', 'Can not get a method of \stdClass.');
75 6
            Assert::object($objectOrClass, 'Can not get a property of a non object. Variable of type "%s" was given.');
76
        }
77
78 8
        $refl = new \ReflectionClass($objectOrClass);
79 8
        if (!$refl->hasMethod($methodName)) {
80 1
            throw new \LogicException(sprintf('The method %s::%s does not exist.', get_class($objectOrClass), $methodName));
81
        }
82
83 7
        $method = $refl->getMethod($methodName);
84 7
        $method->setAccessible(true);
85
86 7
        if ($method->isStatic()) {
87
            // If it is a static call we should pass null as first parameter to \ReflectionMethod::invokeArgs
88 3
            $object = null;
89
        } else {
90 4
            $object = $objectOrClass;
91 4
            Assert::object($objectOrClass, 'Can not access non-static method without an object.');
92
        }
93
94 6
        return $method->invokeArgs($object, $arguments);
95
    }
96
97
    /**
98
     * Get a reflection class that has this property.
99
     *
100
     * @param string $class
101
     * @param string $propertyName
102
     *
103
     * @return \ReflectionClass|null
104
     *
105
     * @throws \InvalidArgumentException
106
     */
107 21
    protected static function getReflectionClassWithProperty($class, $propertyName)
108
    {
109 21
        Assert::string($class, 'First argument to Reflection::getReflectionClassWithProperty must be string. Variable of type "%s" was given.');
110 21
        Assert::classExists($class, 'Could not find class "%s"');
111
112 20
        $refl = new \ReflectionClass($class);
113 20
        if ($refl->hasProperty($propertyName)) {
114 19
            return $refl;
115
        }
116
117 9
        if (false === $parent = get_parent_class($class)) {
118
            // No more parents
119 1
            return;
120
        }
121
122 9
        return self::getReflectionClassWithProperty($parent, $propertyName);
123
    }
124
125
    /**
126
     * Get an reflection property that you can access directly.
127
     *
128
     * @param object|string $objectOrClass
129
     * @param string        $propertyName
130
     *
131
     * @return \ReflectionProperty
132
     *
133
     * @throws \InvalidArgumentException
134
     * @throws \LogicException           if the property is not found on the object
135
     */
136 25
    protected static function getAccessibleReflectionProperty($objectOrClass, $propertyName)
137
    {
138 25
        Assert::string($propertyName, 'Property name must be a string. Variable of type "%s" was given.');
139
140 23
        if (is_string($objectOrClass)) {
141 5
            $class = $objectOrClass;
142
        } else {
143 18
            Assert::object($objectOrClass, 'Can not get a property of a non object. Variable of type "%s" was given.');
144 16
            Assert::notInstanceOf($objectOrClass, '\stdClass', 'Can not get a property of \stdClass.');
145 15
            $class = get_class($objectOrClass);
146
        }
147
148 20
        if (null === $refl = static::getReflectionClassWithProperty($class, $propertyName)) {
149 1
            throw new \LogicException(sprintf('The property %s does not exist on %s or any of its parents.', $propertyName, $class));
150
        }
151
152 19
        $property = $refl->getProperty($propertyName);
153 19
        $property->setAccessible(true);
154
155 19
        if (!$property->isStatic()) {
156 11
            Assert::object($objectOrClass, 'Can not access non-static property without an object.');
157
        }
158
159 18
        return $property;
160
    }
161
}
162