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\Arr;
20:
21: use ArrayIterator;
22: use Closure;
23: use RecursiveArrayIterator;
24: use RecursiveIteratorIterator;
25:
26: /**
27: * This helper contains functions to work with arrays.
28: *
29: * @mixin \GameQ\Helpers\Arr
30: *
31: * @package GameQ\Helpers
32: */
33: trait Recursively
34: {
35: /**
36: * This function is responsible for handling behaivour specific to PHP versions before 8.1.
37: *
38: * @param array &$data
39: * @param RecursiveIteratorIterator $recursiveIterator
40: * @param RecursiveArrayIterator $iterator
41: * @param Closure $callback
42: * @return void
43: */
44: protected static function handleArrayIteratorCopyOrReference(
45: array &$data,
46: RecursiveIteratorIterator $recursiveIterator,
47: RecursiveArrayIterator $iterator,
48: Closure $callback
49: ) {
50: // ArrayIterator before PHP 8.1 does use a copy instead of reference
51: if (PHP_VERSION_ID < 80100) {
52: // Hash the current state of the iterator
53: $hashes = static::hashes((array) $iterator);
54:
55: // Continue with the provided callback
56: $callback();
57:
58: // Determine if the current iterator has been modified
59: if (! empty($diff = array_diff_assoc(static::hashes((array) $iterator), $hashes))) {
60: // Determine path to the current iterator
61: $path = [];
62: for ($depth = 0; $depth < $recursiveIterator->getDepth(); $depth++) {
63: $path[] = $recursiveIterator->getSubIterator($depth)->key();
64: }
65:
66: // Process all modified values
67: foreach (array_keys($diff) as $modified) {
68: // Write the modified value to the original array
69: static::set($data, array_merge($path, [$modified]), $iterator->offsetGet($modified));
70: }
71: }
72: } else {
73: // There is no need to write back any changes when ArrayIterator does use a reference
74: $callback();
75: }
76: }
77:
78: protected static function getArrayIteratorCopyOrReference(array &$data, ArrayIterator $arrayIterator)
79: {
80: if (PHP_VERSION_ID < 80100) {
81: // Return the actual array reference
82: return $data;
83: } else {
84: // Return the ArrayIterator's internal reference
85: return $arrayIterator->getArrayCopy();
86: }
87: }
88: }
89: