Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 150 | Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7
 
8
declare(strict_types=1);
9
 
10
namespace Nette\Utils;
11
 
12
use Nette;
13
use function is_array, is_int, is_object, count;
14
 
15
 
16
/**
17
 * Array tools library.
18
 */
19
class Arrays
20
{
21
	use Nette\StaticClass;
22
 
23
	/**
24
	 * Returns item from array. If it does not exist, it throws an exception, unless a default value is set.
25
	 * @template T
26
	 * @param  array<T>  $array
27
	 * @param  array-key|array-key[]  $key
28
	 * @param  ?T  $default
29
	 * @return ?T
30
	 * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
31
	 */
32
	public static function get(array $array, $key, $default = null)
33
	{
34
		foreach (is_array($key) ? $key : [$key] as $k) {
35
			if (is_array($array) && array_key_exists($k, $array)) {
36
				$array = $array[$k];
37
			} else {
38
				if (func_num_args() < 3) {
39
					throw new Nette\InvalidArgumentException("Missing item '$k'.");
40
				}
41
 
42
				return $default;
43
			}
44
		}
45
 
46
		return $array;
47
	}
48
 
49
 
50
	/**
51
	 * Returns reference to array item. If the index does not exist, new one is created with value null.
52
	 * @template T
53
	 * @param  array<T>  $array
54
	 * @param  array-key|array-key[]  $key
55
	 * @return ?T
56
	 * @throws Nette\InvalidArgumentException if traversed item is not an array
57
	 */
58
	public static function &getRef(array &$array, $key)
59
	{
60
		foreach (is_array($key) ? $key : [$key] as $k) {
61
			if (is_array($array) || $array === null) {
62
				$array = &$array[$k];
63
			} else {
64
				throw new Nette\InvalidArgumentException('Traversed item is not an array.');
65
			}
66
		}
67
 
68
		return $array;
69
	}
70
 
71
 
72
	/**
73
	 * Recursively merges two fields. It is useful, for example, for merging tree structures. It behaves as
74
	 * the + operator for array, ie. it adds a key/value pair from the second array to the first one and retains
75
	 * the value from the first array in the case of a key collision.
76
	 * @template T1
77
	 * @template T2
78
	 * @param  array<T1>  $array1
79
	 * @param  array<T2>  $array2
80
	 * @return array<T1|T2>
81
	 */
82
	public static function mergeTree(array $array1, array $array2): array
83
	{
84
		$res = $array1 + $array2;
85
		foreach (array_intersect_key($array1, $array2) as $k => $v) {
86
			if (is_array($v) && is_array($array2[$k])) {
87
				$res[$k] = self::mergeTree($v, $array2[$k]);
88
			}
89
		}
90
 
91
		return $res;
92
	}
93
 
94
 
95
	/**
96
	 * Returns zero-indexed position of given array key. Returns null if key is not found.
97
	 * @param  array-key  $key
98
	 * @return int|null offset if it is found, null otherwise
99
	 */
100
	public static function getKeyOffset(array $array, $key): ?int
101
	{
102
		return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), true));
103
	}
104
 
105
 
106
	/**
107
	 * @deprecated  use  getKeyOffset()
108
	 */
109
	public static function searchKey(array $array, $key): ?int
110
	{
111
		return self::getKeyOffset($array, $key);
112
	}
113
 
114
 
115
	/**
116
	 * Tests an array for the presence of value.
117
	 * @param  mixed  $value
118
	 */
119
	public static function contains(array $array, $value): bool
120
	{
121
		return in_array($value, $array, true);
122
	}
123
 
124
 
125
	/**
126
	 * Returns the first item from the array or null if array is empty.
127
	 * @template T
128
	 * @param  array<T>  $array
129
	 * @return ?T
130
	 */
131
	public static function first(array $array)
132
	{
133
		return count($array) ? reset($array) : null;
134
	}
135
 
136
 
137
	/**
138
	 * Returns the last item from the array or null if array is empty.
139
	 * @template T
140
	 * @param  array<T>  $array
141
	 * @return ?T
142
	 */
143
	public static function last(array $array)
144
	{
145
		return count($array) ? end($array) : null;
146
	}
147
 
148
 
149
	/**
150
	 * Inserts the contents of the $inserted array into the $array immediately after the $key.
151
	 * If $key is null (or does not exist), it is inserted at the beginning.
152
	 * @param  array-key|null  $key
153
	 */
154
	public static function insertBefore(array &$array, $key, array $inserted): void
