1: <?php
2: /**
3: * This file is part of GameQ.
4: *
5: * GameQ is free software; you can redistribute it and/or modify
6: * it under the terms of the GNU Lesser General Public License as published by
7: * the Free Software Foundation; either version 3 of the License, or
8: * (at your option) any later version.
9: *
10: * GameQ is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: * GNU Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public License
16: * along with this program. If not, see <http://www.gnu.org/licenses/>.
17: */
18:
19: namespace GameQ\Helpers;
20:
21: use Closure;
22: use RecursiveArrayIterator;
23: use RecursiveIteratorIterator;
24:
25: /**
26: * This helper contains functions to work with arrays.
27: *
28: * @package GameQ\Helpers
29: */
30: class Arr
31: {
32: use Arr\Recursively;
33:
34: /**
35: * This helper does process each element of the provided array recursively.
36: * It does so allowing for modifications to the provided array and without
37: * using actual recursive calls.
38: *
39: * @param array $data
40: * @param Closure $callback
41: *
42: * @return array
43: */
44: public static function recursively(array $data, Closure $callback)
45: {
46: // Initialize the RecursiveArrayIterator for the provided data
47: $arrayIterator = new RecursiveArrayIterator($data);
48:
49: // Configure the Iterator for the RecursiveIterator
50: $recursiveIterator = new RecursiveIteratorIterator($arrayIterator);
51:
52: // Traverse the provided data
53: foreach ($recursiveIterator as $key => $value) {
54: // Get the current sub iterator with Type hinting
55: /** @var RecursiveArrayIterator */
56: $subIterator = $recursiveIterator->getSubIterator();
57:
58: // Wrap the implementation to handle PHP < 8.1 behaviour
59: static::handleArrayIteratorCopyOrReference(
60: $data,
61: $recursiveIterator,
62: $subIterator,
63: function () use ($callback, &$value, $key, $subIterator) {
64: // Execute the callback
65: $callback($value, $key, $subIterator);
66:
67: // Update the modified value
68: $subIterator->offsetSet($key, $value);
69: }
70: );
71: }
72:
73: // Return the processed data
74: return static::getArrayIteratorCopyOrReference($data, $arrayIterator);
75: }
76:
77: /**
78: * This helper is intended to hash the provided array's values
79: * and return it back as key => hash.
80: *
81: * @param array $array
82: * @return array<string|int, string>
83: */
84: public static function hashes(array $array)
85: {
86: $hashes = [];
87:
88: // Process the provided array
89: foreach ($array as $key => $value) {
90: // Serialze and hash each value individually
91: $hashes[$key] = md5(serialize($value));
92: }
93:
94: // Return array containing the hashes
95: return $hashes;
96: }
97:
98: /**
99: * This helper is intended to set a value inside the provided array.
100: *
101: * @param array &$array
102: * @param array $path
103: * @param mixed $value
104: * @return array
105: */
106: public static function set(array &$array, array $path, $value)
107: {
108: $current = &$array;
109:
110: // Process the path until the last element
111: foreach ($path as $i => $element) {
112: // Remove the element from the path
113: unset($path[$i]);
114:
115: // Create missing key
116: if (! isset($current[$element])) {
117: $current[$element] = [];
118: }
119:
120: // Set current to a reference of next
121: $current = &$current[$element];
122: }
123:
124: // Finally set the value using the last key
125: $current = $value;
126:
127: // Return the current, modified array (level)
128: return $array;
129: }
130:
131: /**
132: * This helper method is intended to shift the provided arguments to the left.
133: *
134: * **Example:** foo, bar, baz becomes bar, baz, baz
135: *
136: * @param mixed &...$args
137: * @return void
138: */
139: public static function shift(&...$args)
140: {
141: // Get the array keys to ensure numeric index
142: $keys = array_keys($args);
143:
144: // Iterate the provided arguments keys in order
145: foreach ($keys as $i => $key) {
146: // Process until the last argument
147: if ($i < count($keys) - 1) {
148: // Shift next into current
149: $args[$key] = $args[$keys[$i + 1]];
150: }
151: }
152: }
153: }
154: