1: <?php
2: /**
3: * The Horde_Array:: class provides various methods for array manipulation.
4: *
5: * Copyright 2003-2012 Horde LLC (http://www.horde.org/)
6: *
7: * See the enclosed file COPYING for license information (LGPL). If you
8: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
9: *
10: * @author Michael Slusarz <slusarz@horde.org>
11: * @author Marko Djukic <marko@oblo.com>
12: * @author Jan Schneider <jan@horde.org>
13: * @category Horde
14: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
15: * @package Util
16: */
17: class Horde_Array
18: {
19: /**
20: * Prepare a list of addresses for storage.
21: * Namely, trims and lowercases all addresses and then sort.
22: *
23: * @param array $addr The list of addresses.
24: *
25: * @return array The list of addresses, prepared for storage.
26: */
27: static public function prepareAddressList(array $addr)
28: {
29: /* Remove any extra space in the address and make it lowercase. */
30: $addr = array_map(array('Horde_String', 'lower'), array_map('trim', $addr));
31:
32: /* Remove duplicate entries. */
33: $addr = array_keys(array_flip($addr));
34:
35: /* Sort the list. */
36: usort($addr, array(__CLASS__, 'sortAddressList'));
37:
38: return $addr;
39: }
40:
41: /**
42: * Function used by usort() to sort an address list.
43: *
44: * @param string $a Address #1.
45: * @param string $b Address #2.
46: *
47: * @return integer -1, 0, or 1.
48: */
49: static public function sortAddressList($a, $b)
50: {
51: $a = explode('@', $a);
52: $b = explode('@', $b);
53:
54: /* One of the addresses doesn't have a host name. */
55: if (empty($a[0])) {
56: array_shift($a);
57: }
58: if (empty($b[0])) {
59: array_shift($b);
60: }
61: if (count($a) != count($b)) {
62: return (count($a) > count($b));
63: }
64:
65: /* The addresses have different hostname or not hostname and
66: * different mailbox names. */
67: if ($a[(count($a) - 1)] != $b[(count($b) - 1)]) {
68: return strcmp($a[(count($a) - 1)], $b[(count($b) - 1)]);
69: }
70:
71: /* Compare mailbox names. */
72: return strcmp($a[0], $b[0]);
73: }
74:
75: /**
76: * Sorts an array on a specified key. If the key does not exist,
77: * defaults to the first key of the array.
78: *
79: * @param array &$array The array to be sorted, passed by reference.
80: * @param string $key The key by which to sort. If not specified then
81: * the first key is used.
82: * @param integer $dir Sort direction:
83: * 0 = ascending (default)
84: * 1 = descending
85: * @param boolean $assoc Keep key value association?
86: */
87: static public function arraySort(array &$array, $key = null, $dir = 0,
88: $assoc = true)
89: {
90: /* Return if the array is empty. */
91: if (empty($array)) {
92: return;
93: }
94:
95: /* If no key to sort by is specified, use the first key of the
96: * first element. */
97: if (is_null($key)) {
98: $keys = array_keys(reset($array));
99: $key = array_shift($keys);
100: }
101:
102: /* Call the appropriate sort function. */
103: $helper = new Horde_Array_Sort_Helper();
104: $helper->key = $key;
105: $function = $dir ? 'reverseCompare' : 'compare';
106: if ($assoc) {
107: uasort($array, array($helper, $function));
108: } else {
109: usort($array, array($helper, $function));
110: }
111: }
112:
113: /**
114: * Given an HTML type array field "example[key1][key2][key3]" breaks up
115: * the keys so that they could be used to reference a regular PHP array.
116: *
117: * @param string $field The field name to be examined.
118: * @param string &$base Will be set to the base element.
119: * @param array &$keys Will be set to the list of keys.
120: *
121: * @return boolean True on sucess, false on error.
122: */
123: static public function getArrayParts($field, &$base, &$keys)
124: {
125: if (!preg_match('|([^\[]*)((\[[^\[\]]*\])+)|', $field, $matches)) {
126: return false;
127: }
128:
129: $base = $matches[1];
130: $keys = explode('][', $matches[2]);
131: $keys[0] = substr($keys[0], 1);
132: $keys[count($keys) - 1] = substr($keys[count($keys) - 1], 0, strlen($keys[count($keys) - 1]) - 1);
133: return true;
134: }
135:
136: /**
137: * Using an array of keys iterate through the array following the
138: * keys to find the final key value. If a value is passed then set
139: * that value.
140: *
141: * @param array &$array The array to be used.
142: * @param array &$keys The key path to follow as an array.
143: * @param array $value If set the target element will have this value set
144: * to it.
145: *
146: * @return mixed The final value of the key path.
147: */
148: static public function getElement(&$array, array &$keys, $value = null)
149: {
150: if (count($keys)) {
151: $key = array_shift($keys);
152: return isset($array[$key])
153: ? self::getElement($array[$key], $keys, $value)
154: : false;
155: }
156:
157: if (!is_null($value)) {
158: $array = $value;
159: }
160:
161: return $array;
162: }
163:
164: /**
165: * Returns a rectangle of a two-dimensional array.
166: *
167: * @param array $array The array to extract the rectangle from.
168: * @param integer $row The start row of the rectangle.
169: * @param integer $col The start column of the rectangle.
170: * @param integer $height The height of the rectangle.
171: * @param integer $width The width of the rectangle.
172: *
173: * @return array The extracted rectangle.
174: */
175: static public function getRectangle(array $array, $row, $col, $height,
176: $width)
177: {
178: $rec = array();
179: for ($y = $row; $y < $row + $height; $y++) {
180: $rec[] = array_slice($array[$y], $col, $width);
181: }
182: return $rec;
183: }
184:
185: /**
186: * Given an array, returns an associative array with each element key
187: * derived from its value.
188: * For example:
189: * array(0 => 'foo', 1 => 'bar')
190: * would become:
191: * array('foo' => 'foo', 'bar' => 'bar')
192: *
193: * @param array $array An array of values.
194: *
195: * @return array An array with keys the same as values.
196: */
197: static public function valuesToKeys(array $array)
198: {
199: return $array
200: ? array_combine($array, $array)
201: : array();
202: }
203:
204: /**
205: * Backported array_replace_recursive().
206: *
207: * @todo Remove when requiring PHP 5.3.
208: *
209: * @param array $a1 The old array.
210: * @param array $a2 The new array.
211: *
212: * @return array The merged array.
213: */
214: static public function replaceRecursive(array $a1, array $a2)
215: {
216: if (function_exists('array_replace_recursive')) {
217: return array_replace_recursive($a1, $a2);
218: }
219:
220: foreach ($a2 as $key => $val) {
221: if (!isset($a1[$key])) {
222: $a1[$key] = array();
223: }
224:
225: $a1[$key] = (is_array($val))
226: ? self::replaceRecursive($a1[$key], $val)
227: : $val;
228: }
229:
230: return $a1;
231: }
232:
233: }
234: