Completed
Push — 3 ( d27970...2b05d8 )
by Luke
21s
created

ArrayLib::array_merge_recursive()   B

Complexity

Conditions 9
Paths 5

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 5
nop 1
dl 0
loc 31
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Library of static methods for manipulating arrays.
5
 *
6
 * @package framework
7
 * @subpackage misc
8
 */
9
class ArrayLib {
10
11
	/**
12
	 * Inverses the first and second level keys of an associative
13
	 * array, keying the result by the second level, and combines
14
	 * all first level entries within them.
15
	 *
16
	 * Before:
17
	 * <example>
18
	 * array(
19
	 * 	'row1' => array(
20
	 * 		'col1' =>'val1',
21
	 * 		'col2' => 'val2'
22
	 * 	),
23
	 * 	'row2' => array(
24
	 * 		'col1' => 'val3',
25
	 * 		'col2' => 'val4'
26
	 * 	)
27
	 * )
28
	 * </example>
29
	 *
30
	 * After:
31
	 * <example>
32
	 * array(
33
	 * 	'col1' => array(
34
	 * 		'row1' => 'val1',
35
	 * 		'row2' => 'val3',
36
	 * 	),
37
	 * 	'col2' => array(
38
	 * 		'row1' => 'val2',
39
	 * 		'row2' => 'val4',
40
	 * 	),
41
	 * )
42
	 * </example>
43
	 *
44
	 * @param array $arr
45
	 * @return array
46
	 */
47
	public static function invert($arr) {
48
		if(!$arr) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arr of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
49
			return false;
50
		}
51
52
		$result = array();
53
54
		foreach($arr as $columnName => $column) {
55
			foreach($column as $rowName => $cell) {
56
				$result[$rowName][$columnName] = $cell;
57
			}
58
		}
59
60
		return $result;
61
	}
62
63
	/**
64
	 * Return an array where the keys are all equal to the values.
65
	 *
66
	 * @param $arr array
67
	 * @return array
68
	 */
69
	public static function valuekey($arr) {
70
		return array_combine($arr, $arr);
71
	}
72
73
	/**
74
	 * @todo Improve documentation
75
	 */
76
	public static function array_values_recursive($arr) {
77
		$lst = array();
78
79
		foreach(array_keys($arr) as $k) {
80
			$v = $arr[$k];
81
			if (is_scalar($v)) {
82
				$lst[] = $v;
83
			} elseif (is_array($v)) {
84
				$lst = array_merge( $lst,
85
					self::array_values_recursive($v)
86
				);
87
			}
88
		}
89
90
		return $lst;
91
	}
92
93
	/**
94
	 * Filter an array by keys (useful for only allowing certain form-input to
95
	 * be saved).
96
	 *
97
	 * @param $arr array
98
	 * @param $keys array
99
	 *
100
	 * @return array
101
	 */
102
	public static function filter_keys($arr, $keys) {
103
		foreach($arr as $key => $v) {
104
			if(!in_array($key, $keys)) {
105
				unset($arr[$key]);
106
			}
107
		}
108
109
		return $arr;
110
	}
111
112
	/**
113
	 * Determines if an array is associative by checking for existing keys via
114
	 * array_key_exists().
115
	 *
116
	 * @see http://nz.php.net/manual/en/function.is-array.php#76188
117
	 *
118
	 * @param array $arr
119
	 *
120
	 * @return boolean
121
	 */
122
	public static function is_associative($arr) {
123
		if(is_array($arr) && ! empty($arr)) {
124
			for($iterator = count($arr) - 1; $iterator; $iterator--) {
125
				if (!array_key_exists($iterator, $arr)) {
126
					return true;
127
				}
128
			}
129
130
			return !array_key_exists(0, $arr);
131
		}
132
133
		return false;
134
	}
135
136
	/**
137
	 * Recursively searches an array $haystack for the value(s) $needle.
138
	 *
139
	 * Assumes that all values in $needle (if $needle is an array) are at
140
	 * the SAME level, not spread across multiple dimensions of the $haystack.
141
	 *
142
	 * @param mixed $needle
143
	 * @param array $haystack
144
	 * @param boolean $strict
145
	 *
146
	 * @return boolean
147
	 */
148
	public static function in_array_recursive($needle, $haystack, $strict = false) {
149
		if(!is_array($haystack)) {
150
			return false;
151
		}
152
153
		if(in_array($needle, $haystack, $strict)) {
154
			return true;
155
		} else {
156
			foreach($haystack as $obj) {
157
				if(self::in_array_recursive($needle, $obj, $strict)) {
158
					return true;
159
				}
160
			}
161
		}
162
163
		return false;
164
	}
165
166
	/**
167
	 * Similar to array_map, but recurses when arrays are encountered.
168
	 *
169
	 * Actually only one array argument is supported.
170
	 *
171
	 * @param $f callback to apply
172
	 * @param $array array
173
	 * @return array
174
	 */
175
	public static function array_map_recursive($f, $array) {
176
		$applyOrRecurse = function($v) use($f) {
177
			return is_array($v) ? ArrayLib::array_map_recursive($f, $v) : call_user_func($f, $v);
178
		};
179
180
		return array_map($applyOrRecurse, $array);
181
	}
182
183
	/**
184
	 * Recursively merges two or more arrays.
185
	 *
186
	 * Behaves similar to array_merge_recursive(), however it only merges
187
	 * values when both are arrays rather than creating a new array with
188
	 * both values, as the PHP version does. The same behaviour also occurs
189
	 * with numeric keys, to match that of what PHP does to generate $_REQUEST.
190
	 *
191
	 * @param array $array
192
	 *
193
	 * @return array
194
	 */
195
	public static function array_merge_recursive($array) {
196
		$arrays = func_get_args();
197
		$merged = array();
198
199
		if(count($arrays) == 1) {
200
			return $array;
201
		}
202
203
		while ($arrays) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arrays of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
204
			$array = array_shift($arrays);
205
206
			if (!is_array($array)) {
207
				trigger_error('ArrayLib::array_merge_recursive() encountered a non array argument', E_USER_WARNING);
208
				return;
209
			}
210
211
			if (!$array) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $array of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
212
				continue;
213
			}
214
215
			foreach ($array as $key => $value) {
216
				if (is_array($value) && array_key_exists($key, $merged) && is_array($merged[$key])) {
217
					$merged[$key] = ArrayLib::array_merge_recursive($merged[$key], $value);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
218
				} else {
219
					$merged[$key] = $value;
220
				}
221
			}
222
		}
223
224
		return $merged;
225
	}
226
227
	/**
228
	 * Takes an multi dimension array and returns the flattened version.
229
	 *
230
	 * @param array $array
231
	 * @param boolean $preserveKeys
232
	 * @param array $out
233
	 *
234
	 * @return array
235
	 */
236
	public static function flatten($array, $preserveKeys = true, &$out = array()) {
237
		foreach($array as $key => $child) {
238
			if(is_array($child)) {
239
				$out = self::flatten($child, $preserveKeys, $out);
240
			} else if($preserveKeys) {
241
				$out[$key] = $child;
242
			} else {
243
				$out[] = $child;
244
			}
245
		}
246
247
		return $out;
248
	}
249
}
250
251