Completed
Branch master (f7d4c1)
by Tobias
02:09
created

Reflection::invokeMethod()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5

Importance

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