Subversion-Projekte lars-tiefland.laravel_shop

Revision

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