1<?php 2 3namespace Illuminate\Support; 4 5use ArrayAccess; 6use Illuminate\Support\Traits\Macroable; 7 8class Arr 9{ 10 use Macroable; 11 12 /** 13 * Determine whether the given value is array accessible. 14 * 15 * @param mixed $value 16 * @return bool 17 */ 18 public static function accessible($value) 19 { 20 return is_array($value) || $value instanceof ArrayAccess; 21 } 22 23 /** 24 * Add an element to an array using "dot" notation if it doesn't exist. 25 * 26 * @param array $array 27 * @param string $key 28 * @param mixed $value 29 * @return array 30 */ 31 public static function add($array, $key, $value) 32 { 33 if (is_null(static::get($array, $key))) { 34 static::set($array, $key, $value); 35 } 36 37 return $array; 38 } 39 40 /** 41 * Build a new array using a callback. 42 * 43 * @param array $array 44 * @param callable $callback 45 * @return array 46 * 47 * @deprecated since version 5.2. 48 */ 49 public static function build($array, callable $callback) 50 { 51 $results = []; 52 53 foreach ($array as $key => $value) { 54 list($innerKey, $innerValue) = call_user_func($callback, $key, $value); 55 56 $results[$innerKey] = $innerValue; 57 } 58 59 return $results; 60 } 61 62 /** 63 * Collapse an array of arrays into a single array. 64 * 65 * @param array $array 66 * @return array 67 */ 68 public static function collapse($array) 69 { 70 $results = []; 71 72 foreach ($array as $values) { 73 if ($values instanceof Collection) { 74 $values = $values->all(); 75 } elseif (! is_array($values)) { 76 continue; 77 } 78 79 $results = array_merge($results, $values); 80 } 81 82 return $results; 83 } 84 85 /** 86 * Divide an array into two arrays. One with keys and the other with values. 87 * 88 * @param array $array 89 * @return array 90 */ 91 public static function divide($array) 92 { 93 return [array_keys($array), array_values($array)]; 94 } 95 96 /** 97 * Flatten a multi-dimensional associative array with dots. 98 * 99 * @param array $array 100 * @param string $prepend 101 * @return array 102 */ 103 public static function dot($array, $prepend = '') 104 { 105 $results = []; 106 107 foreach ($array as $key => $value) { 108 if (is_array($value) && ! empty($value)) { 109 $results = array_merge($results, static::dot($value, $prepend.$key.'.')); 110 } else { 111 $results[$prepend.$key] = $value; 112 } 113 } 114 115 return $results; 116 } 117 118 /** 119 * Get all of the given array except for a specified array of items. 120 * 121 * @param array $array 122 * @param array|string $keys 123 * @return array 124 */ 125 public static function except($array, $keys) 126 { 127 static::forget($array, $keys); 128 129 return $array; 130 } 131 132 /** 133 * Determine if the given key exists in the provided array. 134 * 135 * @param \ArrayAccess|array $array 136 * @param string|int $key 137 * @return bool 138 */ 139 public static function exists($array, $key) 140 { 141 if ($array instanceof ArrayAccess) { 142 return $array->offsetExists($key); 143 } 144 145 return array_key_exists($key, $array); 146 } 147 148 /** 149 * Return the first element in an array passing a given truth test. 150 * 151 * @param array $array 152 * @param callable|null $callback 153 * @param mixed $default 154 * @return mixed 155 */ 156 public static function first($array, callable $callback = null, $default = null) 157 { 158 if (is_null($callback)) { 159 return empty($array) ? value($default) : reset($array); 160 } 161 162 foreach ($array as $key => $value) { 163 if (call_user_func($callback, $key, $value)) { 164 return $value; 165 } 166 } 167 168 return value($default); 169 } 170 171 /** 172 * Return the last element in an array passing a given truth test. 173 * 174 * @param array $array 175 * @param callable|null $callback 176 * @param mixed $default 177 * @return mixed 178 */ 179 public static function last($array, callable $callback = null, $default = null) 180 { 181 if (is_null($callback)) { 182 return empty($array) ? value($default) : end($array); 183 } 184 185 return static::first(array_reverse($array), $callback, $default); 186 } 187 188 /** 189 * Flatten a multi-dimensional array into a single level. 190 * 191 * @param array $array 192 * @param int $depth 193 * @return array 194 */ 195 public static function flatten($array, $depth = INF) 196 { 197 $result = []; 198 199 foreach ($array as $item) { 200 $item = $item instanceof Collection ? $item->all() : $item; 201 202 if (is_array($item)) { 203 if ($depth === 1) { 204 $result = array_merge($result, $item); 205 continue; 206 } 207 208 $result = array_merge($result, static::flatten($item, $depth - 1)); 209 continue; 210 } 211 212 $result[] = $item; 213 } 214 215 return $result; 216 } 217 218 /** 219 * Remove one or many array items from a given array using "dot" notation. 220 * 221 * @param array $array 222 * @param array|string $keys 223 * @return void 224 */ 225 public static function forget(&$array, $keys) 226 { 227 $original = &$array; 228 229 $keys = (array) $keys; 230 231 if (count($keys) === 0) { 232 return; 233 } 234 235 foreach ($keys as $key) { 236 // if the exact key exists in the top-level, remove it 237 if (static::exists($array, $key)) { 238 unset($array[$key]); 239 240 continue; 241 } 242 243 $parts = explode('.', $key); 244 245 // clean up before each pass 246 $array = &$original; 247 248 while (count($parts) > 1) { 249 $part = array_shift($parts); 250 251 if (isset($array[$part]) && is_array($array[$part])) { 252 $array = &$array[$part]; 253 } else { 254 continue 2; 255 } 256 } 257 258 unset($array[array_shift($parts)]); 259 } 260 } 261 262 /** 263 * Get an item from an array using "dot" notation. 264 * 265 * @param \ArrayAccess|array $array 266 * @param string $key 267 * @param mixed $default 268 * @return mixed 269 */ 270 public static function get($array, $key, $default = null) 271 { 272 if (! static::accessible($array)) { 273 return value($default); 274 } 275 276 if (is_null($key)) { 277 return $array; 278 } 279 280 if (static::exists($array, $key)) { 281 return $array[$key]; 282 } 283 284 foreach (explode('.', $key) as $segment) { 285 if (static::accessible($array) && static::exists($array, $segment)) { 286 $array = $array[$segment]; 287 } else { 288 return value($default); 289 } 290 } 291 292 return $array; 293 } 294 295 /** 296 * Check if an item exists in an array using "dot" notation. 297 * 298 * @param \ArrayAccess|array $array 299 * @param string $key 300 * @return bool 301 */ 302 public static function has($array, $key) 303 { 304 if (! $array) { 305 return false; 306 } 307 308 if (is_null($key)) { 309 return false; 310 } 311 312 if (static::exists($array, $key)) { 313 return true; 314 } 315 316 foreach (explode('.', $key) as $segment) { 317 if (static::accessible($array) && static::exists($array, $segment)) { 318 $array = $array[$segment]; 319 } else { 320 return false; 321 } 322 } 323 324 return true; 325 } 326 327 /** 328 * Determines if an array is associative. 329 * 330 * An array is "associative" if it doesn't have sequential numerical keys beginning with zero. 331 * 332 * @param array $array 333 * @return bool 334 */ 335 public static function isAssoc(array $array) 336 { 337 $keys = array_keys($array); 338 339 return array_keys($keys) !== $keys; 340 } 341 342 /** 343 * Get a subset of the items from the given array. 344 * 345 * @param array $array 346 * @param array|string $keys 347 * @return array 348 */ 349 public static function only($array, $keys) 350 { 351 return array_intersect_key($array, array_flip((array) $keys)); 352 } 353 354 /** 355 * Pluck an array of values from an array. 356 * 357 * @param array $array 358 * @param string|array $value 359 * @param string|array|null $key 360 * @return array 361 */ 362 public static function pluck($array, $value, $key = null) 363 { 364 $results = []; 365 366 list($value, $key) = static::explodePluckParameters($value, $key); 367 368 foreach ($array as $item) { 369 $itemValue = data_get($item, $value); 370 371 // If the key is "null", we will just append the value to the array and keep 372 // looping. Otherwise we will key the array using the value of the key we 373 // received from the developer. Then we'll return the final array form. 374 if (is_null($key)) { 375 $results[] = $itemValue; 376 } else { 377 $itemKey = data_get($item, $key); 378 379 $results[$itemKey] = $itemValue; 380 } 381 } 382 383 return $results; 384 } 385 386 /** 387 * Explode the "value" and "key" arguments passed to "pluck". 388 * 389 * @param string|array $value 390 * @param string|array|null $key 391 * @return array 392 */ 393 protected static function explodePluckParameters($value, $key) 394 { 395 $value = is_string($value) ? explode('.', $value) : $value; 396 397 $key = is_null($key) || is_array($key) ? $key : explode('.', $key); 398 399 return [$value, $key]; 400 } 401 402 /** 403 * Push an item onto the beginning of an array. 404 * 405 * @param array $array 406 * @param mixed $value 407 * @param mixed $key 408 * @return array 409 */ 410 public static function prepend($array, $value, $key = null) 411 { 412 if (is_null($key)) { 413 array_unshift($array, $value); 414 } else { 415 $array = [$key => $value] + $array; 416 } 417 418 return $array; 419 } 420 421 /** 422 * Get a value from the array, and remove it. 423 * 424 * @param array $array 425 * @param string $key 426 * @param mixed $default 427 * @return mixed 428 */ 429 public static function pull(&$array, $key, $default = null) 430 { 431 $value = static::get($array, $key, $default); 432 433 static::forget($array, $key); 434 435 return $value; 436 } 437 438 /** 439 * Set an array item to a given value using "dot" notation. 440 * 441 * If no key is given to the method, the entire array will be replaced. 442 * 443 * @param array $array 444 * @param string $key 445 * @param mixed $value 446 * @return array 447 */ 448 public static function set(&$array, $key, $value) 449 { 450 if (is_null($key)) { 451 return $array = $value; 452 } 453 454 $keys = explode('.', $key); 455 456 while (count($keys) > 1) { 457 $key = array_shift($keys); 458 459 // If the key doesn't exist at this depth, we will just create an empty array 460 // to hold the next value, allowing us to create the arrays to hold final 461 // values at the correct depth. Then we'll keep digging into the array. 462 if (! isset($array[$key]) || ! is_array($array[$key])) { 463 $array[$key] = []; 464 } 465 466 $array = &$array[$key]; 467 } 468 469 $array[array_shift($keys)] = $value; 470 471 return $array; 472 } 473 474 /** 475 * Sort the array using the given callback. 476 * 477 * @param array $array 478 * @param callable $callback 479 * @return array 480 */ 481 public static function sort($array, callable $callback) 482 { 483 return Collection::make($array)->sortBy($callback)->all(); 484 } 485 486 /** 487 * Recursively sort an array by keys and values. 488 * 489 * @param array $array 490 * @return array 491 */ 492 public static function sortRecursive($array) 493 { 494 foreach ($array as &$value) { 495 if (is_array($value)) { 496 $value = static::sortRecursive($value); 497 } 498 } 499 500 if (static::isAssoc($array)) { 501 ksort($array); 502 } else { 503 sort($array); 504 } 505 506 return $array; 507 } 508 509 /** 510 * Filter the array using the given callback. 511 * 512 * @param array $array 513 * @param callable $callback 514 * @return array 515 */ 516 public static function where($array, callable $callback) 517 { 518 $filtered = []; 519 520 foreach ($array as $key => $value) { 521 if (call_user_func($callback, $key, $value)) { 522 $filtered[$key] = $value; 523 } 524 } 525 526 return $filtered; 527 } 528} 529