155
	{
156
		$offset = $key === null ? 0 : (int) self::getKeyOffset($array, $key);
157
		$array = array_slice($array, 0, $offset, true)
158
			+ $inserted
159
			+ array_slice($array, $offset, count($array), true);
160
	}
161
 
162
 
163
	/**
164
	 * Inserts the contents of the $inserted array into the $array before the $key.
165
	 * If $key is null (or does not exist), it is inserted at the end.
166
	 * @param  array-key|null  $key
167
	 */
168
	public static function insertAfter(array &$array, $key, array $inserted): void
169
	{
170
		if ($key === null || ($offset = self::getKeyOffset($array, $key)) === null) {
171
			$offset = count($array) - 1;
172
		}
173
 
174
		$array = array_slice($array, 0, $offset + 1, true)
175
			+ $inserted
176
			+ array_slice($array, $offset + 1, count($array), true);
177
	}
178
 
179
 
180
	/**
181
	 * Renames key in array.
182
	 * @param  array-key  $oldKey
183
	 * @param  array-key  $newKey
184
	 */
185
	public static function renameKey(array &$array, $oldKey, $newKey): bool
186
	{
187
		$offset = self::getKeyOffset($array, $oldKey);
188
		if ($offset === null) {
189
			return false;
190
		}
191
 
192
		$val = &$array[$oldKey];
193
		$keys = array_keys($array);
194
		$keys[$offset] = $newKey;
195
		$array = array_combine($keys, $array);
196
		$array[$newKey] = &$val;
197
		return true;
198
	}
199
 
200
 
201
	/**
202
	 * Returns only those array items, which matches a regular expression $pattern.
203
	 * @param  string[]  $array
204
	 * @return string[]
205
	 */
206
	public static function grep(array $array, string $pattern, int $flags = 0): array
207
	{
208
		return Strings::pcre('preg_grep', [$pattern, $array, $flags]);
209
	}
210
 
211
 
212
	/**
213
	 * Transforms multidimensional array to flat array.
214
	 */
215
	public static function flatten(array $array, bool $preserveKeys = false): array
216
	{
217
		$res = [];
218
		$cb = $preserveKeys
219
			? function ($v, $k) use (&$res): void { $res[$k] = $v; }
220
		: function ($v) use (&$res): void { $res[] = $v; };
221
		array_walk_recursive($array, $cb);
222
		return $res;
223
	}
224
 
225
 
226
	/**
227
	 * Checks if the array is indexed in ascending order of numeric keys from zero, a.k.a list.
228
	 * @param  mixed  $value
229
	 */
230
	public static function isList($value): bool
231
	{
232
		return is_array($value) && (PHP_VERSION_ID < 80100
233
			? !$value || array_keys($value) === range(0, count($value) - 1)
234
			: array_is_list($value)
235
		);
236
	}
237
 
238
 
239
	/**
240
	 * Reformats table to associative tree. Path looks like 'field|field[]field->field=field'.
241
	 * @param  string|string[]  $path
242
	 * @return array|\stdClass
243
	 */
244
	public static function associate(array $array, $path)
