'john.doe')); * * // Returns FALSE * Arr::is_assoc('foo', 'bar'); * * @param array $array array to check * @return boolean */ public static function is_assoc(array $array) { // Keys of the array $keys = array_keys($array); // If the array keys of the keys match the keys, then the array must // not be associative (e.g. the keys array looked like {0:0, 1:1...}). return array_keys($keys) !== $keys; } /** * Test if a value is an array with an additional check for array-like objects. * * // Returns TRUE * Arr::is_array(array()); * Arr::is_array(new ArrayObject); * * // Returns FALSE * Arr::is_array(FALSE); * Arr::is_array('not an array!'); * Arr::is_array(Database::instance()); * * @param mixed $value value to check * @return boolean */ public static function is_array($value) { if (is_array($value)) { // Definitely an array return TRUE; } else { // Possibly a Traversable object, functionally the same as an array return (is_object($value) AND $value instanceof Traversable); } } /** * Gets a value from an array using a dot separated path. * * // Get the value of $array['foo']['bar'] * $value = Arr::path($array, 'foo.bar'); * * Using a wildcard "*" will search intermediate arrays and return an array. * * // Get the values of "color" in theme * $colors = Arr::path($array, 'theme.*.color'); * * // Using an array of keys * $colors = Arr::path($array, array('theme', '*', 'color')); * * @param array $array array to search * @param mixed $path key path string (delimiter separated) or array of keys * @param mixed $default default value if the path is not set * @param string $delimiter key path delimiter * @return mixed */ public static function path($array, $path, $default = NULL, $delimiter = NULL) { if ( ! Arr::is_array($array)) { // This is not an array! return $default; } if (is_array($path)) { // The path has already been separated into keys $keys = $path; } else { if (array_key_exists($path, $array)) { // No need to do extra processing return $array[$path]; } if ($delimiter === NULL) { // Use the default delimiter $delimiter = Arr::$delimiter; } // Remove starting delimiters and spaces $path = ltrim($path, "{$delimiter} "); // Remove ending delimiters, spaces, and wildcards $path = rtrim($path, "{$delimiter} *"); // Split the keys by delimiter $keys = explode($delimiter, $path); } do { $key = array_shift($keys); if (ctype_digit($key)) { // Make the key an integer $key = (int) $key; } if (isset($array[$key])) { if ($keys) { if (Arr::is_array($array[$key])) { // Dig down into the next part of the path $array = $array[$key]; } else { // Unable to dig deeper break; } } else { // Found the path requested return $array[$key]; } } elseif ($key === '*') { // Handle wildcards $values = array(); foreach ($array as $arr) { if ($value = Arr::path($arr, implode('.', $keys))) { $values[] = $value; } } if ($values) { // Found the values requested return $values; } else { // Unable to dig deeper break; } } else { // Unable to dig deeper break; } } while ($keys); // Unable to find the value requested return $default; } /** * Set a value on an array by path. * * @see Arr::path() * @param array $array Array to update * @param string $path Path * @param mixed $value Value to set * @param string $delimiter Path delimiter */ public static function set_path( & $array, $path, $value, $delimiter = NULL) { if ( ! $delimiter) { // Use the default delimiter $delimiter = Arr::$delimiter; } // The path has already been separated into keys $keys = $path; if ( ! is_array($path)) { // Split the keys by delimiter $keys = explode($delimiter, $path); } // Set current $array to inner-most array path while (count($keys) > 1) { $key = array_shift($keys); if (ctype_digit($key)) { // Make the key an integer $key = (int) $key; } if ( ! isset($array[$key])) { $array[$key] = array(); } $array = & $array[$key]; } // Set key on inner-most array $array[array_shift($keys)] = $value; } /** * Fill an array with a range of numbers. * * // Fill an array with values 5, 10, 15, 20 * $values = Arr::range(5, 20); * * @param integer $step stepping * @param integer $max ending number * @return array */ public static function range($step = 10, $max = 100) { if ($step < 1) return array(); $array = array(); for ($i = $step; $i <= $max; $i += $step) { $array[$i] = $i; } return $array; } /** * Retrieve a single key from an array. If the key does not exist in the * array, the default value will be returned instead. * * // Get the value "username" from $_POST, if it exists * $username = Arr::get($_POST, 'username'); * * // Get the value "sorting" from $_GET, if it exists * $sorting = Arr::get($_GET, 'sorting'); * * @param array $array array to extract from * @param string $key key name * @param mixed $default default value * @return mixed */ public static function get($array, $key, $default = NULL) { if ($array instanceof ArrayObject) { // This is a workaround for inconsistent implementation of isset between PHP and HHVM // See https://github.com/facebook/hhvm/issues/3437 return $array->offsetExists($key) ? $array->offsetGet($key) : $default; } else { return isset($array[$key]) ? $array[$key] : $default; } } /** * Retrieves multiple paths from an array. If the path does not exist in the * array, the default value will be added instead. * * // Get the values "username", "password" from $_POST * $auth = Arr::extract($_POST, array('username', 'password')); * * // Get the value "level1.level2a" from $data * $data = array('level1' => array('level2a' => 'value 1', 'level2b' => 'value 2')); * Arr::extract($data, array('level1.level2a', 'password')); * * @param array $array array to extract paths from * @param array $paths list of path * @param mixed $default default value * @return array */ public static function extract($array, array $paths, $default = NULL) { $found = array(); foreach ($paths as $path) { Arr::set_path($found, $path, Arr::path($array, $path, $default)); } return $found; } /** * Retrieves muliple single-key values from a list of arrays. * * // Get all of the "id" values from a result * $ids = Arr::pluck($result, 'id'); * * [!!] A list of arrays is an array that contains arrays, eg: array(array $a, array $b, array $c, ...) * * @param array $array list of arrays to check * @param string $key key to pluck * @return array */ public static function pluck($array, $key) { $values = array(); foreach ($array as $row) { if (isset($row[$key])) { // Found a value in this row $values[] = $row[$key]; } } return $values; } /** * Adds a value to the beginning of an associative array. * * // Add an empty value to the start of a select list * Arr::unshift($array, 'none', 'Select a value'); * * @param array $array array to modify * @param string $key array key name * @param mixed $val array value * @return array */ public static function unshift( array & $array, $key, $val) { $array = array_reverse($array, TRUE); $array[$key] = $val; $array = array_reverse($array, TRUE); return $array; } /** * Recursive version of [array_map](http://php.net/array_map), applies one or more * callbacks to all elements in an array, including sub-arrays. * * // Apply "strip_tags" to every element in the array * $array = Arr::map('strip_tags', $array); * * // Apply $this->filter to every element in the array * $array = Arr::map(array(array($this,'filter')), $array); * * // Apply strip_tags and $this->filter to every element * $array = Arr::map(array('strip_tags',array($this,'filter')), $array); * * [!!] Because you can pass an array of callbacks, if you wish to use an array-form callback * you must nest it in an additional array as above. Calling Arr::map(array($this,'filter'), $array) * will cause an error. * [!!] Unlike `array_map`, this method requires a callback and will only map * a single array. * * @param mixed $callbacks array of callbacks to apply to every element in the array * @param array $array array to map * @param array $keys array of keys to apply to * @return array */ public static function map($callbacks, $array, $keys = NULL) { foreach ($array as $key => $val) { if (is_array($val)) { $array[$key] = Arr::map($callbacks, $array[$key], $keys); } elseif ( ! is_array($keys) OR in_array($key, $keys)) { if (is_array($callbacks)) { foreach ($callbacks as $cb) { $array[$key] = call_user_func($cb, $array[$key]); } } else { $array[$key] = call_user_func($callbacks, $array[$key]); } } } return $array; } /** * Recursively merge two or more arrays. Values in an associative array * overwrite previous values with the same key. Values in an indexed array * are appended, but only when they do not already exist in the result. * * Note that this does not work the same as [array_merge_recursive](http://php.net/array_merge_recursive)! * * $john = array('name' => 'john', 'children' => array('fred', 'paul', 'sally', 'jane')); * $mary = array('name' => 'mary', 'children' => array('jane')); * * // John and Mary are married, merge them together * $john = Arr::merge($john, $mary); * * // The output of $john will now be: * array('name' => 'mary', 'children' => array('fred', 'paul', 'sally', 'jane')) * * @param array $array1 initial array * @param array $array2,... array to merge * @return array */ public static function merge($array1, $array2) { if (Arr::is_assoc($array2)) { foreach ($array2 as $key => $value) { if (is_array($value) AND isset($array1[$key]) AND is_array($array1[$key]) ) { $array1[$key] = Arr::merge($array1[$key], $value); } else { $array1[$key] = $value; } } } else { foreach ($array2 as $value) { if ( ! in_array($value, $array1, TRUE)) { $array1[] = $value; } } } if (func_num_args() > 2) { foreach (array_slice(func_get_args(), 2) as $array2) { if (Arr::is_assoc($array2)) { foreach ($array2 as $key => $value) { if (is_array($value) AND isset($array1[$key]) AND is_array($array1[$key]) ) { $array1[$key] = Arr::merge($array1[$key], $value); } else { $array1[$key] = $value; } } } else { foreach ($array2 as $value) { if ( ! in_array($value, $array1, TRUE)) { $array1[] = $value; } } } } } return $array1; } /** * Overwrites an array with values from input arrays. * Keys that do not exist in the first array will not be added! * * $a1 = array('name' => 'john', 'mood' => 'happy', 'food' => 'bacon'); * $a2 = array('name' => 'jack', 'food' => 'tacos', 'drink' => 'beer'); * * // Overwrite the values of $a1 with $a2 * $array = Arr::overwrite($a1, $a2); * * // The output of $array will now be: * array('name' => 'jack', 'mood' => 'happy', 'food' => 'tacos') * * @param array $array1 master array * @param array $array2 input arrays that will overwrite existing values * @return array */ public static function overwrite($array1, $array2) { foreach (array_intersect_key($array2, $array1) as $key => $value) { $array1[$key] = $value; } if (func_num_args() > 2) { foreach (array_slice(func_get_args(), 2) as $array2) { foreach (array_intersect_key($array2, $array1) as $key => $value) { $array1[$key] = $value; } } } return $array1; } /** * Creates a callable function and parameter list from a string representation. * Note that this function does not validate the callback string. * * // Get the callback function and parameters * list($func, $params) = Arr::callback('Foo::bar(apple,orange)'); * * // Get the result of the callback * $result = call_user_func_array($func, $params); * * @param string $str callback string * @return array function, params */ public static function callback($str) { // Overloaded as parts are found $command = $params = NULL; // command[param,param] if (preg_match('/^([^\(]*+)\((.*)\)$/', $str, $match)) { // command $command = $match[1]; if ($match[2] !== '') { // param,param $params = preg_split('/(? array('one' => 'something'), 'two' => 'other'); * * // Flatten the array * $array = Arr::flatten($array); * * // The array will now be * array('one' => 'something', 'two' => 'other'); * * [!!] The keys of array values will be discarded. * * @param array $array array to flatten * @return array * @since 3.0.6 */ public static function flatten($array) { $is_assoc = Arr::is_assoc($array); $flat = array(); foreach ($array as $key => $value) { if (is_array($value)) { $flat = array_merge($flat, Arr::flatten($value)); } else { if ($is_assoc) { $flat[$key] = $value; } else { $flat[] = $value; } } } return $flat; } }