ClassProxyTrait::castObjectToClass()   B
last analyzed

Complexity

Conditions 7
Paths 24

Size

Total Lines 50
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 7.0178

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 30
c 2
b 0
f 0
dl 0
loc 50
ccs 26
cts 28
cp 0.9286
rs 8.5066
cc 7
nc 24
nop 2
crap 7.0178
1
<?php
2
3
/**
4
 * @author Marwan Al-Soltany <[email protected]>
5
 * @copyright Marwan Al-Soltany 2020
6
 * For the full copyright and license information, please view
7
 * the LICENSE file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace MAKS\AmqpAgent\Helper;
13
14
use Closure;
15
use Exception;
16
use ReflectionClass;
17
use ReflectionObject;
18
use ReflectionException;
19
use MAKS\AmqpAgent\Exception\AmqpAgentException;
20
21
/**
22
 * A trait containing methods for proxy methods calling, properties manipulation, and class utilities.
23
 * @since 2.0.0
24
 */
25
trait ClassProxyTrait
26
{
27
    /**
28
     * Calls a private, protected, or public method on an object.
29
     * @param object $object Class instance.
30
     * @param string $method Method name.
31
     * @param mixed ...$arguments
32
     * @return mixed The function result, or false on error.
33
     * @throws AmqpAgentException On failure or if the called function threw an exception.
34
     */
35 5
    public static function callMethod($object, string $method, ...$arguments)
36
    {
37 5
        return call_user_func(
38 5
            Closure::bind(
39
                function () use ($object, $method, $arguments) {
40
                    try {
41 5
                        return call_user_func_array(
42
                            array(
43 5
                                $object,
44 5
                                $method
45
                            ),
46 5
                            $arguments
47
                        );
48 3
                    } catch (Exception $error) {
49 3
                        AmqpAgentException::rethrow($error, sprintf('%s::%s() failed!', static::class, __FUNCTION__), false);
50
                    }
51 5
                },
52 5
                null,
53 5
                $object
54
            )
55
        );
56
    }
57
58
    /**
59
     * Gets a private, protected, or public property (default, static, or constant) of an object.
60
     * @param object $object Class instance.
61
     * @param string $property Property name.
62
     * @return mixed The property value.
63
     * @throws AmqpAgentException On failure.
64
     */
65 2
    public static function getProperty($object, string $property)
66
    {
67 2
        return call_user_func(
68 2
            Closure::bind(
69
                function () use ($object, $property) {
70 2
                    $return = null;
71
                    try {
72 2
                        $class = get_class($object);
73 2
                        if (defined($class . '::' . $property)) {
74 1
                            $return = constant($class . '::' . $property);
75 2
                        } elseif (isset($object::$$property)) {
76 1
                            $return = $object::$$property;
77 2
                        } elseif (isset($object->{$property})) {
78 2
                            $return = $object->{$property};
79
                        } else {
80 1
                            throw new Exception(
81 1
                                sprintf(
82 1
                                    'No default, static, or constant property with the name "%s" exists!',
83 2
                                    $property
84
                                )
85
                            );
86
                        }
87 1
                    } catch (Exception $error) {
88 1
                        AmqpAgentException::rethrow($error, sprintf('%s::%s() failed!', static::class, __FUNCTION__), false);
89
                    }
90 2
                    return $return;
91 2
                },
92 2
                null,
93 2
                $object
94
            )
95
        );
96
    }
97
98
    /**
99
     * Sets a private, protected, or public property (default or static) of an object.
100
     * @param object $object Class instance.
101
     * @param string $property Property name.
102
     * @param string $value Property value.
103
     * @return mixed The new property value.
104
     * @throws AmqpAgentException On failure.
105
     */
106 3
    public static function setProperty($object, string $property, $value)
107
    {
108 3
        return call_user_func(
109 3
            Closure::bind(
110
                function () use ($object, $property, $value) {
111 3
                    $return = null;
112
                    try {
113 3
                        if (isset($object::$$property)) {
114 1
                            $return = $object::$$property = $value;
115 3
                        } elseif (isset($object->{$property})) {
116 2
                            $return = $object->{$property} = $value;
117
                        } else {
118 2
                            throw new Exception(
119 2
                                sprintf(
120 2
                                    'No default or static property with the name "%s" exists!',
121 3
                                    $property
122
                                )
123
                            );
124
                        }
125 2
                    } catch (Exception $error) {
126 2
                        AmqpAgentException::rethrow($error, sprintf('%s::%s() failed!', static::class, __FUNCTION__), false);
127
                    }
128 2
                    return $return;
129 3
                },
130 3
                null,
131 3
                $object
132
            )
133
        );
134
    }
135
136
    /**
137
     * Returns a reflection class instance on a class.
138
     * @param object|string $class Class instance or class FQN.
139
     * @return ReflectionClass
140
     * @throws ReflectionException
141
     */
142 1
    public static function reflectOnClass($class)
143
    {
144 1
        return new ReflectionClass($class);
145
    }
146
147
    /**
148
     * Returns a reflection object instance on an object.
149
     * @param object $object Class instance.
150
     * @return ReflectionObject
151
     */
152 2
    public static function reflectOnObject($object)
153
    {
154 2
        return new ReflectionObject($object);
155
    }
156
157
    /**
158
     * Tries to cast an object into a new class. Similar classes work best.
159
     * @param object $fromObject Class instance.
160
     * @param string $toClass Class FQN.
161
     * @return object
162
     * @throws AmqpAgentException When passing a wrong argument or on failure.
163
     */
164 3
    public static function castObjectToClass($fromObject, string $toClass)
165
    {
166 3
        if (!is_object($fromObject)) {
167 1
            throw new AmqpAgentException(
168 1
                sprintf(
169 1
                    'The first parameter must be an instance of class, a wrong parameter with (data-type: %s) was passed instead.',
170 1
                    gettype($fromObject)
171
                )
172
            );
173
        }
174
175 2
        if (!class_exists($toClass)) {
176 1
            throw new AmqpAgentException(
177 1
                sprintf(
178 1
                    'Unknown class: %s.',
179 1
                    $toClass
180
                )
181
            );
182
        }
183
184
        try {
185 1
            $toClass = new $toClass();
186
187 1
            $toClassReflection = self::reflectOnObject($toClass);
188 1
            $fromObjectReflection = self::reflectOnObject($fromObject);
189
190 1
            $fromObjectProperties = $fromObjectReflection->getProperties();
191
192 1
            foreach ($fromObjectProperties as $fromObjectProperty) {
193 1
                $fromObjectProperty->setAccessible(true);
194 1
                $name = $fromObjectProperty->getName();
195 1
                $value = $fromObjectProperty->getValue($fromObject);
196
197 1
                if ($toClassReflection->hasProperty($name)) {
198 1
                    $property = $toClassReflection->getProperty($name);
199 1
                    $property->setAccessible(true);
200 1
                    $property->setValue($toClass, $value);
201
                } else {
202
                    try {
203 1
                        self::setProperty($toClass, $name, $value);
204 1
                    } catch (Exception $e) {
205
                        // This exception means target object has a __set()
206
                        // magic method that prevents setting the property.
207
                    }
208
                }
209
            }
210
211 1
            return $toClass;
212
        } catch (Exception $error) {
213
            AmqpAgentException::rethrow($error, sprintf('%s::%s() failed!', static::class, __FUNCTION__), false);
214
        }
215
    }
216
}
217