Passed
Push — master ( bbc3bd...e70b0b )
by Marwan
01:25
created

Misc::getObjectProperty()   A

Complexity

Conditions 5
Paths 1

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 16
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 22
ccs 15
cts 15
cp 1
crap 5
rs 9.4222
1
<?php
2
3
/**
4
 * @author Marwan Al-Soltany <[email protected]>
5
 * @copyright Marwan Al-Soltany 2021
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\Velox\Helper;
13
14
use MAKS\Velox\Backend\Config;
15
16
/**
17
 * A class that serves as a holder for various miscellaneous utility function.
18
 */
19
final class Misc
20
{
21
    /**
22
     * Gets a value from an array via dot-notation.
23
     *
24
     * @param array &$array The array to get the value from.
25
     * @param string $key The dotted key representation.
26
     * @param mixed $default [optional] The default fallback value.
27
     *
28
     * @return mixed The requested value if found otherwise the default parameter.
29
     */
30 35
    public static function getArrayValueByKey(array &$array, string $key, $default = null)
31
    {
32 35
        if (!count($array)) {
33 3
            return $default;
34
        }
35
36 35
        $data = &$array;
37
38 35
        if (strpos($key, '.') !== false) {
39 21
            $parts = explode('.', $key);
40
41 21
            foreach ($parts as $part) {
42 21
                if (!array_key_exists($part, $data)) {
43 4
                    return $default;
44
                }
45
46 21
                $data = &$data[$part];
47
            }
48
49 21
            return $data;
50
        }
51
52 19
        return array_key_exists($key, $data) ? $data[$key] : $default;
53
    }
54
55
    /**
56
     * Sets a value of an array via dot-notation.
57
     *
58
     * @param array $array The array to set the value in.
59
     * @param string $key The string key representation.
60
     * @param mixed $value The value to set.
61
     *
62
     * @return bool True on success.
63
     */
64 31
    public static function setArrayValueByKey(array &$array, string $key, $value): bool
65
    {
66 31
        if (!strlen($key)) {
67 1
            return false;
68
        }
69
70 31
        $parts = explode('.', $key);
71 31
        $lastPart = array_pop($parts);
72
73 31
        $data = &$array;
74
75 31
        if (!empty($parts)) {
76 6
            foreach ($parts as $part) {
77 6
                if (!isset($data[$part])) {
78 4
                    $data[$part] = [];
79
                }
80
81 6
                $data = &$data[$part];
82
            }
83
        }
84
85 31
        $data[$lastPart] = $value;
86
87 31
        return true;
88
    }
89
90
91
    /**
92
     * Gets a private, protected, or public property (default, static, or constant) of an object.
93
     *
94
     * @param object $object Class instance.
95
     * @param string $property Property name.
96
     *
97
     * @return mixed The property value.
98
     *
99
     * @throws \Exception On failure.
100
     */
101 6
    public static function getObjectProperty($object, string $property)
102
    {
103 6
        return \Closure::bind(function ($object, $property) {
104 6
            $return = null;
105
106
            try {
107 6
                $class = get_class($object);
108 6
                if (defined($class . '::' . $property)) {
109 1
                    $return = constant($class . '::' . $property);
110 6
                } elseif (isset($object::$$property)) {
111 5
                    $return = $object::$$property;
112 3
                } elseif (isset($object->{$property})) {
113 3
                    $return = $object->{$property};
114
                } else {
115 6
                    throw new \Exception("No default, static, or constant property with the name '{$property}' exists!");
116
                }
117 1
            } catch (\Exception $error) {
118 1
                throw new \Exception(sprintf('%s() failed!', __METHOD__), $error->getCode(), $error);
119
            }
120
121 6
            return $return;
122 6
        }, null, $object)($object, $property);
123
    }
124
125
    /**
126
     * Sets a private, protected, or public property (default or static) of an object.
127
     *
128
     * @param object $object Class instance.
129
     * @param string $property Property name.
130
     * @param string $value Property value.
131
     *
132
     * @return mixed The new property value.
133
     *
134
     * @throws \Exception On failure.
135
     */
136 13
    public static function setObjectProperty($object, string $property, $value)
137
    {
138 13
        return \Closure::bind(function ($object, $property, $value) {
139 13
            $return = null;
140
141
            try {
142 13
                if (isset($object::$$property)) {
143 13
                    $return = $object::$$property = $value;
144 1
                } elseif (isset($object->{$property})) {
145 1
                    $return = $object->{$property} = $value;
146
                } else {
147 13
                    throw new \Exception("No default, static, or constant property with the name '{$property}' exists!");
148
                }
149 1
            } catch (\Exception $error) {
150 1
                throw new \Exception(sprintf('%s() failed!', __METHOD__), $error->getCode(), $error);
151
            }
152
153 13
            return $return;
154 13
        }, null, $object)($object, $property, $value);
155
    }
156
157
    /**
158
     * Calls a private, protected, or public method on an object.
159
     *
160
     * @param object $object Class instance.
161
     * @param string $method Method name.
162
     * @param mixed ...$arguments
163
     *
164
     * @return mixed The function result, or false on error.
165
     *
166
     * @throws \Exception On failure or if the called function threw an exception.
167
     */
168 1
    public static function callObjectMethod($object, string $method, ...$arguments)
169
    {
170 1
        return \Closure::bind(function ($object, $method, $arguments) {
171
            try {
172 1
                return call_user_func_array([$object, $method], $arguments);
173 1
            } catch (\Exception $error) {
174 1
                throw new \Exception(sprintf('%s() failed!', __METHOD__), $error->getCode(), $error);
175
            }
176 1
        }, null, $object)($object, $method, $arguments);
177
    }
178
179
    /**
180
     * Interpolates context values into text placeholders.
181
     *
182
     * @param string $text The text to interpolate.
183
     * @param array $context An associative array like `['varName' => 'varValue']`.
184
     * @param string $placeholderIndicator The wrapper that indicate a variable. Max 2 chars, anything else will be ignored and "{}" will be used instead.
185
     *
186
     * @return string The interpolated string.
187
     */
188 10
    public static function interpolate(string $text, array $context = [], string $placeholderIndicator = '{}'): string
189
    {
190 10
        if (strlen($placeholderIndicator) !== 2) {
191 1
            $placeholderIndicator = '{}';
192
        }
193
194 10
        $replacements = [];
195 10
        foreach ($context as $key => $value) {
196
            // check that the value can be cast to string
197 10
            if (!is_array($value) && (!is_object($value) || method_exists($value, '__toString'))) {
198 10
                $replacements[$placeholderIndicator[0] . $key . $placeholderIndicator[1]] = $value;
199
            }
200
        }
201
202 10
        return strtr($text, $replacements);
203
    }
204
205
    /**
206
     * Returns the passed key(s) from the backtrace.
207
     *
208
     * @param null|string|string[] $pluck [optional] $pluck The key to to get as a string or an array of strings (keys) from this list `[file, line, function, class, type, args]`, passing `null` will return the entire backtrace.
209
     * @param int $offset [optional] The offset of the backtrace, passing `-1` will reference the last item in the backtrace.
210
     *
211
     * @return string|int|array|null A string or int if a string is passed, an array if an array or null is passed, and null if no match was found.
212
     */
213 5
    public static function backtrace($pluck = null, ?int $offset = 0)
214
    {
215 5
        $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
216 5
        $plucked = null;
217
218 5
        if ($pluck === null) {
219 1
            return $backtrace;
220
        }
221
222 5
        if ($offset === -1) {
223 1
            $offset = 0;
224 1
            $backtrace = array_reverse($backtrace);
225
        }
226
227 5
        if (count($backtrace) < $offset + 1) {
228 1
            return null;
229 5
        } elseif (is_string($pluck)) {
230 3
            $plucked = isset($backtrace[$offset][$pluck]) ? $backtrace[$offset][$pluck] : null;
231 3
        } elseif (is_array($pluck)) {
0 ignored issues
show
introduced by
The condition is_array($pluck) is always true.
Loading history...
232 3
            $plucked = [];
233 3
            foreach ($pluck as $key) {
234 3
                !isset($backtrace[$offset][$key]) ?: $plucked[$key] = $backtrace[$offset][$key];
235
            }
236
        }
237
238 5
        return is_string($plucked) || is_array($plucked) && count($plucked, COUNT_RECURSIVE) ? $plucked : null;
239
    }
240
}
241