: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// $type can be overloaded for case insensitive sort
$type += ['ignoreCase' => false, 'type' => 'regular'];
$ignoreCase = $type['ignoreCase'];
$type = strtolower($type);
if ($type === 'numeric') {
} elseif ($type === 'string') {
} elseif ($type === 'natural') {
} elseif ($type === 'locale') {
$type = \SORT_LOCALE_STRING;
$values = array_map('mb_strtolower', $values);
array_multisort($values, $dir, $type, $keys, $dir, $type);
$keys = array_unique($keys);
if (isset($originalKeys[$k])) {
$sorted[$originalKeys[$k]] = $data[$originalKeys[$k]];
* Helper method for sort()
* Squashes an array to a single hash so it can be sorted.
* @param array $data The data to squash.
* @param string|null $key The key for the data.
protected static function _squash(array $data, $key = null)
foreach ($data as $k => $r) {
if (is_array($r) && !empty($r)) {
$stack = array_merge($stack, static::_squash($r, $id));
$stack[] = ['id' => $id, 'value' => $r];
* Computes the difference between two complex arrays.
* This method differs from the built-in array_diff() in that it will preserve keys
* and work on multi-dimensional arrays.
* @param array $data First value
* @param array $compare Second value
* @return array Returns the key => value pairs that are not common in $data and $compare
* The expression for this function is ($data - $compare) + ($compare - ($data - $compare))
* @link https://book.cakephp.org/3/en/core-libraries/hash.html#Cake\Utility\Hash::diff
public static function diff(array $data, array $compare)
$intersection = array_intersect_key($data, $compare);
while (($key = key($intersection)) !== null) {
if ($data[$key] == $compare[$key]) {
unset($data[$key], $compare[$key]);
* Merges the difference between $data and $compare onto $data.
* @param array $data The data to append onto.
* @param array $compare The data to compare and append onto.
* @return array The merged array.
* @link https://book.cakephp.org/3/en/core-libraries/hash.html#Cake\Utility\Hash::mergeDiff
public static function mergeDiff(array $data, array $compare)
if (empty($data) && !empty($compare)) {
foreach ($compare as $key => $value) {
if (!array_key_exists($key, $data)) {
} elseif (is_array($value) && is_array($data[$key])) {
$data[$key] = static::mergeDiff($data[$key], $value);
* Normalizes an array, and converts it to a standard format.
* @param array $data List to normalize
* @param bool $assoc If true, $data will be converted to an associative array.
* @link https://book.cakephp.org/3/en/core-libraries/hash.html#Cake\Utility\Hash::normalize
public static function normalize(array $data, $assoc = true)
$keys = array_keys($data);
for ($i = 0; $i < $count; $i++) {
if (!is_int($keys[$i])) {
if (!$numeric || $assoc) {
for ($i = 0; $i < $count; $i++) {
$newList[$data[$keys[$i]]] = null;
$newList[$keys[$i]] = $data[$keys[$i]];
* Takes in a flat array and returns a nested array
* - `children` The key name to use in the resultset for children.
* - `idPath` The path to a key that identifies each entry. Should be
* compatible with Hash::extract(). Defaults to `{n}.$alias.id`
* - `parentPath` The path to a key that identifies the parent of each entry.
* Should be compatible with Hash::extract(). Defaults to `{n}.$alias.parent_id`
* - `root` The id of the desired top-most result.
* @param array $data The data to nest.
* @param array $options Options are:
* @return array of results, nested
* @see \Cake\Utility\Hash::extract()
* @throws \InvalidArgumentException When providing invalid data.
* @link https://book.cakephp.org/3/en/core-libraries/hash.html#Cake\Utility\Hash::nest
public static function nest(array $data, array $options = [])
$alias = key(current($data));
'idPath' => "{n}.$alias.id",
'parentPath' => "{n}.$alias.parent_id",
'children' => 'children',
$ids = static::extract($data, $options['idPath']);
$idKeys = explode('.', $options['idPath']);
$parentKeys = explode('.', $options['parentPath']);
array_shift($parentKeys);
foreach ($data as $result) {
$result[$options['children']] = [];
$id = static::get($result, $idKeys);
$parentId = static::get($result, $parentKeys);
if (isset($idMap[$id][$options['children']])) {
$idMap[$id] = array_merge($result, (array)$idMap[$id]);
$idMap[$id] = array_merge($result, [$options['children'] => []]);
if (!$parentId || !in_array($parentId, $ids)) {
$return[] =& $idMap[$id];
$idMap[$parentId][$options['children']][] =& $idMap[$id];
throw new InvalidArgumentException('Invalid data array to nest.');
$root = $options['root'];
$root = static::get($return[0], $parentKeys);
foreach ($return as $i => $result) {
$id = static::get($result, $idKeys);
$parentId = static::get($result, $parentKeys);
if ($id !== $root && $parentId != $root) {
return array_values($return);