245
	{
246
		$parts = is_array($path)
247
			? $path
248
			: preg_split('#(\[\]|->|=|\|)#', $path, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
249
 
250
		if (!$parts || $parts === ['->'] || $parts[0] === '=' || $parts[0] === '|') {
251
			throw new Nette\InvalidArgumentException("Invalid path '$path'.");
252
		}
253
 
254
		$res = $parts[0] === '->' ? new \stdClass : [];
255
 
256
		foreach ($array as $rowOrig) {
257
			$row = (array) $rowOrig;
258
			$x = &$res;
259
 
260
			for ($i = 0; $i < count($parts); $i++) {
261
				$part = $parts[$i];
262
				if ($part === '[]') {
263
					$x = &$x[];
264
 
265
				} elseif ($part === '=') {
266
					if (isset($parts[++$i])) {
267
						$x = $row[$parts[$i]];
268
						$row = null;
269
					}
270
				} elseif ($part === '->') {
271
					if (isset($parts[++$i])) {
272
						if ($x === null) {
273
							$x = new \stdClass;
274
						}
275
 
276
						$x = &$x->{$row[$parts[$i]]};
277
					} else {
278
						$row = is_object($rowOrig) ? $rowOrig : (object) $row;
279
					}
280
				} elseif ($part !== '|') {
281
					$x = &$x[(string) $row[$part]];
282
				}
283
			}
284
 
285
			if ($x === null) {
286
				$x = $row;
287
			}
288
		}
289
 
290
		return $res;
291
	}
292
 
293
 
294
	/**
295
	 * Normalizes array to associative array. Replace numeric keys with their values, the new value will be $filling.
296
	 * @param  mixed  $filling
297
	 */
298
	public static function normalize(array $array, $filling = null): array
299
	{
300
		$res = [];
301
		foreach ($array as $k => $v) {
302
			$res[is_int($k) ? $v : $k] = is_int($k) ? $filling : $v;
303
		}
304
 
305
		return $res;
306
	}
307
 
308
 
309
	/**
310
	 * Returns and removes the value of an item from an array. If it does not exist, it throws an exception,
311
	 * or returns $default, if provided.
312
	 * @template T
313
	 * @param  array<T>  $array
314
	 * @param  array-key  $key
315
	 * @param  ?T  $default
316
	 * @return ?T
317
	 * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
318
	 */
319
	public static function pick(array &$array, $key, $default = null)
320
	{
321
		if (array_key_exists($key, $array)) {
322
			$value = $array[$key];
323
			unset($array[$key]);
324
			return $value;
325
 
326
		} elseif (func_num_args() < 3) {
327
			throw new Nette\InvalidArgumentException("Missing item '$key'.");
328
 
329
		} else {
330
			return $default;
331
		}
332
	}
333
 
334
 
335
	/**
336
	 * Tests whether at least one element in the array passes the test implemented by the
337
	 * provided callback with signature `function ($value, $key, array $array): bool`.
338
	 */
339
	public static function some(iterable $array, callable $callback): bool
340
	{
341
		foreach ($array as $k => $v) {
342
			if ($callback($v, $k, $array)) {
343
				return true;
344
			}
345
		}
346
 
347
		return false;
348
	}
349
 
350
 
351
	/**
352
	 * Tests whether all elements in the array pass the test implemented by the provided function,
353
	 * which has the signature `function ($value, $key, array $array): bool`.
354
	 */
355
	public static function every(iterable $array, callable $callback): bool
356
	{
357
		foreach ($array as $k => $v) {
358
			if (!$callback($v, $k, $array)) {
359
				return false;
360
			}
361
		}
362
 
363
		return true;
364
	}
365
 
366
 
367
	/**
368
	 * Calls $callback on all elements in the array and returns the array of return values.
369
	 * The callback has the signature `function ($value, $key, array $array): bool`.
370
	 */
371
	public static function map(iterable $array, callable $callback): array
372
	{
373
		$res = [];
374
		foreach ($array as $k => $v) {
375
			$res[$k] = $callback($v, $k, $array);
376
		}
377
 
378
		return $res;
379
	}
380
 
381
 
382
	/**
383
	 * Invokes all callbacks and returns array of results.
384
	 * @param  callable[]  $callbacks
385
	 */
386
	public static function invoke(iterable $callbacks, ...$args): array
387
	{
388
		$res = [];
389
		foreach ($callbacks as $k => $cb) {
390
			$res[$k] = $cb(...$args);
391
		}
392
 
393
		return $res;
394
	}
395
 
396
 
397
	/**
398
	 * Invokes method on every object in an array and returns array of results.
399
	 * @param  object[]  $objects
400
	 */
401
	public static function invokeMethod(iterable $objects, string $method, ...$args): array
402
	{
403
		$res = [];
404
		foreach ($objects as $k => $obj) {
405
			$res[$k] = $obj->$method(...$args);
406
		}
407
 
408
		return $res;
409
	}
410
 
411
 
412
	/**
413
	 * Copies the elements of the $array array to the $object object and then returns it.
414
	 * @template T of object
415
	 * @param  T  $object
416
	 * @return T
417
	 */
418
	public static function toObject(iterable $array, $object)
419
	{
420
		foreach ($array as $k => $v) {
421
			$object->$k = $v;
422
		}
423
 
424
		return $object;
425
	}
426
 
427
 
428
	/**
429
	 * Converts value to array key.
430
	 * @param  mixed  $value
431
	 * @return array-key
432
	 */
433
	public static function toKey($value)
434
	{
435
		return key([$value => null]);
436
	}
437
 
438
 
439
	/**
440
	 * Returns copy of the $array where every item is converted to string
441
	 * and prefixed by $prefix and suffixed by $suffix.
442
	 * @param  string[]  $array
443
	 * @return string[]
444
	 */
445
	public static function wrap(array $array, string $prefix = '', string $suffix = ''): array
446
	{
447
		$res = [];
448
		foreach ($array as $k => $v) {
449
			$res[$k] = $prefix . $v . $suffix;
450
		}
451
 
452
		return $res;
453
	}
454
}