1: <?php
2:
3: /*****************************************************************************************
4: * X2Engine Open Source Edition is a customer relationship management program developed by
5: * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
6: *
7: * This program is free software; you can redistribute it and/or modify it under
8: * the terms of the GNU Affero General Public License version 3 as published by the
9: * Free Software Foundation with the addition of the following permission added
10: * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11: * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
12: * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13: *
14: * This program is distributed in the hope that it will be useful, but WITHOUT
15: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16: * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
17: * details.
18: *
19: * You should have received a copy of the GNU Affero General Public License along with
20: * this program; if not, see http://www.gnu.org/licenses or write to the Free
21: * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22: * 02110-1301 USA.
23: *
24: * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
25: * California 95067, USA. or at email address contact@x2engine.com.
26: *
27: * The interactive user interfaces in modified source and object code versions
28: * of this program must display Appropriate Legal Notices, as required under
29: * Section 5 of the GNU Affero General Public License version 3.
30: *
31: * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32: * these Appropriate Legal Notices must retain the display of the "Powered by
33: * X2Engine" logo. If the display of the logo is not reasonably feasible for
34: * technical reasons, the Appropriate Legal Notices must display the words
35: * "Powered by X2Engine".
36: *****************************************************************************************/
37:
38: /**
39: * Standalone class with miscellaneous array functions
40: *
41: * @package
42: * @author Demitri Morgan <demitri@x2engine.com>, Derek Mueller <derek@x2engine.com>
43: */
44: class ArrayUtil {
45:
46: /**
47: * Given two associative arrays, returns an array with the same set of keys
48: * as the first, but with key/value pairs from the second if they are present.
49: * Any keys in the second and not in the first will be ignored/dropped.
50: *
51: * @param array $expectedFields The array with key => default value pairs
52: * @param array $currentFields The array to copy values from
53: * @return array
54: */
55: public static function normalizeToArray($expectedFields, $currentFields){
56: // Expected keys: defined in expectedFields
57: $expKeys = array_keys($expectedFields);
58: // Current keys: in the array to compare against
59: $curKeys = array_keys($currentFields);
60: // Keys to save: both already present in the current fields and defined in the expected
61: // fields
62: $savKeys = array_intersect($expKeys, $curKeys);
63: // New keys: that are not present in the current fields but defined in the expected fields
64: $newKeys = array_diff($expKeys, $curKeys);
65: // The array to return, with normalized data:
66: $fields = array();
67:
68: // Use existing values
69: foreach($savKeys as $fieldName)
70: $fields[$fieldName] = $currentFields[$fieldName];
71: // Use default values as defined in the expected fields
72: foreach($newKeys as $fieldName)
73: $fields[$fieldName] = $expectedFields[$fieldName];
74:
75: return $fields;
76: }
77:
78: /**
79: * A recursive version of normalizeToArray () which optionally maintains order of current
80: * fields.
81: *
82: * @param array $expectedFields The array with key => default value pairs
83: * @param array $currentFields The array to copy values from
84: * @return array
85: */
86: public static function normalizeToArrayR ($expectedFields, $currentFields,$maintainOrder=true) {
87: $fields = array ();
88:
89: /*
90: Use values in current fields if they are present, otherwise use default values in
91: expected fields. If the default value is an array, apply array normalization
92: recursively.
93: */
94: foreach ($expectedFields as $key => $val) {
95: if (is_array ($val) && isset ($currentFields[$key]) &&
96: is_array ($currentFields[$key])) {
97:
98: $fields[$key] = self::normalizeToArrayR (
99: $expectedFields[$key], $currentFields[$key]);
100: } else if (isset ($currentFields[$key])) {
101: $fields[$key] = $currentFields[$key];
102: } else {
103: $fields[$key] = $expectedFields[$key];
104: }
105: }
106:
107: if ($maintainOrder) {
108: /*
109: Maintain array ordering of current fields
110: */
111: $orderedFields = array ();
112: foreach ($currentFields as $key => $val) {
113: if (in_array ($key, array_keys ($fields))) {
114: $orderedFields[$key] = $fields[$key];
115: unset ($fields[$key]);
116: }
117: }
118:
119: /*
120: Add fields not specified in currentFields. These fields can't be sorted so they are
121: simply appended.
122: */
123: foreach ($fields as $key => $val) {
124: $orderedFields[$key] = $fields[$key];
125: }
126:
127: $fields = $orderedFields;
128: }
129:
130: return $fields;
131: }
132:
133: /**
134: * Determines whether a given array is associative
135: *
136: * @param array $array The array for which the check is made
137: * @return bool True if $array is associative, false otherwise
138: */
139: public static function is_assoc ($array) {
140: $keys = array_keys ($array);
141: $type;
142: foreach ($keys as $key) {
143: if (gettype ($key) === 'string') {
144: return true;
145: }
146: }
147: return false;
148: }
149:
150:
151: /**
152: * Similar to array_search but recursive, doesn't return needle of there's only one match, and
153: * allows for regex searching.
154: *
155: * @param string $find regex to search on
156: * @param array $in_array an array to search in
157: * @param array $keys_found keys whose corresponding values match the regex
158: * @return type an array of keys if $in_array is valid, or false if not.
159: */
160: public static function arraySearchPreg($find, $in_array, $keys_found = array()) {
161: // Escape slashes so that the pcre regex doesn't contain an invalid modifier
162: // in the event of invalid data
163: $find = preg_replace ('/\//', '\/', $find);
164:
165: if (is_array($in_array)) {
166: foreach ($in_array as $key => $val) {
167: if (is_array($val)) {
168: $keys_found = self::arraySearchPreg($find, $val, $keys_found);
169: } else {
170: if (preg_match('/' . $find . '/', $val))
171: $keys_found[] = $key;
172: }
173: }
174: return $keys_found;
175: }
176: return false;
177: }
178:
179: /**
180: * Retrieve the first entry from an associative array
181: * @param array $array
182: */
183: public static function assocArrayShift (&$array) {
184: $keys = array_keys ($array);
185: return array ($keys[0] => array_shift ($array));
186: }
187:
188: /**
189: * @param array $array the array to sort
190: * @return the sorted array
191: */
192: public static function sort (array $array) {
193: sort ($array);
194: return $array;
195: }
196:
197: /**
198: * Case-insensitive, no side-effects version of asort
199: * @param array $array the array to sort
200: * @return the sorted array
201: */
202: public static function asorti(array $array) {
203: uasort ($array, function ($a, $b) {
204: return strcasecmp ($a, $b);
205: });
206: return $array;
207: }
208:
209: /**
210: * Side effect free version of array_pop
211: */
212: public static function pop (array $array) {
213: $newArray = $array;
214: return array_pop ($newArray);
215: }
216:
217: public static function transpose ($array) {
218: $newArray = array ();
219: $arraySize = count ($array);
220: for ($i = 0; $i < $arraySize; $i++) {
221: $val = $array[$i];
222: if (is_array ($val)) {
223: $valSize = count ($val);
224: $j = 0;
225: foreach ($val as $key => $cellVal) {
226: $newArray[$j][] = $cellVal;
227: $j++;
228: }
229: } else {
230: $newArray[0][] = $val;
231: }
232: }
233: return $newArray;
234: }
235:
236: /**
237: * Like array_search but returns numeric index instead of key
238: */
239: public static function numericIndexOf ($needle, $haystack, $strict=false) {
240: $i = 0;
241: foreach ($haystack as $elem) {
242: if (!$strict && $elem == $needle || $strict && $elem === $needle) return $i;
243: $i++;
244: }
245: return false;
246: }
247:
248: public static function setAndTrue ($array, $val) {
249: return isset ($array[$val]) && $array[$val];
250: }
251:
252: public static function setEquals ($array1, $array2) {
253: $array1 = self::sort ($array1);
254: $array2 = self::sort ($array2);
255: return $array1 === $array2;
256: }
257:
258: public static function coerceToArray ($arr) {
259: if (!is_array ($arr)) {
260: $arr = array ($arr);
261: }
262: return $arr;
263: }
264:
265: }
266:
267: ?>
268: