Completed
Push — master ( 59dd4d...b5d47e )
by Marwan
15s queued 11s
created

ClassProxyTrait::castObjectToClass()   B

Complexity

Conditions 6
Paths 23

Size

Total Lines 41
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

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