From 050e368bd091cf66cca7e2d191fc2dfb184c8d6f Mon Sep 17 00:00:00 2001 From: Hunter Perrin Date: Wed, 1 Oct 2014 11:22:44 -0700 Subject: [PATCH] Improvements to Hash::expand and Hash::merge. Because of the recursion in these functions, processing very large arrays would take a very long time. I rewrote the functions to eliminate any unnecessary recursion and function calls. Large arrays are now processed much faster. --- lib/Cake/Utility/Hash.php | 51 ++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/Cake/Utility/Hash.php b/lib/Cake/Utility/Hash.php index 6cb5c05d4..3d659a54f 100644 --- a/lib/Cake/Utility/Hash.php +++ b/lib/Cake/Utility/Hash.php @@ -622,6 +622,9 @@ class Hash { */ public static function expand($data, $separator = '.') { $result = array(); + + $stack = array(); + foreach ($data as $flat => $value) { $keys = explode($separator, $flat); $keys = array_reverse($keys); @@ -634,7 +637,24 @@ class Hash { $k => $child ); } - $result = self::merge($result, $child); + + $stack[] = array($child, &$result); + + while (!empty($stack)) { + foreach ($stack as $curKey => &$curMerge) { + foreach ($curMerge[0] as $key => &$val) { + if (!empty($curMerge[1][$key]) && (array)$curMerge[1][$key]===$curMerge[1][$key] && (array)$val===$val) { + $stack[] = array(&$val, &$curMerge[1][$key]); + } elseif ((int)$key===$key && isset($curMerge[1][$key])) { + $curMerge[1][] = $val; + } else { + $curMerge[1][$key] = $val; + } + } + unset($stack[$curKey]); + } + unset($curMerge); + } } return $result; } @@ -654,19 +674,28 @@ class Hash { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::merge */ public static function merge(array $data, $merge) { - $args = func_get_args(); - $return = current($args); + $args = array_slice(func_get_args(), 1); + $return = $data; - while (($arg = next($args)) !== false) { - foreach ((array)$arg as $key => $val) { - if (!empty($return[$key]) && is_array($return[$key]) && is_array($val)) { - $return[$key] = self::merge($return[$key], $val); - } elseif (is_int($key) && isset($return[$key])) { - $return[] = $val; - } else { - $return[$key] = $val; + foreach ($args as &$curArg) { + $stack[] = array((array)$curArg, &$return); + } + unset($curArg); + + while (!empty($stack)) { + foreach ($stack as $curKey => &$curMerge) { + foreach ($curMerge[0] as $key => &$val) { + if (!empty($curMerge[1][$key]) && (array)$curMerge[1][$key]===$curMerge[1][$key] && (array)$val===$val) { + $stack[] = array(&$val, &$curMerge[1][$key]); + } elseif ((int)$key===$key && isset($curMerge[1][$key])) { + $curMerge[1][] = $val; + } else { + $curMerge[1][$key] = $val; + } } + unset($stack[$curKey]); } + unset($curMerge); } return $return; }