3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2013
35 class CRM_Utils_Array
{
38 * if the key exists in the list returns the associated value
42 * @param string $key the key value
43 * @param array $list the array to be searched
44 * @param mixed $default
46 * @return mixed value if exists else $default
49 static function value($key, $list, $default = NULL) {
50 if (is_array($list)) {
51 return array_key_exists($key, $list) ?
$list[$key] : $default;
57 * Given a parameter array and a key to search for,
58 * search recursively for that key's value.
60 * @param array $values The parameter array
61 * @param string $key The key to search for
63 * @return mixed The value of the key, or null.
67 static function retrieveValueRecursive(&$params, $key) {
68 if (!is_array($params)) {
71 elseif ($value = CRM_Utils_Array
::value($key, $params)) {
75 foreach ($params as $subParam) {
76 if (is_array($subParam) &&
77 $value = self
::retrieveValueRecursive($subParam, $key)
87 * if the value exists in the list returns the associated key
91 * @param list the array to be searched
92 * @param value the search value
94 * @return key if exists else null
99 static function key($value, &$list) {
100 if (is_array($list)) {
101 $key = array_search($value, $list);
103 // array_search returns key if found, false otherwise
104 // it may return values like 0 or empty string which
105 // evaluates to false
106 // hence we must use identical comparison operator
107 return ($key === FALSE) ?
NULL : $key;
112 static function &xml(&$list, $depth = 1, $seperator = "\n") {
114 foreach ($list as $name => $value) {
115 $xml .= str_repeat(' ', $depth * 4);
116 if (is_array($value)) {
117 $xml .= "<{$name}>{$seperator}";
118 $xml .= self
::xml($value, $depth +
1, $seperator);
119 $xml .= str_repeat(' ', $depth * 4);
120 $xml .= "</{$name}>{$seperator}";
123 // make sure we escape value
124 $value = self
::escapeXML($value);
125 $xml .= "<{$name}>$value</{$name}>{$seperator}";
131 static function escapeXML($value) {
136 $src = array('&', '<', '>', '\ 1');
137 $dst = array('&', '<', '>', ',');
140 return str_replace($src, $dst, $value);
144 * Convert an array-tree to a flat array
146 * @param array $list the original, tree-shaped list
147 * @param array $flat the flat list to which items will be copied
148 * @param string $prefix
149 * @param string $seperator
151 static function flatten(&$list, &$flat, $prefix = '', $seperator = ".") {
152 foreach ($list as $name => $value) {
153 $newPrefix = ($prefix) ?
$prefix . $seperator . $name : $name;
154 if (is_array($value)) {
155 self
::flatten($value, $flat, $newPrefix, $seperator);
158 if (!empty($value)) {
159 $flat[$newPrefix] = $value;
166 * Convert an array with path-like keys into a tree of arrays
168 * @param $delim A path delimiter
169 * @param $arr A one-dimensional array indexed by string keys
171 * @return array-encoded tree
173 function unflatten($delim, &$arr) {
175 foreach ($arr as $key => $value) {
176 $path = explode($delim, $key);
178 while (count($path) > 1) {
179 $key = array_shift($path);
180 if (!isset($node[$key])) {
181 $node[$key] = array();
183 $node = &$node[$key];
186 $key = array_shift($path);
187 $node[$key] = $value;
193 * Funtion to merge to two arrays recursively
201 static function crmArrayMerge($a1, $a2) {
211 foreach ($a1 as $key => $value) {
212 if (array_key_exists($key, $a2) &&
213 is_array($a2[$key]) && is_array($a1[$key])
215 $a3[$key] = array_merge($a1[$key], $a2[$key]);
218 $a3[$key] = $a1[$key];
222 foreach ($a2 as $key => $value) {
223 if (array_key_exists($key, $a1)) {
224 // already handled in above loop
227 $a3[$key] = $a2[$key];
233 static function isHierarchical(&$list) {
234 foreach ($list as $n => $v) {
245 * @params array $array
246 * @params int $maxdepth
249 * @return array copy of the array
254 static function array_deep_copy(&$array, $maxdepth = 50, $depth = 0) {
255 if ($depth > $maxdepth) {
259 foreach ($array as $key => $value) {
260 if (is_array($value)) {
261 array_deep_copy($value, $copy[$key], $maxdepth, ++
$depth);
264 $copy[$key] = $value;
271 * In some cases, functions return an array by reference, but we really don't
272 * want to receive a reference.
277 static function breakReference($array) {
283 * Array splice function that preserves associative keys
284 * defauly php array_splice function doesnot preserve keys
285 * So specify start and end of the array that you want to remove
287 * @param array $params array to slice
288 * @param Integer $start
289 * @param Integer $end
294 static function crmArraySplice(&$params, $start, $end) {
295 // verify start and end date
299 if ($end > count($params)) {
300 $end = count($params);
305 // procees unset operation
306 foreach ($params as $key => $value) {
307 if ($i >= $start && $i < $end) {
308 unset($params[$key]);
315 * Function for case insensitive in_array search
317 * @param $value value or search string
318 * @param $params array that need to be searched
319 * @param $caseInsensitive boolean true or false
323 static function crmInArray($value, $params, $caseInsensitive = TRUE) {
324 foreach ($params as $item) {
325 if (is_array($item)) {
326 $ret = crmInArray($value, $item, $caseInsensitive);
329 $ret = ($caseInsensitive) ?
strtolower($item) == strtolower($value) : $item == $value;
339 * This function is used to convert associative array names to values
342 * This function is used by both the web form layer and the api. Note that
343 * the api needs the name => value conversion, also the view layer typically
344 * requires value => name conversion
346 static function lookupValue(&$defaults, $property, $lookup, $reverse) {
347 $id = $property . '_id';
349 $src = $reverse ?
$property : $id;
350 $dst = $reverse ?
$id : $property;
352 if (!array_key_exists(strtolower($src), array_change_key_case($defaults, CASE_LOWER
))) {
356 $look = $reverse ?
array_flip($lookup) : $lookup;
358 //trim lookup array, ignore . ( fix for CRM-1514 ), eg for prefix/suffix make sure Dr. and Dr both are valid
360 foreach ($look as $k => $v) {
361 $newLook[trim($k, ".")] = $v;
366 if (is_array($look)) {
367 if (!array_key_exists(trim(strtolower($defaults[strtolower($src)]), '.'), array_change_key_case($look, CASE_LOWER
))) {
372 $tempLook = array_change_key_case($look, CASE_LOWER
);
374 $defaults[$dst] = $tempLook[trim(strtolower($defaults[strtolower($src)]), '.')];
379 * Function to check if give array is empty
380 * @param array $array array to check for empty condition
382 * @return boolean true is array is empty else false
385 static function crmIsEmptyArray($array = array()) {
386 if (!is_array($array)) {
389 foreach ($array as $element) {
390 if (is_array($element)) {
391 if (!self
::crmIsEmptyArray($element)) {
395 elseif (isset($element)) {
403 * Function to determine how many levels in array for multidimensional arrays
405 * @param array $array
407 * @return integer $levels containing number of levels in array
410 static function getLevelsArray($array) {
411 if (!is_array($array)) {
414 $jsonString = json_encode($array);
415 $parts = explode("}", $jsonString);
417 foreach ($parts as $part) {
418 $countLevels = substr_count($part, "{");
419 if ($countLevels > $max) {
427 * Function to sort an associative array of arrays by an attribute using natural string compare
429 * @param array $array Array to be sorted
430 * @param string $field Name of the attribute you want to sort by
432 * @return array $array Sorted array
435 static function crmArraySortByField($array, $field) {
436 $code = "return strnatcmp(\$a['$field'], \$b['$field']);";
437 uasort($array, create_function('$a,$b', $code));
442 * Recursively removes duplicate values from an multi-dimensional array.
444 * @param array $array The input array possibly containing duplicate values.
446 * @return array $array The array with duplicate values removed.
449 static function crmArrayUnique($array) {
450 $result = array_map("unserialize", array_unique(array_map("serialize", $array)));
451 foreach ($result as $key => $value) {
452 if (is_array($value)) {
453 $result[$key] = self
::crmArrayUnique($value);
460 * Sort an array and maintain index association, use Collate from the
461 * PECL "intl" package, if available, for UTF-8 sorting (ex: list of countries).
462 * On Debian/Ubuntu: apt-get install php5-intl
464 * @param array $array array of values
466 * @return array Sorted array
469 static function asort($array = array()) {
470 $lcMessages = CRM_Utils_System
::getUFLocale();
472 if ($lcMessages && $lcMessages != 'en_US' && class_exists('Collator')) {
473 $collator = new Collator($lcMessages . '.utf8');
474 $collator->asort($array);
484 * Convenient way to unset a bunch of items from an array
486 * @param array $items (reference)
487 * @param string/int/array $itemN: other params to this function will be treated as keys
488 * (or arrays of keys) to unset
490 static function remove(&$items) {
491 foreach (func_get_args() as $n => $key) {
492 if ($n && is_array($key)) {
493 foreach($key as $k) {
504 * Build an array-tree which indexes the records in an array
506 * @param $keys array of string (properties by which to index)
507 * @param $records array of records (objects or assoc-arrays)
508 * @return array; multi-dimensional, with one layer for each key
510 static function index($keys, $records) {
511 $final_key = array_pop($keys);
514 foreach ($records as $record) {
516 foreach ($keys as $key) {
517 if (is_array($record)) {
518 $keyvalue = $record[$key];
520 $keyvalue = $record->{$key};
522 if (!is_array($node[$keyvalue])) {
523 $node[$keyvalue] = array();
525 $node = &$node[$keyvalue];
527 if (is_array($record)) {
528 $node[ $record[$final_key] ] = $record;
530 $node[ $record->{$final_key} ] = $record;
537 * Iterate through a list of records and grab the value of some property
539 * @param string $prop
540 * @param array $records a list of records (object|array)
541 * @return array keys are the original keys of $records; values are the $prop values
543 static function collect($prop, $records) {
545 foreach ($records as $key => $record) {
546 if (is_object($record)) {
547 $result[$key] = $record->{$prop};
549 $result[$key] = $record[$prop];
556 * Given a list of key-value pairs, combine thme into a single string
557 * @param array $pairs e.g. array('a' => '1', 'b' => '2')
558 * @param string $l1Delim e.g. ','
559 * @param string $l2Delim e.g. '='
560 * @return string e.g. 'a=1,b=2'
562 static function implodeKeyValue($l1Delim, $l2Delim, $pairs) {
564 foreach ($pairs as $key => $value) {
565 $exprs[] = $key . $l2Delim . $value;
567 return implode($l1Delim, $exprs);
571 * Like explode() but assumes that the $value is padded with $delim on left and right
573 * @param mixed $values
574 * @param string $delim
577 static function explodePadded($values, $delim = CRM_Core_DAO
::VALUE_SEPARATOR
) {
578 if ($values === NULL) {
581 // If we already have an array, no need to continue
582 if (is_array($values)) {
585 return explode($delim, trim((string) $values, $delim));
589 * Like implode() but creates a string that is padded with $delim on left and right
591 * @param mixed $values
592 * @param string $delim
593 * @return string|NULL
595 static function implodePadded($values, $delim = CRM_Core_DAO
::VALUE_SEPARATOR
) {
596 if ($values === NULL) {
599 // If we already have a string, strip $delim off the ends so it doesn't get added twice
600 if (is_string($values)) {
601 $values = trim($values, $delim);
603 return $delim . implode($delim, (array) $values) . $delim;
607 * Function to modify the key in an array without actually changing the order
608 * By default when you add an element it is added at the end
610 * @param array $elementArray associated array element
611 * @param string $oldKey old key
612 * @param string $newKey new key
616 static function crmReplaceKey(&$elementArray, $oldKey, $newKey) {
617 $keys = array_keys($elementArray);
618 if (FALSE === $index = array_search($oldKey, $keys)) {
619 throw new Exception(sprintf('key "%s" does not exit', $oldKey));
621 $keys[$index] = $newKey;
622 $elementArray = array_combine($keys, array_values($elementArray));
623 return $elementArray;
627 * function to get value of first matched
628 * regex key element of an array
630 static function valueByRegexKey($regexKey, $list, $default = NULL) {
631 if (is_array($list) && $regexKey) {
632 $matches = preg_grep($regexKey, array_keys($list));
633 $key = reset($matches);
634 return ($key && array_key_exists($key, $list)) ?
$list[$key] : $default;