diff --git a/composer.json b/composer.json index 4e23657d..84459593 100755 --- a/composer.json +++ b/composer.json @@ -13,6 +13,11 @@ "php": ">=8.4.23", "d11wtq/boris": "~1.0", "filp/whoops": "~2.11", + "illuminate/collections": "^13", + "illuminate/conditionable": "^13", + "illuminate/contracts": "^13", + "illuminate/macroable": "^13", + "illuminate/reflection": "^13", "ircmaxell/password-compat": "~1.0", "laravel/serializable-closure": "^1.2", "monolog/monolog": "^3.10", diff --git a/src/Illuminate/Container/Container.php b/src/Illuminate/Container/Container.php index 61951bad..32351bce 100755 --- a/src/Illuminate/Container/Container.php +++ b/src/Illuminate/Container/Container.php @@ -38,6 +38,11 @@ class Container implements ArrayAccess, ContainerContract { */ protected array $instances = []; + /** + * The registered scoped instances. + */ + protected array $scopedInstances = []; + /** * The registered type aliases. */ @@ -375,6 +380,36 @@ public function singletonIf($abstract, $concrete = null): void } } + /** + * Register a scoped binding in the container. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function scoped($abstract, $concrete = null): void + { + if (! in_array($abstract, $this->scopedInstances, true)) { + $this->scopedInstances[] = $abstract; + } + + $this->singleton($abstract, $concrete); + } + + /** + * Register a scoped binding if it hasn't already been registered. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function scopedIf($abstract, $concrete = null): void + { + if (! $this->bound($abstract)) { + $this->scoped($abstract, $concrete); + } + } + /** * Wrap a Closure such that it is shared. * diff --git a/src/Illuminate/Contracts/Auth/Authenticatable.php b/src/Illuminate/Contracts/Auth/Authenticatable.php deleted file mode 100644 index 4e5a8006..00000000 --- a/src/Illuminate/Contracts/Auth/Authenticatable.php +++ /dev/null @@ -1,14 +0,0 @@ -class = $class; - $this->id = $id; - $this->relations = $relations; - $this->connection = $connection; - } - - public function useCollectionClass(?string $collectionClass) - { - $this->collectionClass = $collectionClass; - return $this; - } - - public function getClass(): ?string - { - return $this->class; - } -} diff --git a/src/Illuminate/Contracts/Encryption/DecryptException.php b/src/Illuminate/Contracts/Encryption/DecryptException.php deleted file mode 100644 index 7edebc9c..00000000 --- a/src/Illuminate/Contracts/Encryption/DecryptException.php +++ /dev/null @@ -1,10 +0,0 @@ -toArray(), $key)); diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 2bb7b8b3..974650cf 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -1363,14 +1363,14 @@ public function pluck($column, $key = null) // otherwise we can just give these values back without a specific key. $results = new Collection($this->get($columns)); - $values = $results->fetch($columns[0])->all(); + $values = $results->pluck($columns[0])->all(); // If a key was specified and we have results, we will go ahead and combine // the values with the keys of all of the records so that the values can // be accessed by the key of the rows instead of simply being numeric. if ( ! is_null($key) && count($results) > 0) { - $keys = $results->fetch($key)->all(); + $keys = $results->pluck($key)->all(); return array_combine($keys, $values); } diff --git a/src/Illuminate/Routing/ResponseFactory.php b/src/Illuminate/Routing/ResponseFactory.php index 226db8d4..b72f4d8a 100644 --- a/src/Illuminate/Routing/ResponseFactory.php +++ b/src/Illuminate/Routing/ResponseFactory.php @@ -1,5 +1,7 @@ make('', $status, $headers); + } + + public function file($file, array $headers = []) + { + return new BinaryFileResponse($file, 200, $headers); + } + + public function streamJson($data, $status = 200, $headers = [], $encodingOptions = JsonResponse::DEFAULT_ENCODING_OPTIONS) + { + return new StreamedJsonResponse($data, $status, $headers, $encodingOptions); + } + + public function streamDownload($callback, $name = null, array $headers = [], $disposition = 'attachment') + { + $response = new StreamedResponse($callback, 200, $headers); + + if ( ! is_null($name)) + { + $response->headers->set('Content-Disposition', $response->headers->makeDisposition( + $disposition, $name, str_replace('%', '', Str::ascii($name)) + )); + } + + return $response; + } + + public function eventStream(Closure $callback, array $headers = [], $endStreamWith = '') + { + return $this->stream(function () use ($callback, $endStreamWith) { + foreach ($callback() as $message) + { + if (connection_aborted()) break; + + if ( ! is_string($message) && ! is_numeric($message)) + { + $message = json_encode($message); + } + + echo "event: update\n"; + echo 'data: '.$message; + echo "\n\n"; + + if (ob_get_level() > 0) ob_flush(); + flush(); + } + + if ($endStreamWith !== null && $endStreamWith !== '') + { + echo "event: update\n"; + echo 'data: '.$endStreamWith; + echo "\n\n"; + + if (ob_get_level() > 0) ob_flush(); + flush(); + } + }, 200, array_merge($headers, [ + 'Content-Type' => 'text/event-stream', + 'Cache-Control' => 'no-cache', + 'X-Accel-Buffering' => 'no', + ])); + } + + public function redirectTo($path, $status = 302, $headers = [], $secure = null) + { + return $this->container['redirect']->to($path, $status, $headers, $secure); + } + + public function redirectToRoute($route, $parameters = [], $status = 302, $headers = []) + { + return $this->container['redirect']->route($route, $parameters, $status, $headers); + } + + public function redirectToAction($action, $parameters = [], $status = 302, $headers = []) + { + return $this->container['redirect']->action($action, $parameters, $status, $headers); + } + + public function redirectGuest($path, $status = 302, $headers = [], $secure = null) + { + return $this->container['redirect']->guest($path, $status, $headers, $secure); + } + + public function redirectToIntended($default = '/', $status = 302, $headers = [], $secure = null) + { + return $this->container['redirect']->intended($default, $status, $headers, $secure); + } + } diff --git a/src/Illuminate/Support/Arr.php b/src/Illuminate/Support/Arr.php deleted file mode 100755 index 1071c58a..00000000 --- a/src/Illuminate/Support/Arr.php +++ /dev/null @@ -1,982 +0,0 @@ - $items, - $items instanceof Enumerable => $items->all(), - $items instanceof Arrayable => $items->toArray(), - $items instanceof WeakMap => iterator_to_array($items, false), - $items instanceof Traversable => iterator_to_array($items), - $items instanceof Jsonable => json_decode($items->toJson(), true), - $items instanceof JsonSerializable => (array) $items->jsonSerialize(), - is_object($items) => (array) $items, - default => throw new InvalidArgumentException('Items cannot be represented by a scalar value.'), - }; - } - - /** - * Run an associative map over each of the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param array $array - * @param callable $callback - * @return array - */ - public static function mapWithKeys(array $array, callable $callback) - { - $result = []; - - foreach ($array as $key => $value) { - $assoc = $callback($value, $key); - - foreach ($assoc as $mapKey => $mapValue) { - $result[$mapKey] = $mapValue; - } - } - - return $result; - } - - /** - * Partition the array into two arrays using the given callback. - * - * @param array $array - * @param callable $callback - * @return array - */ - public static function partition($array, callable $callback) - { - $passed = []; - $failed = []; - - foreach ($array as $key => $item) { - if ($callback($item, $key)) { - $passed[$key] = $item; - } else { - $failed[$key] = $item; - } - } - - return [$passed, $failed]; - } - - /** - * Select an array of values from an array. - * - * @param array $array - * @param array|string $keys - * @return array - */ - public static function select($array, $keys) - { - $keys = static::wrap($keys); - - return static::map($array, function ($item) use ($keys) { - $result = []; - - foreach ($keys as $key) { - if (Arr::accessible($item) && Arr::exists($item, $key)) { - $result[$key] = $item[$key]; - } elseif (is_object($item) && isset($item->{$key})) { - $result[$key] = $item->{$key}; - } - } - - return $result; - }); - } - - /** - * Determine whether the given value is array accessible. - * - * @param mixed $value - * @return bool - */ - public static function accessible($value): bool - { - return is_array($value) || $value instanceof ArrayAccess; - } - - /** - * Add an element to an array using "dot" notation if it doesn't exist. - * - * @param array $array - * @param string $key - * @param mixed $value - * - * @return array - */ - public static function add(array $array, string $key, mixed $value): array - { - if (is_null(static::get($array, $key))) - { - static::set($array, $key, $value); - } - - return $array; - } - - /** - * Build a new array using a callback. - * - * @param array $array - * @param \Closure $callback - * - * @return array - */ - public static function build(array $array, Closure $callback): array - { - $results = array(); - - foreach ($array as $key => $value) - { - [$innerKey, $innerValue] = call_user_func($callback, $key, $value); - - $results[$innerKey] = $innerValue; - } - - return $results; - } - - /** - * Collapse an array of arrays into a single array. - * - * @param iterable $array - * - * @return array - */ - public static function collapse(iterable $array): array - { - $results = []; - - foreach ($array as $values) { - if ($values instanceof Collection) { - $values = $values->all(); - } elseif (! is_array($values)) { - continue; - } - - $results[] = $values; - } - - return array_merge([], ...$results); - } - - /** - * Cross join the given arrays, returning all possible permutations. - * - * @param iterable ...$arrays - * @return array - */ - public static function crossJoin(...$arrays): array - { - $results = [[]]; - - foreach ($arrays as $index => $array) { - $append = []; - - foreach ($results as $product) { - foreach ($array as $item) { - $product[$index] = $item; - - $append[] = $product; - } - } - - $results = $append; - } - - return $results; - } - - /** - * Divide an array into two arrays. One with keys and the other with values. - * - * @param array $array - * - * @return array - */ - public static function divide(array $array): array - { - return array(array_keys($array), array_values($array)); - } - - /** - * Flatten a multi-dimensional associative array with dots. - * - * @param array $array - * @param string $prepend - * - * @return array - */ - public static function dot(array $array, string $prepend = ''): array - { - $results = []; - - foreach ($array as $key => $value) { - if (is_array($value) && ! empty($value)) { - $results = array_merge($results, static::dot($value, $prepend.$key.'.')); - } else { - $results[$prepend.$key] = $value; - } - } - - return $results; - } - - /** - * Convert a flatten "dot" notation array into an expanded array. - * - * @param iterable $array - * - * @return array - */ - public static function undot(iterable $array): array - { - $results = []; - - foreach ($array as $key => $value) { - static::set($results, $key, $value); - } - - return $results; - } - - /** - * Get all of the given array except for a specified array of items. - * - * @param array $array - * @param array|string $keys - * - * @return array - */ - public static function except(array $array, array|string $keys): array - { - static::forget($array, $keys); - - return $array; - } - - /** - * Determine if the given key exists in the provided array. - * - * @param \ArrayAccess|array $array - * @param int|float|string $key - * - * @return bool - */ - public static function exists(ArrayAccess|array $array, $key): bool - { - if ($array instanceof ArrayAccess) { - return $array->offsetExists($key); - } - - if (is_float($key)) { - $key = (string) $key; - } - - return array_key_exists($key, $array); - } - - - /** - * Fetch a flattened array of a nested array element. - * - * @param array $array - * @param string $key - * @return array - */ - public static function fetch($array, $key) - { - foreach (explode('.', $key) as $segment) - { - $results = array(); - - foreach ($array as $value) - { - if (array_key_exists($segment, $value = (array) $value)) - { - $results[] = $value[$segment]; - } - } - - $array = array_values($results); - } - - return array_values($results); - } - - /** - * Return the first element in an array passing a given truth test. - * - * @param array $array - * @param \Closure|null $callback - * @param mixed|null $default - * - * @return mixed - */ - public static function first(array $array, ?Closure $callback = null, mixed $default = null): mixed - { - if (is_null($callback)) { - if (empty($array)) { - return value($default); - } - - foreach ($array as $item) { - return $item; - } - } - - foreach ($array as $key => $value) { - if ($callback($value, $key)) { - return $value; - } - } - - return value($default); - } - - /** - * Join all items using a string. The final items can use a separate glue string. - * - * @param array $array - * @param string $glue - * @param string $finalGlue - * - * @return string - */ - public static function join(array $array, string $glue, string $finalGlue = ''): string - { - if ($finalGlue === '') { - return implode($glue, $array); - } - - if (count($array) === 0) { - return ''; - } - - if (count($array) === 1) { - return end($array); - } - - $finalItem = array_pop($array); - - return implode($glue, $array).$finalGlue.$finalItem; - } - - /** - * Key an associative array by a field or using a callback. - * - * @param array $array - * @param callable|array|string $keyBy - * @return array - */ - public static function keyBy($array, $keyBy): array - { - return Collection::make($array)->keyBy($keyBy)->all(); - } - - /** - * Return the last element in an array passing a given truth test. - * - * @param array $array - * @param \Closure|null $callback - * @param mixed $default - * - * @return mixed - */ - public static function last(array $array, ?Closure $callback = null, $default = null): mixed - { - if (is_null($callback)) { - return empty($array) ? value($default) : end($array); - } - - return static::first(array_reverse($array, true), $callback, $default); - } - - /** - * Flatten a multi-dimensional array into a single level. - * - * @param array $array - * @param int $depth - * - * @return array - */ - public static function flatten(array $array, $depth = INF): array - { - $result = []; - - foreach ($array as $item) { - $item = $item instanceof Collection ? $item->all() : $item; - - if (! is_array($item)) { - $result[] = $item; - } else { - $values = $depth === 1 - ? array_values($item) - : static::flatten($item, $depth - 1); - - foreach ($values as $value) { - $result[] = $value; - } - } - } - - return $result; - } - - /** - * Remove one or many array items from a given array using "dot" notation. - * - * @param array $array - * @param array|string $keys - * @return void - */ - public static function forget(&$array, $keys) - { - $original = &$array; - - $keys = (array) $keys; - - if (count($keys) === 0) { - return; - } - - foreach ($keys as $key) { - // if the exact key exists in the top-level, remove it - if (static::exists($array, $key)) { - unset($array[$key]); - - continue; - } - - $parts = explode('.', $key); - - // clean up before each pass - $array = &$original; - - while (count($parts) > 1) { - $part = array_shift($parts); - - if (isset($array[$part]) && static::accessible($array[$part])) { - $array = &$array[$part]; - } else { - continue 2; - } - } - - unset($array[array_shift($parts)]); - } - } - - /** - * Get an item from an array using "dot" notation. - * - * @param \ArrayAccess|array|null $array - * @param string|null $key - * @param mixed $default - * - * @return mixed - */ - public static function get($array, $key = null, $default = null) - { - if (! static::accessible($array)) { - return value($default); - } - - if (is_null($key)) { - return $array; - } - - if (static::exists($array, $key)) { - return $array[$key]; - } - - if (! str_contains($key, '.')) { - return $array[$key] ?? value($default); - } - - foreach (explode('.', $key) as $segment) { - if (static::accessible($array) && static::exists($array, $segment)) { - $array = $array[$segment]; - } else { - return value($default); - } - } - - return $array; - } - - /** - * Check if an item exists in an array using "dot" notation. - * - * @param \ArrayAccess|array $array - * @param string|array $keys - * @return bool - */ - public static function has($array, $keys): bool - { - $keys = (array) $keys; - - if (! $array || $keys === []) { - return false; - } - - foreach ($keys as $key) { - $subKeyArray = $array; - - if (static::exists($array, $key)) { - continue; - } - - foreach (explode('.', $key) as $segment) { - if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { - $subKeyArray = $subKeyArray[$segment]; - } else { - return false; - } - } - } - - return true; - } - - /** - * Determine if any of the keys exist in an array using "dot" notation. - * - * @param \ArrayAccess|array $array - * @param string|array $keys - * @return bool - */ - public static function hasAny($array, $keys): bool - { - if (is_null($keys)) { - return false; - } - - $keys = (array) $keys; - - if (! $array) { - return false; - } - - if ($keys === []) { - return false; - } - - foreach ($keys as $key) { - if (static::has($array, $key)) { - return true; - } - } - - return false; - } - - /** - * Determines if an array is associative. - * - * An array is "associative" if it doesn't have sequential numerical keys beginning with zero. - * - * @param array $array - * @return bool - */ - public static function isAssoc(array $array): bool - { - return ! array_is_list($array); - } - - /** - * Determines if an array is a list. - * - * An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between. - * - * @param array $array - * - * @return bool - */ - public static function isList(array $array): bool - { - return array_is_list($array); - } - - /** - * Run a map over each of the items in the array. - * - * @param array $array - * @param callable $callback - * @return array - */ - public static function map(array $array, callable $callback): array - { - $keys = array_keys($array); - - try { - $items = array_map($callback, $array, $keys); - } catch (ArgumentCountError) { - $items = array_map($callback, $array); - } - - return array_combine($keys, $items); - } - - /** - * Get a subset of the items from the given array. - * - * @param array $array - * @param array|string $keys - * - * @return array - */ - public static function only(array $array, array|string $keys): array - { - return array_intersect_key($array, array_flip((array) $keys)); - } - - /** - * Pluck an array of values from an array. - * - * @param array $array - * @param array|string|null $value - * @param string|null $key - * - * @return array - */ - public static function pluck(array $array, $value = null, $key = null): array - { - $results = []; - - [$value, $key] = static::explodePluckParameters($value, $key); - - foreach ($array as $item) { - $itemValue = data_get($item, $value); - - // If the key is "null", we will just append the value to the array and keep - // looping. Otherwise we will key the array using the value of the key we - // received from the developer. Then we'll return the final array form. - if (is_null($key)) { - $results[] = $itemValue; - } else { - $itemKey = data_get($item, $key); - - if (is_object($itemKey) && method_exists($itemKey, '__toString')) { - $itemKey = (string) $itemKey; - } - - $results[$itemKey] = $itemValue; - } - } - - return $results; - } - - /** - * Explode the "value" and "key" arguments passed to "pluck". - * - * @param string|array $value - * @param string|array|null $key - * @return array - */ - protected static function explodePluckParameters($value, $key): array - { - $value = is_string($value) ? explode('.', $value) : $value; - - $key = is_null($key) || is_array($key) ? $key : explode('.', $key); - - return [$value, $key]; - } - - /** - * Push an item onto the beginning of an array. - * - * @param array $array - * @param mixed $value - * @param mixed $key - * - * @return array - */ - public static function prepend(array $array, $value, $key = null): array - { - if (func_num_args() == 2) { - array_unshift($array, $value); - } else { - $array = [$key => $value] + $array; - } - - return $array; - } - - /** - * Prepend the key names of an associative array. - * - * @param array $array - * @param string $prependWith - * @return array - */ - public static function prependKeysWith($array, $prependWith): array - { - return Collection::make($array)->mapWithKeys(function ($item, $key) use ($prependWith) { - return [$prependWith.$key => $item]; - })->all(); - } - - /** - * Get a value from the array, and remove it. - * - * @param array $array - * @param string $key - * @param mixed $default - * @return mixed - */ - public static function pull(&$array, $key, $default = null) - { - $value = static::get($array, $key, $default); - - static::forget($array, $key); - - return $value; - } - - /** - * Convert the array into a query string. - * - * @param array $array - * @return string - */ - public static function query($array): string - { - return http_build_query($array, '', '&', PHP_QUERY_RFC3986); - } - - /** - * Get one or a specified number of random values from an array. - * - * @param array $array - * @param int|null $number - * @param bool|false $preserveKeys - * @return mixed - * - * @throws \InvalidArgumentException - */ - public static function random($array, $number = null, $preserveKeys = false) - { - $requested = is_null($number) ? 1 : $number; - - $count = count($array); - - if ($requested > $count) { - throw new InvalidArgumentException( - "You requested {$requested} items, but there are only {$count} items available." - ); - } - - if (is_null($number)) { - return $array[array_rand($array)]; - } - - if ((int) $number === 0) { - return []; - } - - $keys = array_rand($array, $number); - - $results = []; - - if ($preserveKeys) { - foreach ((array) $keys as $key) { - $results[$key] = $array[$key]; - } - } else { - foreach ((array) $keys as $key) { - $results[] = $array[$key]; - } - } - - return $results; - } - - /** - * Set an array item to a given value using "dot" notation. - * - * If no key is given to the method, the entire array will be replaced. - * - * @param array $array - * @param string $key - * @param mixed $value - * @return array - */ - public static function set(&$array, $key, $value): array - { - if (is_null($key)) return $array = $value; - - $keys = explode('.', $key); - - while (count($keys) > 1) - { - $key = array_shift($keys); - - // If the key doesn't exist at this depth, we will just create an empty array - // to hold the next value, allowing us to create the arrays to hold final - // values at the correct depth. Then we'll keep digging into the array. - if ( ! isset($array[$key]) || ! is_array($array[$key])) - { - $array[$key] = array(); - } - - $array =& $array[$key]; - } - - $array[array_shift($keys)] = $value; - - return $array; - } - - /** - * Shuffle the given array and return the result. - * - * @param array $array - * @param int|null $seed - * @return array - */ - public static function shuffle($array, $seed = null): array - { - if (is_null($seed)) { - shuffle($array); - } else { - mt_srand($seed); - shuffle($array); - mt_srand(); - } - - return $array; - } - - /** - * Sort the array using the given Closure. - * - * @param array $array - * @param callable|array|string|null $callback - * - * @return array - */ - public static function sort(array $array, $callback = null): array - { - return Collection::make($array)->sortBy($callback)->all(); - } - - /** - * Recursively sort an array by keys and values. - * - * @param array $array - * @param int $options - * @param bool $descending - * @return array - */ - public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false): array - { - foreach ($array as &$value) { - if (is_array($value)) { - $value = static::sortRecursive($value, $options, $descending); - } - } - - if (! array_is_list($array)) { - $descending - ? krsort($array, $options) - : ksort($array, $options); - } else { - $descending - ? rsort($array, $options) - : sort($array, $options); - } - - return $array; - } - - /** - * Conditionally compile classes from an array into a CSS class list. - * - * @param array $array - * - * @return string - */ - public static function toCssClasses(array $array): string - { - $classList = static::wrap($array); - - $classes = []; - - foreach ($classList as $class => $constraint) { - if (is_numeric($class)) { - $classes[] = $constraint; - } elseif ($constraint) { - $classes[] = $class; - } - } - - return implode(' ', $classes); - } - - /** - * Filter the array using the given callback. - * - * @param array $array - * @param callable $callback - * @return array - */ - public static function where($array, callable $callback): array - { - return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); - } - - /** - * Filter items where the value is not null. - * - * @param array $array - * @return array - */ - public static function whereNotNull($array) - { - return static::where($array, function ($value, $key) { - return ! is_null($value); - }); - } - - /** - * If the given value is not an array and not null, wrap it in one. - * - * @param mixed $value - * @return array - */ - public static function wrap($value): array - { - if (is_null($value)) { - return []; - } - - return is_array($value) ? $value : [$value]; - } -} diff --git a/src/Illuminate/Support/Collection.php b/src/Illuminate/Support/Collection.php deleted file mode 100755 index 01aec573..00000000 --- a/src/Illuminate/Support/Collection.php +++ /dev/null @@ -1,2050 +0,0 @@ - - * @implements \Illuminate\Support\Enumerable - */ -class Collection implements ArrayAccess, ArrayableInterface, CanBeEscapedWhenCastToString, Enumerable, JsonableInterface -{ - /** - * @use \Illuminate\Support\Traits\EnumeratesValues - */ - use EnumeratesValues, Macroable; - - /** - * The items contained in the collection. - * - * @var array - */ - protected $items = []; - - /** - * Create a new collection. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items - */ - public function __construct($items = []) - { - $this->items = $this->getArrayableItems($items); - } - - // ponytail: Laravel 4.2 holdovers kept so the fork's Database layer and existing - // callers keep working across the L13 shape change; removed in migration task 2.5 - // (lists → pluck, fetch → pluck) once app call-sites are converted. - - /** - * Get an array with the values of a given key. - * - * @param string $value - * @param string|null $key - * @return array - */ - public function lists(string $value, ?string $key = null): array - { - $results = []; - - foreach ($this->items as $item) { - $itemValue = is_object($item) ? $item->{$value} : $item[$value]; - - if (is_null($key)) { - $results[] = $itemValue; - } else { - $itemKey = is_object($item) ? $item->{$key} : $item[$key]; - - $results[$itemKey] = $itemValue; - } - } - - return $results; - } - - /** - * Fetch a nested element of the collection. - * - * @param string $key - * @return static - */ - public function fetch($key) - { - return new static(array_fetch($this->items, $key)); - } - - /** - * Create a new instance of the collection. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items - * @return static - */ - protected function newInstance($items = []) - { - return new static($items); - } - - /** - * Create a collection with the given range. - * - * @param int $from - * @param int $to - * @param int $step - * @return static - */ - public static function range($from, $to, $step = 1, ...$args) - { - return new static(range($from, $to, $step), ...$args); - } - - /** - * Get all of the items in the collection. - * - * @return array - */ - public function all() - { - return $this->items; - } - - /** - * Get a lazy collection for the items in this collection. - * - * @return \Illuminate\Support\LazyCollection - */ - public function lazy() - { - return new LazyCollection($this->items); - } - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return float|int|null - */ - public function median($key = null) - { - $values = (isset($key) ? $this->pluck($key) : $this) - ->reject(fn ($item) => is_null($item)) - ->sort()->values(); - - $count = $values->count(); - - if ($count === 0) { - return; - } - - $middle = intdiv($count, 2); - - if ($count % 2) { - return $values->get($middle); - } - - return $this->newInstance([ - $values->get($middle - 1), $values->get($middle), - ])->average(); - } - - /** - * Get the mode of a given key. - * - * @param string|array|null $key - * @return array|null - */ - public function mode($key = null) - { - if ($this->isEmpty()) { - return; - } - - $collection = isset($key) ? $this->pluck($key) : $this; - - $counts = $this->newInstance(); - - $collection->each(fn ($value) => $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1); - - $sorted = $counts->sort(); - - $highestValue = $sorted->last(); - - return $sorted->filter(fn ($value) => $value == $highestValue) - ->sort()->keys()->all(); - } - - /** - * Collapse the collection of items into a single array. - * - * @return static - */ - public function collapse() - { - return $this->newInstance(Arr::collapse($this->items)); - } - - /** - * Collapse the collection of items into a single array while preserving its keys. - * - * @return static - */ - public function collapseWithKeys() - { - if (! $this->items) { - return $this->newInstance(); - } - - $results = []; - - foreach ($this->items as $key => $values) { - if ($values instanceof Collection) { - $values = $values->all(); - } elseif (! is_array($values)) { - continue; - } - - $results[$key] = $values; - } - - if (! $results) { - return $this->newInstance(); - } - - return $this->newInstance(array_replace(...$results)); - } - - /** - * Determine if an item exists in the collection. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function contains($key, $operator = null, $value = null) - { - if (func_num_args() === 1) { - if ($this->useAsCallable($key)) { - return array_any($this->items, $key); - } - - return in_array($key, $this->items); - } - - return $this->contains($this->operatorForWhere(...func_get_args())); - } - - /** - * Determine if an item exists, using strict comparison. - * - * @param (callable(TValue): bool)|TValue|array-key $key - * @param TValue|null $value - * @return bool - */ - public function containsStrict($key, $value = null) - { - if (func_num_args() === 2) { - return $this->contains(fn ($item) => data_get($item, $key) === $value); - } - - if ($this->useAsCallable($key)) { - return ! is_null($this->first($key)); - } - - return in_array($key, $this->items, true); - } - - /** - * Determine if an item is not contained in the collection. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function doesntContain($key, $operator = null, $value = null) - { - return ! $this->contains(...func_get_args()); - } - - /** - * Determine if an item is not contained in the enumerable, using strict comparison. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function doesntContainStrict($key, $operator = null, $value = null) - { - return ! $this->containsStrict(...func_get_args()); - } - - /** - * Cross join with the given lists, returning all possible permutations. - * - * @template TCrossJoinKey - * @template TCrossJoinValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists - * @return static> - */ - public function crossJoin(...$lists) - { - return $this->newInstance(Arr::crossJoin( - $this->items, ...array_map($this->getArrayableItems(...), $lists) - )); - } - - /** - * Get the items in the collection that are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diff($items) - { - return $this->newInstance(array_diff($this->items, $this->getArrayableItems($items))); - } - - /** - * Get the items in the collection that are not present in the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function diffUsing($items, callable $callback) - { - return $this->newInstance(array_udiff($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Get the items in the collection whose keys and values are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diffAssoc($items) - { - return $this->newInstance(array_diff_assoc($this->items, $this->getArrayableItems($items))); - } - - /** - * Get the items in the collection whose keys and values are not present in the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TKey, TKey): int $callback - * @return static - */ - public function diffAssocUsing($items, callable $callback) - { - return $this->newInstance(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Get the items in the collection whose keys are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diffKeys($items) - { - return $this->newInstance(array_diff_key($this->items, $this->getArrayableItems($items))); - } - - /** - * Get the items in the collection whose keys are not present in the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TKey, TKey): int $callback - * @return static - */ - public function diffKeysUsing($items, callable $callback) - { - return $this->newInstance(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Retrieve duplicate items from the collection. - * - * @template TMapValue - * - * @param (callable(TValue): TMapValue)|string|null $callback - * @param bool $strict - * @return static - */ - public function duplicates($callback = null, $strict = false) - { - $items = $this->map($this->valueRetriever($callback)); - - $uniqueItems = $items->unique(null, $strict); - - $compare = $this->duplicateComparator($strict); - - $duplicates = $this->newInstance(); - - foreach ($items as $key => $value) { - if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) { - $uniqueItems->shift(); - } else { - $duplicates[$key] = $value; - } - } - - return $duplicates; - } - - /** - * Retrieve duplicate items from the collection using strict comparison. - * - * @template TMapValue - * - * @param (callable(TValue): TMapValue)|string|null $callback - * @return static - */ - public function duplicatesStrict($callback = null) - { - return $this->duplicates($callback, true); - } - - /** - * Get the comparison function to detect duplicates. - * - * @param bool $strict - * @return callable(TValue, TValue): bool - */ - protected function duplicateComparator($strict) - { - if ($strict) { - return fn ($a, $b) => $a === $b; - } - - return fn ($a, $b) => $a == $b; - } - - /** - * Get all items except for those with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array|string $keys - * @return static - */ - public function except($keys) - { - if (is_null($keys)) { - return $this->newInstance($this->items); - } - - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_array($keys)) { - $keys = func_get_args(); - } - - return $this->newInstance(Arr::except($this->items, $keys)); - } - - /** - * Run a filter over each of the items. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return static - */ - public function filter(?callable $callback = null) - { - if ($callback) { - return $this->newInstance(Arr::where($this->items, $callback)); - } - - return $this->newInstance(array_filter($this->items)); - } - - /** - * Get the first item from the collection passing the given truth test. - * - * @template TFirstDefault - * - * @param (callable(TValue, TKey): bool)|null $callback - * @param TFirstDefault|(\Closure(): TFirstDefault) $default - * @return TValue|TFirstDefault - */ - public function first(?callable $callback = null, $default = null) - { - return Arr::first($this->items, $callback, $default); - } - - /** - * Get a flattened array of the items in the collection. - * - * @param int $depth - * @return static - */ - public function flatten($depth = INF) - { - return $this->newInstance(Arr::flatten($this->items, $depth)); - } - - /** - * Flip the items in the collection. - * - * @return static - */ - public function flip() - { - return $this->newInstance(array_flip($this->items)); - } - - /** - * Remove an item from the collection by key. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|TKey $keys - * @return $this - */ - public function forget($keys) - { - foreach ($this->getArrayableItems($keys) as $key) { - $this->offsetUnset($key); - } - - return $this; - } - - /** - * Get an item from the collection by key. - * - * @template TGetDefault - * - * @param TKey|null $key - * @param TGetDefault|(\Closure(): TGetDefault) $default - * @return TValue|TGetDefault - */ - public function get($key, $default = null) - { - $key ??= ''; - - if (array_key_exists($key, $this->items)) { - return $this->items[$key]; - } - - return value($default); - } - - /** - * Get an item from the collection by key or add it to collection if it does not exist. - * - * @template TGetOrPutValue - * - * @param mixed $key - * @param TGetOrPutValue|(\Closure(): TGetOrPutValue) $value - * @return TValue|TGetOrPutValue - */ - public function getOrPut($key, $value) - { - if (array_key_exists($key ?? '', $this->items)) { - return $this->items[$key ?? '']; - } - - $this->offsetSet($key, $value = value($value)); - - return $value; - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function groupBy($groupBy, $preserveKeys = false) - { - if (! $this->useAsCallable($groupBy) && is_array($groupBy)) { - $nextGroups = $groupBy; - - $groupBy = array_shift($nextGroups); - } - - $groupBy = $this->valueRetriever($groupBy); - - $results = []; - - foreach ($this->items as $key => $value) { - $groupKeys = $groupBy($value, $key); - - if (! is_array($groupKeys)) { - $groupKeys = [$groupKeys]; - } - - foreach ($groupKeys as $groupKey) { - $groupKey = match (true) { - is_bool($groupKey) => (int) $groupKey, - $groupKey instanceof \UnitEnum => enum_value($groupKey), - $groupKey instanceof \Stringable, is_null($groupKey) => (string) $groupKey, - default => $groupKey, - }; - - if (! array_key_exists($groupKey, $results)) { - $results[$groupKey] = $this->newInstance(); - } - - $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); - } - } - - $result = $this->newInstance($results); - - if (! empty($nextGroups)) { - return $result->map->groupBy($nextGroups, $preserveKeys); - } - - return $result; - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function keyBy($keyBy) - { - $keyBy = $this->valueRetriever($keyBy); - - $results = []; - - foreach ($this->items as $key => $item) { - $resolvedKey = $keyBy($item, $key); - - if ($resolvedKey instanceof \UnitEnum) { - $resolvedKey = enum_value($resolvedKey); - } - - if (is_object($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - if (is_null($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - $results[$resolvedKey] = $item; - } - - return $this->newInstance($results); - } - - /** - * Determine if an item exists in the collection by key. - * - * @param TKey|array $key - * @return bool - */ - public function has($key) - { - $keys = is_array($key) ? $key : func_get_args(); - - return array_all($keys, fn ($key) => array_key_exists($key ?? '', $this->items)); - } - - /** - * Determine if any of the keys exist in the collection. - * - * @param TKey|array $key - * @return bool - */ - public function hasAny($key) - { - if ($this->isEmpty()) { - return false; - } - - $keys = is_array($key) ? $key : func_get_args(); - - return array_any($keys, fn ($key) => array_key_exists($key ?? '', $this->items)); - } - - /** - * Concatenate values of a given key as a string. - * - * @param (callable(TValue, TKey): mixed)|string|null $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null) - { - if ($this->useAsCallable($value)) { - return implode($glue ?? '', $this->map($value)->all()); - } - - $first = $this->first(); - - if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) { - return implode($glue ?? '', $this->pluck($value)->all()); - } - - return implode($value ?? '', $this->items); - } - - /** - * Intersect the collection with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersect($items) - { - return $this->newInstance(array_intersect($this->items, $this->getArrayableItems($items))); - } - - /** - * Intersect the collection with the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function intersectUsing($items, callable $callback) - { - return $this->newInstance(array_uintersect($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Intersect the collection with the given items with additional index check. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersectAssoc($items) - { - return $this->newInstance(array_intersect_assoc($this->items, $this->getArrayableItems($items))); - } - - /** - * Intersect the collection with the given items with additional index check, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function intersectAssocUsing($items, callable $callback) - { - return $this->newInstance(array_intersect_uassoc($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Intersect the collection with the given items by key. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersectByKeys($items) - { - return $this->newInstance(array_intersect_key( - $this->items, $this->getArrayableItems($items) - )); - } - - /** - * Determine if the collection is empty or not. - * - * @phpstan-assert-if-true null $this->first() - * @phpstan-assert-if-true null $this->last() - * - * @phpstan-assert-if-false TValue $this->first() - * @phpstan-assert-if-false TValue $this->last() - * - * @return bool - */ - public function isEmpty() - { - return empty($this->items); - } - - /** - * Determine if the collection contains exactly one item. If a callback is provided, determine if exactly one item matches the condition. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return bool - * - * @deprecated 12.49.0 Use the `hasSole()` method instead. - */ - public function containsOneItem(?callable $callback = null): bool - { - return $this->hasSole($callback); - } - - /** - * Determine if the collection contains multiple items. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return bool - * - * @deprecated 12.50.0 Use the `hasMany()` method instead. - */ - public function containsManyItems(?callable $callback = null): bool - { - return $this->hasMany($callback); - } - - /** - * Join all items from the collection using a string. The final items can use a separate glue string. - * - * @param string $glue - * @param string $finalGlue - * @return TValue|string - */ - public function join($glue, $finalGlue = '') - { - if ($finalGlue === '') { - return $this->implode($glue); - } - - $count = $this->count(); - - if ($count === 0) { - return ''; - } - - if ($count === 1) { - return $this->last(); - } - - $collection = $this->newInstance($this->items); - - $finalItem = $collection->pop(); - - return $collection->implode($glue).$finalGlue.$finalItem; - } - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys() - { - return $this->newInstance(array_keys($this->items)); - } - - /** - * Get the last item from the collection. - * - * @template TLastDefault - * - * @param (callable(TValue, TKey): bool)|null $callback - * @param TLastDefault|(\Closure(): TLastDefault) $default - * @return TValue|TLastDefault - */ - public function last(?callable $callback = null, $default = null) - { - return Arr::last($this->items, $callback, $default); - } - - /** - * Get the values of a given key. - * - * @param \Closure|string|int|array|null $value - * @param \Closure|string|null $key - * @return static - */ - public function pluck($value, $key = null) - { - return $this->newInstance(Arr::pluck($this->items, $value, $key)); - } - - /** - * Run a map over each of the items. - * - * @template TMapValue - * - * @param callable(TValue, TKey): TMapValue $callback - * @return static - */ - public function map(callable $callback) - { - return $this->newInstance(Arr::map($this->items, $callback)); - } - - /** - * Run a dictionary map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapToDictionaryKey of array-key - * @template TMapToDictionaryValue - * - * @param callable(TValue, TKey): array $callback - * @return static> - */ - public function mapToDictionary(callable $callback) - { - $dictionary = []; - - foreach ($this->items as $key => $item) { - $pair = $callback($item, $key); - - $key = key($pair); - - $value = reset($pair); - - if (! isset($dictionary[$key])) { - $dictionary[$key] = []; - } - - $dictionary[$key][] = $value; - } - - return $this->newInstance($dictionary); - } - - /** - * Run an associative map over each of the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapWithKeysKey of array-key - * @template TMapWithKeysValue - * - * @param callable(TValue, TKey): array $callback - * @return static - */ - public function mapWithKeys(callable $callback) - { - return $this->newInstance(Arr::mapWithKeys($this->items, $callback)); - } - - /** - * Merge the collection with the given items. - * - * @template TMergeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function merge($items) - { - return $this->newInstance(array_merge($this->items, $this->getArrayableItems($items))); - } - - /** - * Recursively merge the collection with the given items. - * - * @template TMergeRecursiveValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function mergeRecursive($items) - { - return $this->newInstance(array_merge_recursive($this->items, $this->getArrayableItems($items))); - } - - /** - * Multiply the items in the collection by the multiplier. - * - * @param int $multiplier - * @return static - */ - public function multiply(int $multiplier) - { - $new = $this->newInstance(); - - for ($i = 0; $i < $multiplier; $i++) { - $new->push(...$this->items); - } - - return $new; - } - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @template TCombineValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function combine($values) - { - return $this->newInstance(array_combine($this->all(), $this->getArrayableItems($values))); - } - - /** - * Union the collection with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function union($items) - { - return $this->newInstance($this->items + $this->getArrayableItems($items)); - } - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return ($step is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function nth($step, $offset = 0) - { - if ($step < 1) { - throw new InvalidArgumentException('Step value must be at least 1.'); - } - - $new = []; - - $position = 0; - - foreach ($this->slice($offset)->items as $item) { - if ($position % $step === 0) { - $new[] = $item; - } - - $position++; - } - - return $this->newInstance($new); - } - - /** - * Get the items with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array|string|null $keys - * @return static - */ - public function only($keys) - { - if (is_null($keys)) { - return $this->newInstance($this->items); - } - - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } - - $keys = is_array($keys) ? $keys : func_get_args(); - - return $this->newInstance(Arr::only($this->items, $keys)); - } - - /** - * Select specific values from the items within the collection. - * - * @param \Illuminate\Support\Enumerable|list|string|null $keys - * @return static - */ - public function select($keys) - { - if (is_null($keys)) { - return $this->newInstance($this->items); - } - - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } - - $keys = is_array($keys) ? $keys : func_get_args(); - - return $this->newInstance(Arr::select($this->items, $keys)); - } - - /** - * Get and remove the last N items from the collection. - * - * @param int $count - * @return ($count is 1 ? TValue|null : static) - */ - public function pop($count = 1) - { - if ($count < 1) { - return $this->newInstance(); - } - - if ($count === 1) { - return array_pop($this->items); - } - - if ($this->isEmpty()) { - return $this->newInstance(); - } - - $results = []; - - $collectionCount = $this->count(); - - foreach (range(1, min($count, $collectionCount)) as $item) { - $results[] = array_pop($this->items); - } - - return $this->newInstance($results); - } - - /** - * Push an item onto the beginning of the collection. - * - * @param TValue $value - * @param TKey $key - * @return $this - */ - public function prepend($value, $key = null) - { - $this->items = Arr::prepend($this->items, ...(func_num_args() > 1 ? func_get_args() : [$value])); - - return $this; - } - - /** - * Push one or more items onto the end of the collection. - * - * @param TValue ...$values - * @return $this - */ - public function push(...$values) - { - foreach ($values as $value) { - $this->items[] = $value; - } - - return $this; - } - - /** - * Prepend one or more items to the beginning of the collection. - * - * @param TValue ...$values - * @return $this - */ - public function unshift(...$values) - { - array_unshift($this->items, ...$values); - - return $this; - } - - /** - * Push all of the given items onto the collection. - * - * @template TConcatKey of array-key - * @template TConcatValue - * - * @param iterable $source - * @return static - */ - public function concat($source) - { - $result = $this->newInstance($this); - - foreach ($source as $item) { - $result->push($item); - } - - return $result; - } - - /** - * Get and remove an item from the collection. - * - * @template TPullDefault - * - * @param TKey $key - * @param TPullDefault|(\Closure(): TPullDefault) $default - * @return TValue|TPullDefault - */ - public function pull($key, $default = null) - { - return Arr::pull($this->items, $key, $default); - } - - /** - * Put an item in the collection by key. - * - * @param TKey $key - * @param TValue $value - * @return $this - */ - public function put($key, $value) - { - $this->offsetSet($key, $value); - - return $this; - } - - /** - * Get one or a specified number of items randomly from the collection. - * - * @param (callable(self): int)|int|null $number - * @param bool $preserveKeys - * @return ($number is null ? TValue : static) - * - * @throws \InvalidArgumentException - */ - public function random($number = null, $preserveKeys = false) - { - if (is_null($number)) { - return Arr::random($this->items); - } - - if (is_callable($number)) { - return $this->newInstance(Arr::random($this->items, $number($this), $preserveKeys)); - } - - return $this->newInstance(Arr::random($this->items, $number, $preserveKeys)); - } - - /** - * Replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replace($items) - { - return $this->newInstance(array_replace($this->items, $this->getArrayableItems($items))); - } - - /** - * Recursively replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replaceRecursive($items) - { - return $this->newInstance(array_replace_recursive($this->items, $this->getArrayableItems($items))); - } - - /** - * Reverse items order. - * - * @return static - */ - public function reverse() - { - return $this->newInstance(array_reverse($this->items, true)); - } - - /** - * Search the collection for a given value and return the corresponding key if successful. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TKey|false - */ - public function search($value, $strict = false) - { - if (! $this->useAsCallable($value)) { - return array_search($value, $this->items, $strict); - } - - return array_find_key($this->items, $value) ?? false; - } - - /** - * Get the item before the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function before($value, $strict = false) - { - $key = $this->search($value, $strict); - - if ($key === false) { - return null; - } - - $position = ($keys = $this->keys())->search($key); - - if ($position === 0) { - return null; - } - - return $this->get($keys->get($position - 1)); - } - - /** - * Get the item after the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function after($value, $strict = false) - { - $key = $this->search($value, $strict); - - if ($key === false) { - return null; - } - - $position = ($keys = $this->keys())->search($key); - - if ($position === $keys->count() - 1) { - return null; - } - - return $this->get($keys->get($position + 1)); - } - - /** - * Get and remove the first N items from the collection. - * - * @param int<0, max> $count - * @return ($count is 1 ? TValue|null : static) - * - * @throws \InvalidArgumentException - */ - public function shift($count = 1) - { - if ($count < 0) { - throw new InvalidArgumentException('Number of shifted items may not be less than zero.'); - } - - if ($this->isEmpty()) { - return null; - } - - if ($count === 0) { - return $this->newInstance(); - } - - if ($count === 1) { - return array_shift($this->items); - } - - $results = []; - - $collectionCount = $this->count(); - - foreach (range(1, min($count, $collectionCount)) as $item) { - $results[] = array_shift($this->items); - } - - return $this->newInstance($results); - } - - /** - * Shuffle the items in the collection. - * - * @return static - */ - public function shuffle() - { - return $this->newInstance(Arr::shuffle($this->items)); - } - - /** - * Create chunks representing a "sliding window" view of the items in the collection. - * - * @param positive-int $size - * @param positive-int $step - * @return static - * - * @throws \InvalidArgumentException - */ - public function sliding($size = 2, $step = 1) - { - if ($size < 1) { - throw new InvalidArgumentException('Size value must be at least 1.'); - } elseif ($step < 1) { - throw new InvalidArgumentException('Step value must be at least 1.'); - } - - $chunks = floor(($this->count() - $size) / $step) + 1; - - return static::times($chunks, fn ($number) => $this->slice(($number - 1) * $step, $size)); - } - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($count) - { - return $this->slice($count); - } - - /** - * Skip items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipUntil($value) - { - return $this->newInstance($this->lazy()->skipUntil($value)->all()); - } - - /** - * Skip items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipWhile($value) - { - return $this->newInstance($this->lazy()->skipWhile($value)->all()); - } - - /** - * Slice the underlying collection array. - * - * @param int $offset - * @param int|null $length - * @return static - */ - public function slice($offset, $length = null) - { - return $this->newInstance(array_slice($this->items, $offset, $length, true)); - } - - /** - * Split a collection into a certain number of groups. - * - * @param int $numberOfGroups - * @return ($numberOfGroups is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function split($numberOfGroups) - { - if ($numberOfGroups < 1) { - throw new InvalidArgumentException('Number of groups must be at least 1.'); - } - - if ($this->isEmpty()) { - return $this->newInstance(); - } - - $groups = $this->newInstance(); - - $groupSize = floor($this->count() / $numberOfGroups); - - $remain = $this->count() % $numberOfGroups; - - $start = 0; - - for ($i = 0; $i < $numberOfGroups; $i++) { - $size = $groupSize; - - if ($i < $remain) { - $size++; - } - - if ($size) { - $groups->push($this->newInstance(array_slice($this->items, $start, $size))); - - $start += $size; - } - } - - return $groups; - } - - /** - * Split a collection into a certain number of groups, and fill the first groups completely. - * - * @param int $numberOfGroups - * @return ($numberOfGroups is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function splitIn($numberOfGroups) - { - if ($numberOfGroups < 1) { - throw new InvalidArgumentException('Number of groups must be at least 1.'); - } - - return $this->chunk((int) ceil($this->count() / $numberOfGroups)); - } - - /** - * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return TValue - * - * @throws \Illuminate\Support\ItemNotFoundException - * @throws \Illuminate\Support\MultipleItemsFoundException - */ - public function sole($key = null, $operator = null, $value = null) - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - $items = $this->unless($filter == null)->filter($filter); - - $count = $items->count(); - - if ($count === 0) { - throw new ItemNotFoundException; - } - - if ($count > 1) { - throw new MultipleItemsFoundException($count); - } - - return $items->first(); - } - - /** - * Determine if the collection contains a single item, optionally matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasSole($key = null, $operator = null, $value = null): bool - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - return $this - ->unless($filter == null) - ->filter($filter) - ->count() === 1; - } - - /** - * Get the first item in the collection but throw an exception if no matching items exist. - * - * @param (callable(TValue, TKey): bool)|string $key - * @param mixed $operator - * @param mixed $value - * @return TValue - * - * @throws \Illuminate\Support\ItemNotFoundException - */ - public function firstOrFail($key = null, $operator = null, $value = null) - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - $placeholder = new stdClass(); - - $item = $this->first($filter, $placeholder); - - if ($item === $placeholder) { - throw new ItemNotFoundException; - } - - return $item; - } - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @param bool $preserveKeys - * @return ($preserveKeys is true ? static : static>) - */ - public function chunk($size, $preserveKeys = true) - { - if ($size <= 0) { - return $this->newInstance(); - } - - $chunks = []; - - foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { - $chunks[] = $this->newInstance($chunk); - } - - return $this->newInstance($chunks); - } - - /** - * Chunk the collection into chunks with a callback. - * - * @param callable(TValue, TKey, static): bool $callback - * @return static> - */ - public function chunkWhile(callable $callback) - { - return $this->newInstance( - $this->lazy()->chunkWhile($callback)->mapInto(static::class) - ); - } - - /** - * Sort through each item with a callback. - * - * @param (callable(TValue, TValue): int)|null|int $callback - * @return static - */ - public function sort($callback = null) - { - $items = $this->items; - - $callback && is_callable($callback) - ? uasort($items, $callback) - : asort($items, $callback ?? SORT_REGULAR); - - return $this->newInstance($items); - } - - /** - * Sort items in descending order. - * - * @param int-mask-of $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR) - { - $items = $this->items; - - arsort($items, $options); - - return $this->newInstance($items); - } - - /** - * Sort the collection using the given callback. - * - * @param array|(callable(TValue, TKey): mixed)|string|int $callback - * @param int-mask-of $options - * @param SortDirection|bool $descending - * @return static - */ - public function sortBy($callback, $options = SORT_REGULAR, $descending = false) - { - if (is_array($callback) && ! is_callable($callback)) { - return $this->sortByMany($callback, $options); - } - - $results = []; - - $callback = $this->valueRetriever($callback); - - // First we will loop through the items and get the comparator from a callback - // function which we were given. Then, we will sort the returned values and - // grab all the corresponding values for the sorted keys from this array. - foreach ($this->items as $key => $value) { - $results[$key] = $callback($value, $key); - } - - match ($descending) { - false, SortDirection::Ascending => asort($results, $options), - true, SortDirection::Descending => arsort($results, $options), - }; - - // Once we have sorted all of the keys in the array, we will loop through them - // and grab the corresponding model so we can set the underlying items list - // to the sorted version. Then we'll just return the collection instance. - foreach (array_keys($results) as $key) { - $results[$key] = $this->items[$key]; - } - - return $this->newInstance($results); - } - - /** - * Sort the collection using multiple comparisons. - * - * @param array $comparisons - * @param int-mask-of $options - * @return static - */ - protected function sortByMany(array $comparisons = [], int $options = SORT_REGULAR) - { - $items = $this->items; - - uasort($items, function ($a, $b) use ($comparisons, $options) { - foreach ($comparisons as $comparison) { - $comparison = Arr::wrap($comparison); - - $prop = $comparison[0]; - - $direction = match (Arr::get($comparison, 1, true)) { - true, 'asc', SortDirection::Ascending => SortDirection::Ascending, - false, 'desc', SortDirection::Descending => SortDirection::Descending, - default => SortDirection::Descending, // for backwards compatibility - }; - - if (! is_string($prop) && is_callable($prop)) { - $result = $prop($a, $b); - } else { - $values = [data_get($a, $prop), data_get($b, $prop)]; - - if ($direction === SortDirection::Descending) { - $values = array_reverse($values); - } - - if (($options & SORT_FLAG_CASE) === SORT_FLAG_CASE) { - if (($options & SORT_NATURAL) === SORT_NATURAL) { - $result = strnatcasecmp($values[0], $values[1]); - } else { - $result = strcasecmp($values[0], $values[1]); - } - } else { - $result = match ($options) { - SORT_NUMERIC => (int) $values[0] <=> (int) $values[1], - SORT_STRING => strcmp($values[0], $values[1]), - SORT_NATURAL => strnatcmp((string) $values[0], (string) $values[1]), - SORT_LOCALE_STRING => strcoll($values[0], $values[1]), - default => $values[0] <=> $values[1], - }; - } - } - - if ($result === 0) { - continue; - } - - return $result; - } - }); - - return $this->newInstance($items); - } - - /** - * Sort the collection in descending order using the given callback. - * - * @param array|(callable(TValue, TKey): mixed)|string|int $callback - * @param int-mask-of $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR) - { - if (is_array($callback) && ! is_callable($callback)) { - foreach ($callback as $index => $key) { - $comparison = Arr::wrap($key); - - $comparison[1] = SortDirection::Descending; - - $callback[$index] = $comparison; - } - } - - return $this->sortBy($callback, $options, true); - } - - /** - * Sort the collection keys. - * - * @param int-mask-of $options - * @param SortDirection|bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false) - { - $items = $this->items; - - match ($descending) { - false, SortDirection::Ascending => ksort($items, $options), - true, SortDirection::Descending => krsort($items, $options), - }; - - return $this->newInstance($items); - } - - /** - * Sort the collection keys in descending order. - * - * @param int-mask-of $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR) - { - return $this->sortKeys($options, SortDirection::Descending); - } - - /** - * Sort the collection keys using a callback. - * - * @param callable(TKey, TKey): int $callback - * @return static - */ - public function sortKeysUsing(callable $callback) - { - $items = $this->items; - - uksort($items, $callback); - - return $this->newInstance($items); - } - - /** - * Splice a portion of the underlying collection array. - * - * @param int $offset - * @param int|null $length - * @param array $replacement - * @return static - */ - public function splice($offset, $length = null, $replacement = []) - { - if (func_num_args() === 1) { - return $this->newInstance(array_splice($this->items, $offset)); - } - - return $this->newInstance(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement))); - } - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit) - { - if ($limit < 0) { - return $this->slice($limit, abs($limit)); - } - - return $this->slice(0, $limit); - } - - /** - * Take items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeUntil($value) - { - return $this->newInstance($this->lazy()->takeUntil($value)->all()); - } - - /** - * Take items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeWhile($value) - { - return $this->newInstance($this->lazy()->takeWhile($value)->all()); - } - - /** - * Transform each item in the collection using a callback. - * - * @template TMapValue - * - * @param callable(TValue, TKey): TMapValue $callback - * @return $this - * - * @phpstan-this-out static - */ - public function transform(callable $callback) - { - $this->items = $this->map($callback)->all(); - - return $this; - } - - /** - * Flatten a multi-dimensional associative array with dots. - * - * @param int $depth - * @return static - */ - public function dot($depth = INF) - { - return $this->newInstance(Arr::dot($this->all(), '', $depth)); - } - - /** - * Convert a flatten "dot" notation array into an expanded array. - * - * @return static - */ - public function undot() - { - return $this->newInstance(Arr::undot($this->all())); - } - - /** - * Return only unique items from the collection array. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false) - { - if (is_null($key) && $strict === false) { - return $this->newInstance(array_unique($this->items, SORT_REGULAR)); - } - - $callback = $this->valueRetriever($key); - - $exists = []; - - return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { - if (in_array($id = $callback($item, $key), $exists, $strict)) { - return true; - } - - $exists[] = $id; - }); - } - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values() - { - return $this->newInstance(array_values($this->items)); - } - - /** - * Zip the collection together with one or more arrays. - * - * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); - * => [[1, 4], [2, 5], [3, 6]] - * - * @template TZipValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items - * @return static> - */ - public function zip($items) - { - $arrayableItems = array_map(fn ($items) => $this->getArrayableItems($items), func_get_args()); - - $params = array_merge([fn () => $this->newInstance(func_get_args()), $this->items], $arrayableItems); - - return $this->newInstance(array_map(...$params)); - } - - /** - * Pad collection to the specified length with a value. - * - * @template TPadValue - * - * @param int $size - * @param TPadValue $value - * @return static - */ - public function pad($size, $value) - { - return $this->newInstance(array_pad($this->items, $size, $value)); - } - - /** - * Get an iterator for the items. - * - * @return \ArrayIterator - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } - - /** - * Count the number of items in the collection. - * - * @return int<0, max> - */ - public function count(): int - { - return count($this->items); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function countBy($countBy = null) - { - return $this->newInstance($this->lazy()->countBy($countBy)->all()); - } - - /** - * Add an item to the collection. - * - * @param TValue $item - * @return $this - */ - public function add($item) - { - $this->items[] = $item; - - return $this; - } - - /** - * Get a base Support collection instance from this collection. - * - * @return \Illuminate\Support\Collection - */ - public function toBase() - { - return new self($this); - } - - /** - * Determine if an item exists at an offset. - * - * @param TKey $offset - * @return bool - */ - public function offsetExists($offset): bool - { - return isset($this->items[$offset]); - } - - /** - * Get an item at a given offset. - * - * @param TKey $offset - * @return TValue - */ - public function offsetGet($offset): mixed - { - return $this->items[$offset]; - } - - /** - * Set the item at a given offset. - * - * @param TKey|null $offset - * @param TValue $value - * @return void - */ - public function offsetSet($offset, $value): void - { - if (is_null($offset)) { - $this->items[] = $value; - } else { - $this->items[$offset] = $value; - } - } - - /** - * Unset the item at a given offset. - * - * @param TKey $offset - * @return void - */ - public function offsetUnset($offset): void - { - unset($this->items[$offset]); - } -} diff --git a/src/Illuminate/Support/Enumerable.php b/src/Illuminate/Support/Enumerable.php deleted file mode 100644 index 36e04705..00000000 --- a/src/Illuminate/Support/Enumerable.php +++ /dev/null @@ -1,1379 +0,0 @@ - - * @extends \IteratorAggregate - */ -interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable -{ - /** - * Create a new collection instance if the value isn't one already. - * - * @template TMakeKey of array-key - * @template TMakeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items - * @return static - */ - public static function make($items = []); - - /** - * Create a new instance by invoking the callback a given amount of times. - * - * @template TTimesValue - * - * @param int $number - * @param (callable(int): TTimesValue)|null $callback - * @return static - */ - public static function times($number, ?callable $callback = null); - - /** - * Create a collection with the given range. - * - * @param int $from - * @param int $to - * @param int $step - * @return static - */ - public static function range($from, $to, $step = 1); - - /** - * Wrap the given value in a collection if applicable. - * - * @template TWrapValue - * - * @param iterable|TWrapValue $value - * @return static - */ - public static function wrap($value); - - /** - * Get the underlying items from the given collection if applicable. - * - * @template TUnwrapKey of array-key - * @template TUnwrapValue - * - * @param array|static $value - * @return array - */ - public static function unwrap($value); - - /** - * Create a new instance with no items. - * - * @return static - */ - public static function empty(); - - /** - * Get all items in the enumerable. - * - * @return array - */ - public function all(); - - /** - * Alias for the "avg" method. - * - * @param (callable(TValue): float|int)|string|null $callback - * @return float|int|null - */ - public function average($callback = null); - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return float|int|null - */ - public function median($key = null); - - /** - * Get the mode of a given key. - * - * @param string|array|null $key - * @return array|null - */ - public function mode($key = null); - - /** - * Collapse the items into a single enumerable. - * - * @return static - */ - public function collapse(); - - /** - * Alias for the "contains" method. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function some($key, $operator = null, $value = null); - - /** - * Determine if an item exists, using strict comparison. - * - * @param (callable(TValue): bool)|TValue|array-key $key - * @param TValue|null $value - * @return bool - */ - public function containsStrict($key, $value = null); - - /** - * Get the average value of a given key. - * - * @param (callable(TValue): float|int)|string|null $callback - * @return float|int|null - */ - public function avg($callback = null); - - /** - * Determine if an item exists in the enumerable. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function contains($key, $operator = null, $value = null); - - /** - * Determine if an item is not contained in the collection. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function doesntContain($key, $operator = null, $value = null); - - /** - * Cross join with the given lists, returning all possible permutations. - * - * @template TCrossJoinKey - * @template TCrossJoinValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists - * @return static> - */ - public function crossJoin(...$lists); - - /** - * Dump the collection and end the script. - * - * @param mixed ...$args - * @return never - */ - public function dd(...$args); - - /** - * Dump the collection. - * - * @param mixed ...$args - * @return $this - */ - public function dump(...$args); - - /** - * Get the items that are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diff($items); - - /** - * Get the items that are not present in the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function diffUsing($items, callable $callback); - - /** - * Get the items whose keys and values are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diffAssoc($items); - - /** - * Get the items whose keys and values are not present in the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TKey, TKey): int $callback - * @return static - */ - public function diffAssocUsing($items, callable $callback); - - /** - * Get the items whose keys are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diffKeys($items); - - /** - * Get the items whose keys are not present in the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TKey, TKey): int $callback - * @return static - */ - public function diffKeysUsing($items, callable $callback); - - /** - * Retrieve duplicate items. - * - * @param (callable(TValue): bool)|string|null $callback - * @param bool $strict - * @return static - */ - public function duplicates($callback = null, $strict = false); - - /** - * Retrieve duplicate items using strict comparison. - * - * @param (callable(TValue): bool)|string|null $callback - * @return static - */ - public function duplicatesStrict($callback = null); - - /** - * Execute a callback over each item. - * - * @param callable(TValue, TKey): mixed $callback - * @return $this - */ - public function each(callable $callback); - - /** - * Execute a callback over each nested chunk of items. - * - * @param callable $callback - * @return static - */ - public function eachSpread(callable $callback); - - /** - * Determine if all items pass the given truth test. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function every($key, $operator = null, $value = null); - - /** - * Get all items except for those with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array $keys - * @return static - */ - public function except($keys); - - /** - * Run a filter over each of the items. - * - * @param (callable(TValue): bool)|null $callback - * @return static - */ - public function filter(?callable $callback = null); - - /** - * Apply the callback if the given "value" is (or resolves to) truthy. - * - * @template TWhenReturnType as null - * - * @param bool $value - * @param (callable($this): TWhenReturnType)|null $callback - * @param (callable($this): TWhenReturnType)|null $default - * @return $this|TWhenReturnType - */ - public function when($value, ?callable $callback = null, ?callable $default = null); - - /** - * Apply the callback if the collection is empty. - * - * @template TWhenEmptyReturnType - * - * @param (callable($this): TWhenEmptyReturnType) $callback - * @param (callable($this): TWhenEmptyReturnType)|null $default - * @return $this|TWhenEmptyReturnType - */ - public function whenEmpty(callable $callback, ?callable $default = null); - - /** - * Apply the callback if the collection is not empty. - * - * @template TWhenNotEmptyReturnType - * - * @param callable($this): TWhenNotEmptyReturnType $callback - * @param (callable($this): TWhenNotEmptyReturnType)|null $default - * @return $this|TWhenNotEmptyReturnType - */ - public function whenNotEmpty(callable $callback, ?callable $default = null); - - /** - * Apply the callback if the given "value" is (or resolves to) falsy. - * - * @template TUnlessReturnType - * - * @param bool $value - * @param (callable($this): TUnlessReturnType) $callback - * @param (callable($this): TUnlessReturnType)|null $default - * @return $this|TUnlessReturnType - */ - public function unless($value, callable $callback, ?callable $default = null); - - /** - * Apply the callback unless the collection is empty. - * - * @template TUnlessEmptyReturnType - * - * @param callable($this): TUnlessEmptyReturnType $callback - * @param (callable($this): TUnlessEmptyReturnType)|null $default - * @return $this|TUnlessEmptyReturnType - */ - public function unlessEmpty(callable $callback, ?callable $default = null); - - /** - * Apply the callback unless the collection is not empty. - * - * @template TUnlessNotEmptyReturnType - * - * @param callable($this): TUnlessNotEmptyReturnType $callback - * @param (callable($this): TUnlessNotEmptyReturnType)|null $default - * @return $this|TUnlessNotEmptyReturnType - */ - public function unlessNotEmpty(callable $callback, ?callable $default = null); - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param mixed $operator - * @param mixed $value - * @return static - */ - public function where($key, $operator = null, $value = null); - - /** - * Filter items where the value for the given key is null. - * - * @param string|null $key - * @return static - */ - public function whereNull($key = null); - - /** - * Filter items where the value for the given key is not null. - * - * @param string|null $key - * @return static - */ - public function whereNotNull($key = null); - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param mixed $value - * @return static - */ - public function whereStrict($key, $value); - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @param bool $strict - * @return static - */ - public function whereIn($key, $values, $strict = false); - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereInStrict($key, $values); - - /** - * Filter items such that the value of the given key is between the given values. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereBetween($key, $values); - - /** - * Filter items such that the value of the given key is not between the given values. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereNotBetween($key, $values); - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @param bool $strict - * @return static - */ - public function whereNotIn($key, $values, $strict = false); - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereNotInStrict($key, $values); - - /** - * Filter the items, removing any items that don't match the given type(s). - * - * @template TWhereInstanceOf - * - * @param class-string|array> $type - * @return static - */ - public function whereInstanceOf($type); - - /** - * Get the first item from the enumerable passing the given truth test. - * - * @template TFirstDefault - * - * @param (callable(TValue,TKey): bool)|null $callback - * @param TFirstDefault|(\Closure(): TFirstDefault) $default - * @return TValue|TFirstDefault - */ - public function first(?callable $callback = null, $default = null); - - /** - * Get the first item by the given key value pair. - * - * @param string $key - * @param mixed $operator - * @param mixed $value - * @return TValue|null - */ - public function firstWhere($key, $operator = null, $value = null); - - /** - * Get a flattened array of the items in the collection. - * - * @param int $depth - * @return static - */ - public function flatten($depth = INF); - - /** - * Flip the values with their keys. - * - * @return static - */ - public function flip(); - - /** - * Get an item from the collection by key. - * - * @template TGetDefault - * - * @param TKey $key - * @param TGetDefault|(\Closure(): TGetDefault) $default - * @return TValue|TGetDefault - */ - public function get($key, $default = null); - - /** - * Group an associative array by a field or using a callback. - * - * @template TGroupKey of array-key|\UnitEnum|\Stringable - * - * @param (callable(TValue, TKey): TGroupKey)|array|string $groupBy - * @param bool $preserveKeys - * @return static< - * ($groupBy is (array|string) - * ? array-key - * : (TGroupKey is \UnitEnum ? array-key : (TGroupKey is \Stringable ? string : TGroupKey))), - * static<($preserveKeys is true ? TKey : int), ($groupBy is array ? mixed : TValue)> - * > - */ - public function groupBy($groupBy, $preserveKeys = false); - - /** - * Key an associative array by a field or using a callback. - * - * @template TNewKey of array-key|\UnitEnum - * - * @param (callable(TValue, TKey): TNewKey)|array|string $keyBy - * @return static<($keyBy is (array|string) ? array-key : (TNewKey is \UnitEnum ? array-key : TNewKey)), TValue> - */ - public function keyBy($keyBy); - - /** - * Determine if an item exists in the collection by key. - * - * @param TKey|array $key - * @return bool - */ - public function has($key); - - /** - * Determine if any of the keys exist in the collection. - * - * @param mixed $key - * @return bool - */ - public function hasAny($key); - - /** - * Concatenate values of a given key as a string. - * - * @param (callable(TValue, TKey): mixed)|string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null); - - /** - * Intersect the collection with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersect($items); - - /** - * Intersect the collection with the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function intersectUsing($items, callable $callback); - - /** - * Intersect the collection with the given items with additional index check. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersectAssoc($items); - - /** - * Intersect the collection with the given items with additional index check, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function intersectAssocUsing($items, callable $callback); - - /** - * Intersect the collection with the given items by key. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersectByKeys($items); - - /** - * Determine if the collection is empty or not. - * - * @return bool - */ - public function isEmpty(); - - /** - * Determine if the collection is not empty. - * - * @return bool - */ - public function isNotEmpty(); - - /** - * Determine if the collection contains a single item. - * - * @return bool - */ - public function containsOneItem(); - - /** - * Determine if the collection contains multiple items. - * - * @return bool - */ - public function containsManyItems(); - - /** - * Determine if the collection contains a single item, optionally matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasSole($key = null, $operator = null, $value = null); - - /** - * Determine if the collection contains multiple items, optionally matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasMany($key = null, $operator = null, $value = null); - - /** - * Join all items from the collection using a string. The final items can use a separate glue string. - * - * @param string $glue - * @param string $finalGlue - * @return string - */ - public function join($glue, $finalGlue = ''); - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys(); - - /** - * Get the last item from the collection. - * - * @template TLastDefault - * - * @param (callable(TValue, TKey): bool)|null $callback - * @param TLastDefault|(\Closure(): TLastDefault) $default - * @return TValue|TLastDefault - */ - public function last(?callable $callback = null, $default = null); - - /** - * Run a map over each of the items. - * - * @template TMapValue - * - * @param callable(TValue, TKey): TMapValue $callback - * @return static - */ - public function map(callable $callback); - - /** - * Run a map over each nested chunk of items. - * - * @param callable $callback - * @return static - */ - public function mapSpread(callable $callback); - - /** - * Run a dictionary map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapToDictionaryKey of array-key - * @template TMapToDictionaryValue - * - * @param callable(TValue, TKey): array $callback - * @return static> - */ - public function mapToDictionary(callable $callback); - - /** - * Run a grouping map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapToGroupsKey of array-key - * @template TMapToGroupsValue - * - * @param callable(TValue, TKey): array $callback - * @return static> - */ - public function mapToGroups(callable $callback); - - /** - * Run an associative map over each of the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapWithKeysKey of array-key - * @template TMapWithKeysValue - * - * @param callable(TValue, TKey): array $callback - * @return static - */ - public function mapWithKeys(callable $callback); - - /** - * Map a collection and flatten the result by a single level. - * - * @template TFlatMapKey of array-key - * @template TFlatMapValue - * - * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback - * @return static - */ - public function flatMap(callable $callback); - - /** - * Map the values into a new class. - * - * @template TMapIntoValue - * - * @param class-string $class - * @return static - */ - public function mapInto($class); - - /** - * Merge the collection with the given items. - * - * @template TMergeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function merge($items); - - /** - * Recursively merge the collection with the given items. - * - * @template TMergeRecursiveValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function mergeRecursive($items); - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @template TCombineValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function combine($values); - - /** - * Union the collection with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function union($items); - - /** - * Get the min value of a given key. - * - * @template TMinResult = mixed - * - * @param (callable(TValue): TMinResult)|string|null $callback - * @return ($callback is callable ? ?TMinResult : ($callback is null ? ?TValue : mixed)) - */ - public function min($callback = null); - - /** - * Get the max value of a given key. - * - * @template TMaxResult = mixed - * - * @param (callable(TValue): TMaxResult)|string|null $callback - * @return ($callback is callable ? ?TMaxResult : ($callback is null ? ?TValue : mixed)) - */ - public function max($callback = null); - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return static - */ - public function nth($step, $offset = 0); - - /** - * Get the items with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array|string $keys - * @return static - */ - public function only($keys); - - /** - * "Paginate" the collection by slicing it into a smaller collection. - * - * @param int $page - * @param int $perPage - * @return static - */ - public function forPage($page, $perPage); - - /** - * Partition the collection into two arrays using the given callback or key. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return static, static> - */ - public function partition($key, $operator = null, $value = null); - - /** - * Push all of the given items onto the collection. - * - * @template TConcatKey of array-key - * @template TConcatValue - * - * @param iterable $source - * @return static - */ - public function concat($source); - - /** - * Get one or a specified number of items randomly from the collection. - * - * @param int|null $number - * @return ($number is null ? TValue : static) - * - * @throws \InvalidArgumentException - */ - public function random($number = null); - - /** - * Reduce the collection to a single value. - * - * @template TReduceInitial - * @template TReduceReturnType - * - * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback - * @param TReduceInitial $initial - * @return TReduceInitial|TReduceReturnType - */ - public function reduce(callable $callback, $initial = null); - - /** - * Reduce the collection to a single value by mutating an initial value. - * - * @template TReduceIntoInitial - * - * @param TReduceIntoInitial $initial - * @param callable(TReduceIntoInitial, TValue, TKey): void $callback - * @return TReduceIntoInitial - */ - public function reduceInto($initial, callable $callback); - - /** - * Reduce the collection to multiple aggregate values. - * - * @param callable $callback - * @param mixed ...$initial - * @return array - * - * @throws \UnexpectedValueException - */ - public function reduceSpread(callable $callback, ...$initial); - - /** - * Replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replace($items); - - /** - * Recursively replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replaceRecursive($items); - - /** - * Reverse items order. - * - * @return static - */ - public function reverse(); - - /** - * Search the collection for a given value and return the corresponding key if successful. - * - * @param TValue|callable(TValue,TKey): bool $value - * @param bool $strict - * @return TKey|false - */ - public function search($value, $strict = false); - - /** - * Get the item before the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function before($value, $strict = false); - - /** - * Get the item after the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function after($value, $strict = false); - - /** - * Shuffle the items in the collection. - * - * @return static - */ - public function shuffle(); - - /** - * Create chunks representing a "sliding window" view of the items in the collection. - * - * @param int $size - * @param int $step - * @return static - */ - public function sliding($size = 2, $step = 1); - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($count); - - /** - * Skip items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipUntil($value); - - /** - * Skip items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipWhile($value); - - /** - * Get a slice of items from the enumerable. - * - * @param int $offset - * @param int|null $length - * @return static - */ - public function slice($offset, $length = null); - - /** - * Split a collection into a certain number of groups. - * - * @param int $numberOfGroups - * @return static - */ - public function split($numberOfGroups); - - /** - * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. - * - * @param (callable(TValue, TKey): bool)|string $key - * @param mixed $operator - * @param mixed $value - * @return TValue - * - * @throws \Illuminate\Support\ItemNotFoundException - * @throws \Illuminate\Support\MultipleItemsFoundException - */ - public function sole($key = null, $operator = null, $value = null); - - /** - * Get the first item in the collection but throw an exception if no matching items exist. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return TValue - * - * @throws \Illuminate\Support\ItemNotFoundException - */ - public function firstOrFail($key = null, $operator = null, $value = null); - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @return static - */ - public function chunk($size); - - /** - * Chunk the collection into chunks with a callback. - * - * @param callable(TValue, TKey, static): bool $callback - * @return static> - */ - public function chunkWhile(callable $callback); - - /** - * Chunk the collection into chunks by comparing adjacent values using the given key or callback. - * - * @param (callable(TValue, TKey): mixed)|string $key - * @return static> - */ - public function chunkBy($key); - - /** - * Split a collection into a certain number of groups, and fill the first groups completely. - * - * @param int $numberOfGroups - * @return static - */ - public function splitIn($numberOfGroups); - - /** - * Sort through each item with a callback. - * - * @param (callable(TValue, TValue): int)|null|int $callback - * @return static - */ - public function sort($callback = null); - - /** - * Sort items in descending order. - * - * @param int-mask-of $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR); - - /** - * Sort the collection using the given callback. - * - * @param array|(callable(TValue, TKey): mixed)|string|int $callback - * @param int-mask-of $options - * @param bool $descending - * @return static - */ - public function sortBy($callback, $options = SORT_REGULAR, $descending = false); - - /** - * Sort the collection in descending order using the given callback. - * - * @param array|(callable(TValue, TKey): mixed)|string|int $callback - * @param int-mask-of $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR); - - /** - * Sort the collection keys. - * - * @param int-mask-of $options - * @param bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false); - - /** - * Sort the collection keys in descending order. - * - * @param int-mask-of $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR); - - /** - * Sort the collection keys using a callback. - * - * @param callable(TKey, TKey): int $callback - * @return static - */ - public function sortKeysUsing(callable $callback); - - /** - * Get the sum of the given values. - * - * @param (callable(TValue, TKey): mixed)|string|null $callback - * @return mixed - */ - public function sum($callback = null); - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit); - - /** - * Take items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeUntil($value); - - /** - * Take items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeWhile($value); - - /** - * Pass the collection to the given callback and then return it. - * - * @param callable(TValue): mixed $callback - * @return $this - */ - public function tap(callable $callback); - - /** - * Pass the enumerable to the given callback and return the result. - * - * @template TPipeReturnType - * - * @param callable($this): TPipeReturnType $callback - * @return TPipeReturnType - */ - public function pipe(callable $callback); - - /** - * Pass the collection into a new class. - * - * @template TPipeIntoValue - * - * @param class-string $class - * @return TPipeIntoValue - */ - public function pipeInto($class); - - /** - * Pass the collection through a series of callable pipes and return the result. - * - * @param array $pipes - * @return mixed - */ - public function pipeThrough($pipes); - - /** - * Get the values of a given key. - * - * @param string|array $value - * @param string|null $key - * @return static - */ - public function pluck($value, $key = null); - - /** - * Create a collection of all elements that do not pass a given truth test. - * - * @param (callable(TValue, TKey): bool)|bool|TValue $callback - * @return static - */ - public function reject($callback = true); - - /** - * Convert a flatten "dot" notation array into an expanded array. - * - * @return static - */ - public function undot(); - - /** - * Return only unique items from the collection array. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false); - - /** - * Return only unique items from the collection array using strict comparison. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @return static - */ - public function uniqueStrict($key = null); - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values(); - - /** - * Pad collection to the specified length with a value. - * - * @template TPadValue - * - * @param int $size - * @param TPadValue $value - * @return static - */ - public function pad($size, $value); - - /** - * Get the values iterator. - * - * @return \Traversable - */ - public function getIterator(): Traversable; - - /** - * Count the number of items in the collection. - * - * @return int - */ - public function count(): int; - - /** - * Count the number of items in the collection by a field or using a callback. - * - * @param (callable(TValue, TKey): (array-key|\UnitEnum))|string|null $countBy - * @return static - */ - public function countBy($countBy = null); - - /** - * Zip the collection together with one or more arrays. - * - * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); - * => [[1, 4], [2, 5], [3, 6]] - * - * @template TZipValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items - * @return static> - */ - public function zip($items); - - /** - * Collect the values into a collection. - * - * @return \Illuminate\Support\Collection - */ - public function collect(); - - /** - * Get the collection of items as a plain array. - * - * @return array - */ - public function toArray(); - - /** - * Convert the object into something JSON serializable. - * - * @return mixed - */ - public function jsonSerialize(): mixed; - - /** - * Get the collection of items as JSON. - * - * @param int $options - * @return string - */ - public function toJson($options = 0); - - /** - * Get the collection of items as pretty print formatted JSON. - * - * @param int $options - * @return string - */ - public function toPrettyJson(int $options = 0); - - /** - * Get a CachingIterator instance. - * - * @param int $flags - * @return \CachingIterator - */ - public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING); - - /** - * Convert the collection to its string representation. - * - * @return string - */ - public function __toString(); - - /** - * Indicate that the model's string representation should be escaped when __toString is invoked. - * - * @param bool $escape - * @return $this - */ - public function escapeWhenCastingToString($escape = true); - - /** - * Add a method to the list of proxied methods. - * - * @param string $method - * @return void - */ - public static function proxy($method); - - /** - * Dynamically access collection proxies. - * - * @param string $key - * @return mixed - * - * @throws \Exception - */ - public function __get($key); -} diff --git a/src/Illuminate/Support/HigherOrderCollectionProxy.php b/src/Illuminate/Support/HigherOrderCollectionProxy.php deleted file mode 100644 index 035d0fda..00000000 --- a/src/Illuminate/Support/HigherOrderCollectionProxy.php +++ /dev/null @@ -1,69 +0,0 @@ - - * @mixin TValue - */ -class HigherOrderCollectionProxy -{ - /** - * The collection being operated on. - * - * @var \Illuminate\Support\Enumerable - */ - protected $collection; - - /** - * The method being proxied. - * - * @var string - */ - protected $method; - - /** - * Create a new proxy instance. - * - * @param \Illuminate\Support\Enumerable $collection - * @param string $method - */ - public function __construct(Enumerable $collection, $method) - { - $this->method = $method; - $this->collection = $collection; - } - - /** - * Proxy accessing an attribute onto the collection items. - * - * @param string $key - * @return mixed - */ - public function __get($key) - { - return $this->collection->{$this->method}(function ($value) use ($key) { - return is_array($value) ? $value[$key] : $value->{$key}; - }); - } - - /** - * Proxy a method call onto the collection items. - * - * @param string $method - * @param array $parameters - * @return mixed - */ - public function __call($method, $parameters) - { - return $this->collection->{$this->method}(function ($value) use ($method, $parameters) { - return is_string($value) - ? $value::{$method}(...$parameters) - : $value->{$method}(...$parameters); - }); - } -} diff --git a/src/Illuminate/Support/HigherOrderWhenProxy.php b/src/Illuminate/Support/HigherOrderWhenProxy.php deleted file mode 100644 index 0a694c24..00000000 --- a/src/Illuminate/Support/HigherOrderWhenProxy.php +++ /dev/null @@ -1,108 +0,0 @@ -target = $target; - } - - /** - * Set the condition on the proxy. - * - * @param bool $condition - * @return $this - */ - public function condition($condition) - { - [$this->condition, $this->hasCondition] = [$condition, true]; - - return $this; - } - - /** - * Indicate that the condition should be negated. - * - * @return $this - */ - public function negateConditionOnCapture() - { - $this->negateConditionOnCapture = true; - - return $this; - } - - /** - * Proxy accessing an attribute onto the target. - * - * @param string $key - * @return mixed - */ - public function __get($key) - { - if (! $this->hasCondition) { - $condition = $this->target->{$key}; - - return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition); - } - - return $this->condition - ? $this->target->{$key} - : $this->target; - } - - /** - * Proxy a method call on the target. - * - * @param string $method - * @param array $parameters - * @return mixed - */ - public function __call($method, $parameters) - { - if (! $this->hasCondition) { - $condition = $this->target->{$method}(...$parameters); - - return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition); - } - - return $this->condition - ? $this->target->{$method}(...$parameters) - : $this->target; - } -} diff --git a/src/Illuminate/Support/ItemNotFoundException.php b/src/Illuminate/Support/ItemNotFoundException.php deleted file mode 100644 index 05a51d95..00000000 --- a/src/Illuminate/Support/ItemNotFoundException.php +++ /dev/null @@ -1,9 +0,0 @@ - - */ -class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable -{ - /** - * @use \Illuminate\Support\Traits\EnumeratesValues - */ - use EnumeratesValues, Macroable; - - /** - * The source from which to generate items. - * - * @var (Closure(): \Generator)|static|array - */ - public $source; - - /** - * Create a new lazy collection instance. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $source - * - * @throws \InvalidArgumentException - */ - public function __construct($source = null) - { - if ($source instanceof Closure || $source instanceof self) { - $this->source = $source; - } elseif (is_null($source)) { - $this->source = static::empty(); - } elseif ($source instanceof Generator) { - throw new InvalidArgumentException( - 'Generators should not be passed directly to LazyCollection. Instead, pass a generator function.' - ); - } else { - $this->source = $this->getArrayableItems($source); - } - } - - /** - * Create a new instance of the collection. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $items - * @return static - */ - protected function newInstance($items = []) - { - return new static($items); - } - - /** - * Create a new collection instance if the value isn't one already. - * - * @template TMakeKey of array-key - * @template TMakeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $items - * @return static - */ - public static function make($items = [], ...$args) - { - return new static($items, ...$args); - } - - /** - * Create a collection with the given range. - * - * @param int $from - * @param int $to - * @param int $step - * @return ($step is 0 ? never : static) - * - * @throws \InvalidArgumentException - */ - public static function range($from, $to, $step = 1, ...$args) - { - if ($step == 0) { - throw new InvalidArgumentException('Step value cannot be zero.'); - } - - return new static(function () use ($from, $to, $step) { - if ($from <= $to) { - for (; $from <= $to; $from += abs($step)) { - yield $from; - } - } else { - for (; $from >= $to; $from -= abs($step)) { - yield $from; - } - } - }); - } - - /** - * Get all items in the enumerable. - * - * @return array - */ - public function all() - { - if (is_array($this->source)) { - return $this->source; - } - - return iterator_to_array($this->getIterator()); - } - - /** - * Eager load all items into a new lazy collection backed by an array. - * - * @return static - */ - public function eager() - { - return new static($this->all()); - } - - /** - * Cache values as they're enumerated. - * - * @return static - */ - public function remember() - { - $iterator = $this->getIterator(); - - $iteratorIndex = 0; - - $cache = []; - - return new static(function () use ($iterator, &$iteratorIndex, &$cache) { - for ($index = 0; true; $index++) { - if (array_key_exists($index, $cache)) { - yield $cache[$index][0] => $cache[$index][1]; - - continue; - } - - if ($iteratorIndex < $index) { - $iterator->next(); - - $iteratorIndex++; - } - - if (! $iterator->valid()) { - break; - } - - $cache[$index] = [$iterator->key(), $iterator->current()]; - - yield $cache[$index][0] => $cache[$index][1]; - } - }); - } - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return float|int|null - */ - public function median($key = null) - { - return $this->collect()->median($key); - } - - /** - * Get the mode of a given key. - * - * @param string|array|null $key - * @return array|null - */ - public function mode($key = null) - { - return $this->collect()->mode($key); - } - - /** - * Collapse the collection of items into a single array. - * - * @return static - */ - public function collapse() - { - return new static(function () { - foreach ($this as $values) { - if (is_array($values) || $values instanceof Enumerable) { - foreach ($values as $value) { - yield $value; - } - } - } - }); - } - - /** - * Collapse the collection of items into a single array while preserving its keys. - * - * @return static - */ - public function collapseWithKeys() - { - return new static(function () { - foreach ($this as $values) { - if (is_array($values) || $values instanceof Enumerable) { - foreach ($values as $key => $value) { - yield $key => $value; - } - } - } - }); - } - - /** - * Determine if an item exists in the enumerable. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function contains($key, $operator = null, $value = null) - { - if (func_num_args() === 1 && $this->useAsCallable($key)) { - $placeholder = new stdClass; - - /** @var callable $key */ - return $this->first($key, $placeholder) !== $placeholder; - } - - if (func_num_args() === 1) { - $needle = $key; - - foreach ($this as $value) { - if ($value == $needle) { - return true; - } - } - - return false; - } - - return $this->contains($this->operatorForWhere(...func_get_args())); - } - - /** - * Determine if an item exists, using strict comparison. - * - * @param (callable(TValue): bool)|TValue|array-key $key - * @param TValue|null $value - * @return bool - */ - public function containsStrict($key, $value = null) - { - if (func_num_args() === 2) { - return $this->contains(fn ($item) => data_get($item, $key) === $value); - } - - if ($this->useAsCallable($key)) { - return ! is_null($this->first($key)); - } - - foreach ($this as $item) { - if ($item === $key) { - return true; - } - } - - return false; - } - - /** - * Determine if an item is not contained in the enumerable. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function doesntContain($key, $operator = null, $value = null) - { - return ! $this->contains(...func_get_args()); - } - - /** - * Determine if an item is not contained in the enumerable, using strict comparison. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function doesntContainStrict($key, $operator = null, $value = null) - { - return ! $this->containsStrict(...func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function crossJoin(...$arrays) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function countBy($countBy = null) - { - $countBy = is_null($countBy) - ? $this->identity() - : $this->valueRetriever($countBy); - - return new static(function () use ($countBy) { - $counts = []; - - foreach ($this as $key => $value) { - $group = enum_value($countBy($value, $key)); - - if (empty($counts[$group])) { - $counts[$group] = 0; - } - - $counts[$group]++; - } - - yield from $counts; - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diff($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffAssoc($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffAssocUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffKeys($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffKeysUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function duplicates($callback = null, $strict = false) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function duplicatesStrict($callback = null) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function except($keys) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Run a filter over each of the items. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return static - */ - public function filter(?callable $callback = null) - { - if (is_null($callback)) { - $callback = fn ($value) => (bool) $value; - } - - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - if ($callback($value, $key)) { - yield $key => $value; - } - } - }); - } - - /** - * Get the first item from the enumerable passing the given truth test. - * - * @template TFirstDefault - * - * @param (callable(TValue): bool)|null $callback - * @param TFirstDefault|(\Closure(): TFirstDefault) $default - * @return TValue|TFirstDefault - */ - public function first(?callable $callback = null, $default = null) - { - $iterator = $this->getIterator(); - - if (is_null($callback)) { - if (! $iterator->valid()) { - return value($default); - } - - return $iterator->current(); - } - - foreach ($iterator as $key => $value) { - if ($callback($value, $key)) { - return $value; - } - } - - return value($default); - } - - /** - * Get a flattened list of the items in the collection. - * - * @param int $depth - * @return static - */ - public function flatten($depth = INF) - { - $instance = new static(function () use ($depth) { - foreach ($this as $item) { - if (! is_array($item) && ! $item instanceof Enumerable) { - yield $item; - } elseif ($depth === 1) { - yield from $item; - } else { - yield from (new static($item))->flatten($depth - 1); - } - } - }); - - return $instance->values(); - } - - /** - * Flip the items in the collection. - * - * @return static - */ - public function flip() - { - return new static(function () { - foreach ($this as $key => $value) { - if (is_string($value) || is_int($value)) { - yield $value => $key; - } - } - }); - } - - /** - * Get an item by key. - * - * @template TGetDefault - * - * @param TKey|null $key - * @param TGetDefault|(\Closure(): TGetDefault) $default - * @return TValue|TGetDefault - */ - public function get($key, $default = null) - { - if (is_null($key)) { - return; - } - - foreach ($this as $outerKey => $outerValue) { - if ($outerKey == $key) { - return $outerValue; - } - } - - return value($default); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function groupBy($groupBy, $preserveKeys = false) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function keyBy($keyBy) - { - return new static(function () use ($keyBy) { - $keyBy = $this->valueRetriever($keyBy); - - foreach ($this as $key => $item) { - $resolvedKey = $keyBy($item, $key); - - if ($resolvedKey instanceof \UnitEnum) { - $resolvedKey = enum_value($resolvedKey); - } - - if (is_object($resolvedKey) || is_null($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - yield $resolvedKey => $item; - } - }); - } - - /** - * Determine if an item exists in the collection by key. - * - * @param mixed $key - * @return bool - */ - public function has($key) - { - $keys = array_flip(is_array($key) ? $key : func_get_args()); - - foreach ($this as $key => $value) { - unset($keys[$key]); - - if (empty($keys)) { - return true; - } - } - - return false; - } - - /** - * Determine if any of the keys exist in the collection. - * - * @param mixed $key - * @return bool - */ - public function hasAny($key) - { - $keys = array_flip(is_array($key) ? $key : func_get_args()); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $keys)) { - return true; - } - } - - return false; - } - - /** - * Concatenate values of a given key as a string. - * - * @param (callable(TValue, TKey): mixed)|string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null) - { - return $this->collect()->implode(...func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersect($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersectUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersectAssoc($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersectAssocUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersectByKeys($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Determine if the items are empty or not. - * - * @return bool - */ - public function isEmpty() - { - return ! $this->getIterator()->valid(); - } - - /** - * Determine if the collection contains a single item. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return bool - * - * @deprecated 12.49.0 Use the `hasSole()` method instead. - */ - public function containsOneItem(?callable $callback = null): bool - { - return $this->hasSole($callback); - } - - /** - * Determine if the collection contains multiple items. - * - * @return bool - * - * @deprecated 12.50.0 Use the `hasMany()` method instead. - */ - public function containsManyItems(): bool - { - return $this->hasMany(); - } - - /** - * Join all items from the collection using a string. The final items can use a separate glue string. - * - * @param string $glue - * @param string $finalGlue - * @return string - */ - public function join($glue, $finalGlue = '') - { - return $this->collect()->join(...func_get_args()); - } - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys() - { - return new static(function () { - foreach ($this as $key => $value) { - yield $key; - } - }); - } - - /** - * Get the last item from the collection. - * - * @template TLastDefault - * - * @param (callable(TValue, TKey): bool)|null $callback - * @param TLastDefault|(\Closure(): TLastDefault) $default - * @return TValue|TLastDefault - */ - public function last(?callable $callback = null, $default = null) - { - $needle = $placeholder = new stdClass; - - foreach ($this as $key => $value) { - if (is_null($callback) || $callback($value, $key)) { - $needle = $value; - } - } - - return $needle === $placeholder ? value($default) : $needle; - } - - /** - * Get the values of a given key. - * - * @param string|array $value - * @param string|null $key - * @return static - */ - public function pluck($value, $key = null) - { - return new static(function () use ($value, $key) { - [$value, $key] = $this->explodePluckParameters($value, $key); - - foreach ($this as $item) { - $itemValue = $value instanceof Closure - ? $value($item) - : data_get($item, $value); - - if (is_null($key)) { - yield $itemValue; - } else { - $itemKey = $key instanceof Closure - ? $key($item) - : data_get($item, $key); - - if (is_object($itemKey) && method_exists($itemKey, '__toString')) { - $itemKey = (string) $itemKey; - } - - yield $itemKey => $itemValue; - } - } - }); - } - - /** - * Run a map over each of the items. - * - * @template TMapValue - * - * @param callable(TValue, TKey): TMapValue $callback - * @return static - */ - public function map(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - yield $key => $callback($value, $key); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function mapToDictionary(callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Run an associative map over each of the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapWithKeysKey of array-key - * @template TMapWithKeysValue - * - * @param callable(TValue, TKey): array $callback - * @return static - */ - public function mapWithKeys(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - yield from $callback($value, $key); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function merge($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function mergeRecursive($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Multiply the items in the collection by the multiplier. - * - * @param int $multiplier - * @return static - */ - public function multiply(int $multiplier) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @template TCombineValue - * - * @param \IteratorAggregate|array|(callable(): \Generator) $values - * @return static - */ - public function combine($values) - { - return new static(function () use ($values) { - $values = $this->makeIterator($values); - - $errorMessage = 'Both parameters should have an equal number of elements'; - - foreach ($this as $key) { - if (! $values->valid()) { - trigger_error($errorMessage, E_USER_WARNING); - - break; - } - - yield $key => $values->current(); - - $values->next(); - } - - if ($values->valid()) { - trigger_error($errorMessage, E_USER_WARNING); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function union($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return ($step is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function nth($step, $offset = 0) - { - if ($step < 1) { - throw new InvalidArgumentException('Step value must be at least 1.'); - } - - return new static(function () use ($step, $offset) { - $position = 0; - - foreach ($this->slice($offset) as $item) { - if ($position % $step === 0) { - yield $item; - } - - $position++; - } - }); - } - - /** - * Get the items with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array|string $keys - * @return static - */ - public function only($keys) - { - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_null($keys)) { - $keys = is_array($keys) ? $keys : func_get_args(); - } - - return new static(function () use ($keys) { - if (is_null($keys)) { - yield from $this; - } else { - $keys = array_flip($keys); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $keys)) { - yield $key => $value; - - unset($keys[$key]); - - if (empty($keys)) { - break; - } - } - } - } - }); - } - - /** - * Select specific values from the items within the collection. - * - * @param \Illuminate\Support\Enumerable|array|string $keys - * @return static - */ - public function select($keys) - { - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_null($keys)) { - $keys = is_array($keys) ? $keys : func_get_args(); - } - - return new static(function () use ($keys) { - if (is_null($keys)) { - yield from $this; - } else { - foreach ($this as $item) { - $result = []; - - foreach ($keys as $key) { - if (Arr::accessible($item) && Arr::exists($item, $key)) { - $result[$key] = $item[$key]; - } elseif (is_object($item) && isset($item->{$key})) { - $result[$key] = $item->{$key}; - } - } - - yield $result; - } - } - }); - } - - /** - * Push all of the given items onto the collection. - * - * @template TConcatKey of array-key - * @template TConcatValue - * - * @param iterable $source - * @return static - */ - public function concat($source) - { - return (new static(function () use ($source) { - yield from $this; - yield from $source; - }))->values(); - } - - /** - * Get one or a specified number of items randomly from the collection. - * - * @param int|null $number - * @param bool $preserveKeys - * @return ($number is null ? TValue : static) - * - * @throws \InvalidArgumentException - */ - public function random($number = null, $preserveKeys = false) - { - $result = $this->collect()->random(...func_get_args()); - - return is_null($number) ? $result : new static($result); - } - - /** - * Replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replace($items) - { - return new static(function () use ($items) { - $items = $this->getArrayableItems($items); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $items)) { - yield $key => $items[$key]; - - unset($items[$key]); - } else { - yield $key => $value; - } - } - - foreach ($items as $key => $value) { - yield $key => $value; - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function replaceRecursive($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function reverse() - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Search the collection for a given value and return the corresponding key if successful. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TKey|false - */ - public function search($value, $strict = false) - { - /** @var (callable(TValue,TKey): bool) $predicate */ - $predicate = $this->useAsCallable($value) - ? $value - : function ($item) use ($value, $strict) { - return $strict ? $item === $value : $item == $value; - }; - - foreach ($this as $key => $item) { - if ($predicate($item, $key)) { - return $key; - } - } - - return false; - } - - /** - * Get the item before the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function before($value, $strict = false) - { - $previous = null; - - /** @var (callable(TValue,TKey): bool) $predicate */ - $predicate = $this->useAsCallable($value) - ? $value - : function ($item) use ($value, $strict) { - return $strict ? $item === $value : $item == $value; - }; - - foreach ($this as $key => $item) { - if ($predicate($item, $key)) { - return $previous; - } - - $previous = $item; - } - - return null; - } - - /** - * Get the item after the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function after($value, $strict = false) - { - $found = false; - - /** @var (callable(TValue,TKey): bool) $predicate */ - $predicate = $this->useAsCallable($value) - ? $value - : function ($item) use ($value, $strict) { - return $strict ? $item === $value : $item == $value; - }; - - foreach ($this as $key => $item) { - if ($found) { - return $item; - } - - if ($predicate($item, $key)) { - $found = true; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function shuffle() - { - return $this->passthru(__FUNCTION__, []); - } - - /** - * Create chunks representing a "sliding window" view of the items in the collection. - * - * @param positive-int $size - * @param positive-int $step - * @return static - * - * @throws \InvalidArgumentException - */ - public function sliding($size = 2, $step = 1) - { - if ($size < 1) { - throw new InvalidArgumentException('Size value must be at least 1.'); - } elseif ($step < 1) { - throw new InvalidArgumentException('Step value must be at least 1.'); - } - - return new static(function () use ($size, $step) { - $iterator = $this->getIterator(); - - $chunk = []; - - while ($iterator->valid()) { - $chunk[$iterator->key()] = $iterator->current(); - - if (count($chunk) == $size) { - yield (new static($chunk))->tap(function () use (&$chunk, $step) { - $chunk = array_slice($chunk, $step, null, true); - }); - - // If the $step between chunks is bigger than each chunk's $size - // we will skip the extra items (which should never be in any - // chunk) before we continue to the next chunk in the loop. - if ($step > $size) { - $skip = $step - $size; - - for ($i = 0; $i < $skip && $iterator->valid(); $i++) { - $iterator->next(); - } - } - } - - $iterator->next(); - } - }); - } - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($count) - { - return new static(function () use ($count) { - $iterator = $this->getIterator(); - - while ($iterator->valid() && $count--) { - $iterator->next(); - } - - while ($iterator->valid()) { - yield $iterator->key() => $iterator->current(); - - $iterator->next(); - } - }); - } - - /** - * Skip items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipUntil($value) - { - $callback = $this->useAsCallable($value) ? $value : $this->equality($value); - - return $this->skipWhile($this->negate($callback)); - } - - /** - * Skip items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipWhile($value) - { - $callback = $this->useAsCallable($value) ? $value : $this->equality($value); - - return new static(function () use ($callback) { - $iterator = $this->getIterator(); - - while ($iterator->valid() && $callback($iterator->current(), $iterator->key())) { - $iterator->next(); - } - - while ($iterator->valid()) { - yield $iterator->key() => $iterator->current(); - - $iterator->next(); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function slice($offset, $length = null) - { - if ($offset < 0 || $length < 0) { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - $instance = $this->skip($offset); - - return is_null($length) ? $instance : $instance->take($length); - } - - /** - * {@inheritDoc} - * - * @throws \InvalidArgumentException - */ - #[\Override] - public function split($numberOfGroups) - { - if ($numberOfGroups < 1) { - throw new InvalidArgumentException('Number of groups must be at least 1.'); - } - - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return TValue - * - * @throws \Illuminate\Support\ItemNotFoundException - * @throws \Illuminate\Support\MultipleItemsFoundException - */ - public function sole($key = null, $operator = null, $value = null) - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - return $this - ->unless($filter == null) - ->filter($filter) - ->take(2) - ->collect() - ->sole(); - } - - /** - * Determine if the collection contains a single item or a single item matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasSole($key = null, $operator = null, $value = null): bool - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - return $this - ->unless($filter == null) - ->filter($filter) - ->take(2) - ->count() === 1; - } - - /** - * Get the first item in the collection but throw an exception if no matching items exist. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return TValue - * - * @throws \Illuminate\Support\ItemNotFoundException - */ - public function firstOrFail($key = null, $operator = null, $value = null) - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - return $this - ->unless($filter == null) - ->filter($filter) - ->take(1) - ->collect() - ->firstOrFail(); - } - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @param bool $preserveKeys - * @return ($preserveKeys is true ? static : static>) - */ - public function chunk($size, $preserveKeys = true) - { - if ($size <= 0) { - return static::empty(); - } - - $add = match ($preserveKeys) { - true => fn (array &$chunk, Traversable $iterator) => $chunk[$iterator->key()] = $iterator->current(), - false => fn (array &$chunk, Traversable $iterator) => $chunk[] = $iterator->current(), - }; - - return new static(function () use ($size, $add) { - $iterator = $this->getIterator(); - - while ($iterator->valid()) { - $chunk = []; - - while (true) { - $add($chunk, $iterator); - - if (count($chunk) < $size) { - $iterator->next(); - - if (! $iterator->valid()) { - break; - } - } else { - break; - } - } - - yield new static($chunk); - - $iterator->next(); - } - }); - } - - /** - * Split a collection into a certain number of groups, and fill the first groups completely. - * - * @param int $numberOfGroups - * @return ($numberOfGroups is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function splitIn($numberOfGroups) - { - if ($numberOfGroups < 1) { - throw new InvalidArgumentException('Number of groups must be at least 1.'); - } - - return $this->chunk((int) ceil($this->count() / $numberOfGroups)); - } - - /** - * Chunk the collection into chunks with a callback. - * - * @param callable(TValue, TKey, Collection): bool $callback - * @return static> - */ - public function chunkWhile(callable $callback) - { - return new static(function () use ($callback) { - $iterator = $this->getIterator(); - - $chunk = new Collection; - - if ($iterator->valid()) { - $chunk[$iterator->key()] = $iterator->current(); - - $iterator->next(); - } - - while ($iterator->valid()) { - if (! $callback($iterator->current(), $iterator->key(), $chunk)) { - yield new static($chunk); - - $chunk = new Collection; - } - - $chunk[$iterator->key()] = $iterator->current(); - - $iterator->next(); - } - - if ($chunk->isNotEmpty()) { - yield new static($chunk); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sort($callback = null) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortDesc($options = SORT_REGULAR) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortBy($callback, $options = SORT_REGULAR, $descending = false) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortByDesc($callback, $options = SORT_REGULAR) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortKeys($options = SORT_REGULAR, $descending = false) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortKeysDesc($options = SORT_REGULAR) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortKeysUsing(callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit) - { - if ($limit < 0) { - return new static(function () use ($limit) { - $limit = abs($limit); - $ringBuffer = []; - $position = 0; - - foreach ($this as $key => $value) { - $ringBuffer[$position] = [$key, $value]; - $position = ($position + 1) % $limit; - } - - for ($i = 0, $end = min($limit, count($ringBuffer)); $i < $end; $i++) { - $pointer = ($position + $i) % $limit; - yield $ringBuffer[$pointer][0] => $ringBuffer[$pointer][1]; - } - }); - } - - return new static(function () use ($limit) { - $iterator = $this->getIterator(); - - while ($limit--) { - if (! $iterator->valid()) { - break; - } - - yield $iterator->key() => $iterator->current(); - - if ($limit) { - $iterator->next(); - } - } - }); - } - - /** - * Take items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeUntil($value) - { - /** @var callable(TValue, TKey): bool $callback */ - $callback = $this->useAsCallable($value) ? $value : $this->equality($value); - - return new static(function () use ($callback) { - foreach ($this as $key => $item) { - if ($callback($item, $key)) { - break; - } - - yield $key => $item; - } - }); - } - - /** - * Take items in the collection until a given point in time, with an optional callback on timeout. - * - * @param \DateTimeInterface $timeout - * @param callable(TValue|null, TKey|null): mixed|null $callback - * @return static - */ - public function takeUntilTimeout(DateTimeInterface $timeout, ?callable $callback = null) - { - $timeout = $timeout->getTimestamp(); - - return new static(function () use ($timeout, $callback) { - if ($this->now() >= $timeout) { - if ($callback) { - $callback(null, null); - } - - return; - } - - foreach ($this as $key => $value) { - yield $key => $value; - - if ($this->now() >= $timeout) { - if ($callback) { - $callback($value, $key); - } - - break; - } - } - }); - } - - /** - * Take items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeWhile($value) - { - /** @var callable(TValue, TKey): bool $callback */ - $callback = $this->useAsCallable($value) ? $value : $this->equality($value); - - return $this->takeUntil(fn ($item, $key) => ! $callback($item, $key)); - } - - /** - * Pass each item in the collection to the given callback, lazily. - * - * @param callable(TValue, TKey): mixed $callback - * @return static - */ - public function tapEach(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - $callback($value, $key); - - yield $key => $value; - } - }); - } - - /** - * Throttle the values, releasing them at most once per the given seconds. - * - * @return static - */ - public function throttle(float $seconds) - { - return new static(function () use ($seconds) { - $microseconds = $seconds * 1_000_000; - - foreach ($this as $key => $value) { - $fetchedAt = $this->preciseNow(); - - yield $key => $value; - - $sleep = $microseconds - ($this->preciseNow() - $fetchedAt); - - $this->usleep((int) $sleep); - } - }); - } - - /** - * Flatten a multi-dimensional associative array with dots. - * - * @param int $depth - * @return static - */ - public function dot($depth = INF) - { - return $this->passthru(__FUNCTION__, [$depth]); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function undot() - { - return $this->passthru(__FUNCTION__, []); - } - - /** - * Return only unique items from the collection array. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false) - { - $callback = $this->valueRetriever($key); - - return new static(function () use ($callback, $strict) { - $exists = []; - - foreach ($this as $key => $item) { - if (! in_array($id = $callback($item, $key), $exists, $strict)) { - yield $key => $item; - - $exists[] = $id; - } - } - }); - } - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values() - { - return new static(function () { - foreach ($this as $item) { - yield $item; - } - }); - } - - /** - * Run the given callback every time the interval has passed. - * - * @return static - */ - public function withHeartbeat(DateInterval|int $interval, callable $callback) - { - $seconds = is_int($interval) ? $interval : $this->intervalSeconds($interval); - - return new static(function () use ($seconds, $callback) { - $start = $this->now(); - - foreach ($this as $key => $value) { - $now = $this->now(); - - if (($now - $start) >= $seconds) { - $callback(); - - $start = $now; - } - - yield $key => $value; - } - }); - } - - /** - * Get the total seconds from the given interval. - */ - protected function intervalSeconds(DateInterval $interval): int - { - $start = new DateTimeImmutable(); - - return $start->add($interval)->getTimestamp() - $start->getTimestamp(); - } - - /** - * Zip the collection together with one or more arrays. - * - * e.g. new LazyCollection([1, 2, 3])->zip([4, 5, 6]); - * => [[1, 4], [2, 5], [3, 6]] - * - * @template TZipValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items - * @return static> - */ - public function zip($items) - { - $iterables = func_get_args(); - - return new static(function () use ($iterables) { - $iterators = (new Collection($iterables)) - ->map(fn ($iterable) => $this->makeIterator($iterable)) - ->prepend($this->getIterator()); - - while ($iterators->contains->valid()) { - yield new static($iterators->map->current()); - - $iterators->each->next(); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function pad($size, $value) - { - if ($size < 0) { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - return new static(function () use ($size, $value) { - $yielded = 0; - - foreach ($this as $index => $item) { - yield $index => $item; - - $yielded++; - } - - while ($yielded++ < $size) { - yield $value; - } - }); - } - - /** - * Get the values iterator. - * - * @return \Traversable - */ - public function getIterator(): Traversable - { - return $this->makeIterator($this->source); - } - - /** - * Count the number of items in the collection. - * - * @return int - */ - public function count(): int - { - if (is_array($this->source)) { - return count($this->source); - } - - return iterator_count($this->getIterator()); - } - - /** - * Make an iterator from the given source. - * - * @template TIteratorKey of array-key - * @template TIteratorValue - * - * @param \IteratorAggregate|array|(callable(): \Generator) $source - * @return \Traversable - */ - protected function makeIterator($source) - { - if ($source instanceof IteratorAggregate) { - return $source->getIterator(); - } - - if (is_array($source)) { - return new ArrayIterator($source); - } - - if (is_callable($source)) { - $maybeTraversable = $source(); - - return $maybeTraversable instanceof Traversable - ? $maybeTraversable - : new ArrayIterator(Arr::wrap($maybeTraversable)); - } - - return new ArrayIterator((array) $source); - } - - /** - * Explode the "value" and "key" arguments passed to "pluck". - * - * @param string|string[] $value - * @param string|string[]|null $key - * @return array{string[],string[]|null} - */ - protected function explodePluckParameters($value, $key) - { - $value = is_string($value) ? explode('.', $value) : $value; - - $key = is_null($key) || is_array($key) || $key instanceof Closure ? $key : explode('.', $key); - - return [$value, $key]; - } - - /** - * Pass this lazy collection through a method on the collection class. - * - * @param string $method - * @param array $params - * @return static - */ - protected function passthru($method, array $params) - { - return new static(function () use ($method, $params) { - yield from $this->collect()->$method(...$params); - }); - } - - /** - * Get the current time. - * - * @return int - */ - protected function now() - { - return class_exists(Carbon::class) - ? Carbon::now()->getTimestamp() - : time(); - } - - /** - * Get the precise current time. - * - * @return float - */ - protected function preciseNow() - { - return class_exists(Carbon::class) - ? Carbon::now()->getPreciseTimestamp() - : microtime(true) * 1_000_000; - } - - /** - * Sleep for the given amount of microseconds. - * - * @return void - */ - protected function usleep(int $microseconds) - { - if ($microseconds <= 0) { - return; - } - - class_exists(Sleep::class) - ? Sleep::usleep($microseconds) - : usleep($microseconds); - } -} diff --git a/src/Illuminate/Support/MultipleItemsFoundException.php b/src/Illuminate/Support/MultipleItemsFoundException.php deleted file mode 100644 index 9c5c7c56..00000000 --- a/src/Illuminate/Support/MultipleItemsFoundException.php +++ /dev/null @@ -1,39 +0,0 @@ -count = $count; - - parent::__construct("$count items were found.", $code, $previous); - } - - /** - * Get the number of items found. - * - * @return int - */ - public function getCount() - { - return $this->count; - } -} diff --git a/src/Illuminate/Support/Reflector.php b/src/Illuminate/Support/Reflector.php deleted file mode 100644 index e96f41ed..00000000 --- a/src/Illuminate/Support/Reflector.php +++ /dev/null @@ -1,40 +0,0 @@ -getType(); - - if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) { - return null; - } - - $name = $type->getName(); - - if (! is_null($class = $parameter->getDeclaringClass())) { - if ($name === 'self') { - return $class->getName(); - } - - if ($name === 'parent' && $parent = $class->getParentClass()) { - return $parent->getName(); - } - } - - return $name; - } -} \ No newline at end of file diff --git a/src/Illuminate/Support/Traits/Conditionable.php b/src/Illuminate/Support/Traits/Conditionable.php deleted file mode 100644 index 5e3194bb..00000000 --- a/src/Illuminate/Support/Traits/Conditionable.php +++ /dev/null @@ -1,73 +0,0 @@ -condition($value); - } - - if ($value) { - return $callback($this, $value) ?? $this; - } elseif ($default) { - return $default($this, $value) ?? $this; - } - - return $this; - } - - /** - * Apply the callback if the given "value" is (or resolves to) falsy. - * - * @template TUnlessParameter - * @template TUnlessReturnType - * - * @param (\Closure($this): TUnlessParameter)|TUnlessParameter|null $value - * @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $callback - * @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $default - * @return $this|TUnlessReturnType - */ - public function unless($value = null, ?callable $callback = null, ?callable $default = null) - { - $value = $value instanceof Closure ? $value($this) : $value; - - if (func_num_args() === 0) { - return (new HigherOrderWhenProxy($this))->negateConditionOnCapture(); - } - - if (func_num_args() === 1) { - return (new HigherOrderWhenProxy($this))->condition(! $value); - } - - if (! $value) { - return $callback($this, $value) ?? $this; - } elseif ($default) { - return $default($this, $value) ?? $this; - } - - return $this; - } -} diff --git a/src/Illuminate/Support/Traits/EnumeratesValues.php b/src/Illuminate/Support/Traits/EnumeratesValues.php deleted file mode 100644 index fbd7c90d..00000000 --- a/src/Illuminate/Support/Traits/EnumeratesValues.php +++ /dev/null @@ -1,1244 +0,0 @@ - $average - * @property-read HigherOrderCollectionProxy $avg - * @property-read HigherOrderCollectionProxy $contains - * @property-read HigherOrderCollectionProxy $doesntContain - * @property-read HigherOrderCollectionProxy $each - * @property-read HigherOrderCollectionProxy $every - * @property-read HigherOrderCollectionProxy $filter - * @property-read HigherOrderCollectionProxy $first - * @property-read HigherOrderCollectionProxy $flatMap - * @property-read HigherOrderCollectionProxy $groupBy - * @property-read HigherOrderCollectionProxy $hasMany - * @property-read HigherOrderCollectionProxy $hasSole - * @property-read HigherOrderCollectionProxy $keyBy - * @property-read HigherOrderCollectionProxy $last - * @property-read HigherOrderCollectionProxy $map - * @property-read HigherOrderCollectionProxy $max - * @property-read HigherOrderCollectionProxy $min - * @property-read HigherOrderCollectionProxy $partition - * @property-read HigherOrderCollectionProxy $percentage - * @property-read HigherOrderCollectionProxy $reject - * @property-read HigherOrderCollectionProxy $skipUntil - * @property-read HigherOrderCollectionProxy $skipWhile - * @property-read HigherOrderCollectionProxy $some - * @property-read HigherOrderCollectionProxy $sortBy - * @property-read HigherOrderCollectionProxy $sortByDesc - * @property-read HigherOrderCollectionProxy $sum - * @property-read HigherOrderCollectionProxy $takeUntil - * @property-read HigherOrderCollectionProxy $takeWhile - * @property-read HigherOrderCollectionProxy $unique - * @property-read HigherOrderCollectionProxy $unless - * @property-read HigherOrderCollectionProxy $until - * @property-read HigherOrderCollectionProxy $when - */ -trait EnumeratesValues -{ - use Conditionable; - - /** - * Indicates that the object's string representation should be escaped when __toString is invoked. - * - * @var bool - */ - protected $escapeWhenCastingToString = false; - - /** - * The methods that can be proxied. - * - * @var array - */ - protected static $proxies = [ - 'average', - 'avg', - 'contains', - 'doesntContain', - 'each', - 'every', - 'filter', - 'first', - 'flatMap', - 'groupBy', - 'hasMany', - 'hasSole', - 'keyBy', - 'last', - 'map', - 'max', - 'min', - 'partition', - 'percentage', - 'reject', - 'skipUntil', - 'skipWhile', - 'some', - 'sortBy', - 'sortByDesc', - 'sum', - 'takeUntil', - 'takeWhile', - 'unique', - 'unless', - 'until', - 'when', - ]; - - /** - * Create a new collection instance if the value isn't one already. - * - * @template TMakeKey of array-key - * @template TMakeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items - * @return static - */ - public static function make($items = [], ...$args) - { - return new static($items, ...$args); - } - - /** - * Wrap the given value in a collection if applicable. - * - * @template TWrapValue - * - * @param iterable|TWrapValue $value - * @return static - */ - public static function wrap($value, ...$args) - { - return $value instanceof Enumerable - ? new static($value, ...$args) - : new static(Arr::wrap($value), ...$args); - } - - /** - * Get the underlying items from the given collection if applicable. - * - * @template TUnwrapKey of array-key - * @template TUnwrapValue - * - * @param array|static $value - * @return array - */ - public static function unwrap($value) - { - return $value instanceof Enumerable ? $value->all() : $value; - } - - /** - * Create a new instance with no items. - * - * @return static - */ - public static function empty(...$args) - { - return new static([], ...$args); - } - - /** - * Create a new collection by invoking the callback a given amount of times. - * - * @template TTimesValue - * - * @param int $number - * @param (callable(int): TTimesValue)|null $callback - * @return static - */ - public static function times($number, ?callable $callback = null, ...$args) - { - if ($number < 1) { - return new static([], ...$args); - } - - return static::range(1, $number, 1, ...$args) - ->unless($callback == null) - ->map($callback); - } - - /** - * Create a new collection by decoding a JSON string. - * - * @param string $json - * @param int $depth - * @param int $flags - * @return static - */ - public static function fromJson($json, $depth = 512, $flags = 0, ...$args) - { - return new static(json_decode($json, true, $depth, $flags), ...$args); - } - - /** - * Get the average value of a given key. - * - * @param (callable(TValue): float|int)|string|null $callback - * @return float|int|null - */ - public function avg($callback = null) - { - $callback = $this->valueRetriever($callback); - - $reduced = $this->reduce(static function (&$reduce, $value) use ($callback) { - if (! is_null($resolved = $callback($value))) { - $reduce[0] += $resolved; - $reduce[1]++; - } - - return $reduce; - }, [0, 0]); - - return $reduced[1] ? $reduced[0] / $reduced[1] : null; - } - - /** - * Alias for the "avg" method. - * - * @param (callable(TValue): float|int)|string|null $callback - * @return float|int|null - */ - public function average($callback = null) - { - return $this->avg($callback); - } - - /** - * Alias for the "contains" method. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function some($key, $operator = null, $value = null) - { - return $this->contains(...func_get_args()); - } - - /** - * Dump the given arguments and terminate execution. - * - * @param mixed ...$args - * @return never - */ - public function dd(...$args) - { - dd($this->all(), ...$args); - } - - /** - * Dump the items. - * - * @param mixed ...$args - * @return $this - */ - public function dump(...$args) - { - dump($this->all(), ...$args); - - return $this; - } - - /** - * Execute a callback over each item. - * - * @param callable(TValue, TKey): mixed $callback - * @return $this - */ - public function each(callable $callback) - { - foreach ($this as $key => $item) { - if ($callback($item, $key) === false) { - break; - } - } - - return $this; - } - - /** - * Execute a callback over each nested chunk of items. - * - * @param callable(...mixed): mixed $callback - * @return static - */ - public function eachSpread(callable $callback) - { - return $this->each(function ($chunk, $key) use ($callback) { - $chunk[] = $key; - - return $callback(...$chunk); - }); - } - - /** - * Determine if all items pass the given truth test. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function every($key, $operator = null, $value = null) - { - if (func_num_args() === 1) { - $callback = $this->valueRetriever($key); - - foreach ($this as $k => $v) { - if (! $callback($v, $k)) { - return false; - } - } - - return true; - } - - return $this->every($this->operatorForWhere(...func_get_args())); - } - - /** - * Get the first item by the given key value pair. - * - * @param callable|string $key - * @param mixed $operator - * @param mixed $value - * @return TValue|null - */ - public function firstWhere($key, $operator = null, $value = null) - { - return $this->first($this->operatorForWhere(...func_get_args())); - } - - /** - * Determine if the collection contains multiple items, optionally matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasMany($key = null, $operator = null, $value = null): bool - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - return $this - ->unless($filter == null) - ->filter($filter) - ->take(2) - ->count() === 2; - } - - /** - * Get a single key's value from the first matching item in the collection. - * - * @template TValueDefault - * - * @param string $key - * @param TValueDefault|(\Closure(): TValueDefault) $default - * @return TValue|TValueDefault - */ - public function value($key, $default = null) - { - $value = $this->first(function ($target) use ($key) { - return data_has($target, $key); - }); - - return data_get($value, $key, $default); - } - - /** - * Ensure that every item in the collection is of the expected type. - * - * @template TEnsureOfType - * - * @param class-string|array>|'string'|'int'|'float'|'bool'|'array'|'null' $type - * @return static - * - * @throws \UnexpectedValueException - */ - public function ensure($type) - { - $allowedTypes = is_array($type) ? $type : [$type]; - - return $this->each(function ($item, $index) use ($allowedTypes) { - $itemType = get_debug_type($item); - - foreach ($allowedTypes as $allowedType) { - if ($itemType === $allowedType || $item instanceof $allowedType) { - return true; - } - } - - throw new UnexpectedValueException( - sprintf("Collection should only include [%s] items, but '%s' found at position %d.", implode(', ', $allowedTypes), $itemType, $index) - ); - }); - } - - /** - * Determine if the collection is not empty. - * - * @phpstan-assert-if-true TValue $this->first() - * @phpstan-assert-if-true TValue $this->last() - * - * @phpstan-assert-if-false null $this->first() - * @phpstan-assert-if-false null $this->last() - * - * @return bool - */ - public function isNotEmpty() - { - return ! $this->isEmpty(); - } - - /** - * Run a map over each nested chunk of items. - * - * @template TMapSpreadValue - * - * @param callable(mixed...): TMapSpreadValue $callback - * @return static - */ - public function mapSpread(callable $callback) - { - return $this->map(function ($chunk, $key) use ($callback) { - $chunk[] = $key; - - return $callback(...$chunk); - }); - } - - /** - * Run a grouping map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapToGroupsKey of array-key - * @template TMapToGroupsValue - * - * @param callable(TValue, TKey): array $callback - * @return static> - */ - public function mapToGroups(callable $callback) - { - $groups = $this->mapToDictionary($callback); - - return $groups->map($this->make(...)); - } - - /** - * Map a collection and flatten the result by a single level. - * - * @template TFlatMapKey of array-key - * @template TFlatMapValue - * - * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback - * @return static - */ - public function flatMap(callable $callback) - { - return $this->map($callback)->collapse(); - } - - /** - * Map the values into a new class. - * - * @template TMapIntoValue - * - * @param class-string $class - * @return static - */ - public function mapInto($class) - { - if (is_subclass_of($class, BackedEnum::class)) { - return $this->map(fn ($value, $key) => $class::from($value)); - } - - return $this->map(fn ($value, $key) => new $class($value, $key)); - } - - /** - * Get the min value of a given key. - * - * @template TMinResult = mixed - * - * @param (callable(TValue): TMinResult)|string|null $callback - * @return ($callback is callable ? ?TMinResult : ($callback is null ? ?TValue : mixed)) - */ - public function min($callback = null) - { - $callback = $this->valueRetriever($callback); - - return $this->map(fn ($value) => $callback($value)) - ->reject(fn ($value) => is_null($value)) - ->reduce(fn ($result, $value) => is_null($result) || $value < $result ? $value : $result); - } - - /** - * Get the max value of a given key. - * - * @template TMaxResult = mixed - * - * @param (callable(TValue): TMaxResult)|string|null $callback - * @return ($callback is callable ? ?TMaxResult : ($callback is null ? ?TValue : mixed)) - */ - public function max($callback = null) - { - $callback = $this->valueRetriever($callback); - - return $this->reject(fn ($value) => is_null($value))->reduce(function ($result, $item) use ($callback) { - $value = $callback($item); - - return is_null($result) || $value > $result ? $value : $result; - }); - } - - /** - * "Paginate" the collection by slicing it into a smaller collection. - * - * @param int $page - * @param int $perPage - * @return static - */ - public function forPage($page, $perPage) - { - $offset = max(0, ($page - 1) * $perPage); - - return $this->slice($offset, $perPage); - } - - /** - * Partition the collection into two arrays using the given callback or key. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return static, static> - */ - public function partition($key, $operator = null, $value = null) - { - $callback = func_num_args() === 1 - ? $this->valueRetriever($key) - : $this->operatorForWhere(...func_get_args()); - - [$passed, $failed] = Arr::partition($this->getIterator(), $callback); - - return $this->newInstance([$this->newInstance($passed), $this->newInstance($failed)]); - } - - /** - * Calculate the percentage of items that pass a given truth test. - * - * @param (callable(TValue, TKey): bool) $callback - * @param int $precision - * @return float|null - */ - public function percentage(callable $callback, int $precision = 2) - { - if ($this->isEmpty()) { - return null; - } - - return round( - $this->filter($callback)->count() / $this->count() * 100, - $precision - ); - } - - /** - * Get the sum of the given values. - * - * @template TReturnType - * - * @param (callable(TValue, TKey): TReturnType)|string|null $callback - * @return ($callback is callable ? TReturnType : mixed) - */ - public function sum($callback = null) - { - $callback = is_null($callback) - ? $this->identity() - : $this->valueRetriever($callback); - - return $this->reduce(fn ($result, $item, $key) => $result + $callback($item, $key), 0); - } - - /** - * Apply the callback if the collection is empty. - * - * @template TWhenEmptyReturnType - * - * @param (callable($this): TWhenEmptyReturnType) $callback - * @param (callable($this): TWhenEmptyReturnType)|null $default - * @return $this|TWhenEmptyReturnType - */ - public function whenEmpty(callable $callback, ?callable $default = null) - { - return $this->when($this->isEmpty(), $callback, $default); - } - - /** - * Apply the callback if the collection is not empty. - * - * @template TWhenNotEmptyReturnType - * - * @param callable($this): TWhenNotEmptyReturnType $callback - * @param (callable($this): TWhenNotEmptyReturnType)|null $default - * @return $this|TWhenNotEmptyReturnType - */ - public function whenNotEmpty(callable $callback, ?callable $default = null) - { - return $this->when($this->isNotEmpty(), $callback, $default); - } - - /** - * Apply the callback unless the collection is empty. - * - * @template TUnlessEmptyReturnType - * - * @param callable($this): TUnlessEmptyReturnType $callback - * @param (callable($this): TUnlessEmptyReturnType)|null $default - * @return $this|TUnlessEmptyReturnType - */ - public function unlessEmpty(callable $callback, ?callable $default = null) - { - return $this->whenNotEmpty($callback, $default); - } - - /** - * Apply the callback unless the collection is not empty. - * - * @template TUnlessNotEmptyReturnType - * - * @param callable($this): TUnlessNotEmptyReturnType $callback - * @param (callable($this): TUnlessNotEmptyReturnType)|null $default - * @return $this|TUnlessNotEmptyReturnType - */ - public function unlessNotEmpty(callable $callback, ?callable $default = null) - { - return $this->whenEmpty($callback, $default); - } - - /** - * Filter items by the given key value pair. - * - * @param callable|string $key - * @param mixed $operator - * @param mixed $value - * @return static - */ - public function where($key, $operator = null, $value = null) - { - return $this->filter($this->operatorForWhere(...func_get_args())); - } - - /** - * Filter items where the value for the given key is null. - * - * @param string|null $key - * @return static - */ - public function whereNull($key = null) - { - return $this->whereStrict($key, null); - } - - /** - * Filter items where the value for the given key is not null. - * - * @param string|null $key - * @return static - */ - public function whereNotNull($key = null) - { - return $this->where($key, '!==', null); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param mixed $value - * @return static - */ - public function whereStrict($key, $value) - { - return $this->where($key, '===', $value); - } - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @param bool $strict - * @return static - */ - public function whereIn($key, $values, $strict = false) - { - $values = $this->getArrayableItems($values); - - return $this->filter(fn ($item) => in_array(data_get($item, $key), $values, $strict)); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereInStrict($key, $values) - { - return $this->whereIn($key, $values, true); - } - - /** - * Filter items such that the value of the given key is between the given values. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereBetween($key, $values) - { - return $this->where($key, '>=', reset($values))->where($key, '<=', end($values)); - } - - /** - * Filter items such that the value of the given key is not between the given values. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereNotBetween($key, $values) - { - return $this->filter( - fn ($item) => data_get($item, $key) < reset($values) || data_get($item, $key) > end($values) - ); - } - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @param bool $strict - * @return static - */ - public function whereNotIn($key, $values, $strict = false) - { - $values = $this->getArrayableItems($values); - - return $this->reject(fn ($item) => in_array(data_get($item, $key), $values, $strict)); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereNotInStrict($key, $values) - { - return $this->whereNotIn($key, $values, true); - } - - /** - * Filter the items, removing any items that don't match the given type(s). - * - * @template TWhereInstanceOf - * - * @param class-string|array> $type - * @return static - */ - public function whereInstanceOf($type) - { - return $this->filter(function ($value) use ($type) { - if (is_array($type)) { - return array_any($type, fn ($classType) => $value instanceof $classType); - } - - return $value instanceof $type; - }); - } - - /** - * Pass the collection to the given callback and return the result. - * - * @template TPipeReturnType - * - * @param callable($this): TPipeReturnType $callback - * @return TPipeReturnType - */ - public function pipe(callable $callback) - { - return $callback($this); - } - - /** - * Pass the collection into a new class. - * - * @template TPipeIntoValue - * - * @param class-string $class - * @return TPipeIntoValue - */ - public function pipeInto($class) - { - return new $class($this); - } - - /** - * Pass the collection through a series of callable pipes and return the result. - * - * @param array $callbacks - * @return mixed - */ - public function pipeThrough($callbacks) - { - return (new Collection($callbacks))->reduce( - fn ($carry, $callback) => $callback($carry), - $this, - ); - } - - /** - * Reduce the collection to a single value. - * - * @template TReduceInitial - * @template TReduceReturnType - * - * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback - * @param TReduceInitial $initial - * @return TReduceInitial|TReduceReturnType - */ - public function reduce(callable $callback, $initial = null) - { - $result = $initial; - - foreach ($this as $key => $value) { - $result = $callback($result, $value, $key); - } - - return $result; - } - - /** - * Reduce the collection to a single value by mutating an initial value. - * - * @template TReduceIntoInitial - * - * @param TReduceIntoInitial $initial - * @param callable(TReduceIntoInitial, TValue, TKey): void $callback - * @return TReduceIntoInitial - */ - public function reduceInto($initial, callable $callback) - { - foreach ($this as $key => $value) { - $callback($initial, $value, $key); - } - - return $initial; - } - - /** - * Reduce the collection to multiple aggregate values. - * - * @param callable $callback - * @param mixed ...$initial - * @return array - * - * @throws \UnexpectedValueException - */ - public function reduceSpread(callable $callback, ...$initial) - { - $result = $initial; - - foreach ($this as $key => $value) { - $result = call_user_func_array($callback, array_merge($result, [$value, $key])); - - if (! is_array($result)) { - throw new UnexpectedValueException(sprintf( - "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.", - class_basename(static::class), gettype($result) - )); - } - } - - return $result; - } - - /** - * Reduce an associative collection to a single value. - * - * @template TReduceWithKeysInitial - * @template TReduceWithKeysReturnType - * - * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue, TKey): TReduceWithKeysReturnType $callback - * @param TReduceWithKeysInitial $initial - * @return TReduceWithKeysInitial|TReduceWithKeysReturnType - */ - public function reduceWithKeys(callable $callback, $initial = null) - { - return $this->reduce($callback, $initial); - } - - /** - * Create a collection of all elements that do not pass a given truth test. - * - * @param (callable(TValue, TKey): bool)|bool|TValue $callback - * @return static - */ - public function reject($callback = true) - { - $useAsCallable = $this->useAsCallable($callback); - - return $this->filter(function ($value, $key) use ($callback, $useAsCallable) { - return $useAsCallable - ? ! $callback($value, $key) - : $value != $callback; - }); - } - - /** - * Chunk the collection into chunks by comparing adjacent values using the given key or callback. - * - * @param (callable(TValue, TKey): mixed)|string $key - * @return static> - */ - public function chunkBy($key) - { - $callback = $this->valueRetriever($key); - - return $this->chunkWhile( - fn ($value, $key, $chunk) => $callback($value, $key) == $callback($chunk->last(), $chunk->keys()->last()) - ); - } - - /** - * Pass the collection to the given callback and then return it. - * - * @param callable($this): mixed $callback - * @return $this - */ - public function tap(callable $callback) - { - $callback($this); - - return $this; - } - - /** - * Return only unique items from the collection array. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false) - { - $callback = $this->valueRetriever($key); - - $exists = []; - - return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { - if (in_array($id = $callback($item, $key), $exists, $strict)) { - return true; - } - - $exists[] = $id; - }); - } - - /** - * Return only unique items from the collection array using strict comparison. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @return static - */ - public function uniqueStrict($key = null) - { - return $this->unique($key, true); - } - - /** - * Collect the values into a collection. - * - * @return \Illuminate\Support\Collection - */ - public function collect() - { - return new Collection($this->all()); - } - - /** - * Get the collection of items as a plain array. - * - * @return array - */ - public function toArray() - { - return $this->map(fn ($value) => $value instanceof Arrayable ? $value->toArray() : $value)->all(); - } - - /** - * Convert the object into something JSON serializable. - * - * @return array - */ - public function jsonSerialize(): array - { - return array_map(function ($value) { - return match (true) { - $value instanceof JsonSerializable => $value->jsonSerialize(), - $value instanceof Jsonable => json_decode($value->toJson(), true), - $value instanceof Arrayable => $value->toArray(), - default => $value, - }; - }, $this->all()); - } - - /** - * Get the collection of items as JSON. - * - * @param int $options - * @return string - */ - public function toJson($options = 0) - { - return json_encode($this->jsonSerialize(), $options); - } - - /** - * Get the collection of items as pretty print formatted JSON. - * - * @param int $options - * @return string - */ - public function toPrettyJson(int $options = 0) - { - return $this->toJson(JSON_PRETTY_PRINT | $options); - } - - /** - * Get a CachingIterator instance. - * - * @param int $flags - * @return \CachingIterator - */ - public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING) - { - return new CachingIterator($this->getIterator(), $flags); - } - - /** - * Convert the collection to its string representation. - * - * @return string - */ - public function __toString() - { - return $this->escapeWhenCastingToString - ? e($this->toJson()) - : $this->toJson(); - } - - /** - * Indicate that the model's string representation should be escaped when __toString is invoked. - * - * @param bool $escape - * @return $this - */ - public function escapeWhenCastingToString($escape = true) - { - $this->escapeWhenCastingToString = $escape; - - return $this; - } - - /** - * Add a method to the list of proxied methods. - * - * @param string $method - * @return void - */ - public static function proxy($method) - { - static::$proxies[] = $method; - } - - /** - * Dynamically access collection proxies. - * - * @param string $key - * @return mixed - * - * @throws \Exception - */ - public function __get($key) - { - if (! in_array($key, static::$proxies)) { - throw new Exception("Property [{$key}] does not exist on this collection instance."); - } - - return new HigherOrderCollectionProxy($this, $key); - } - - /** - * Results array of items from Collection or Arrayable. - * - * @param mixed $items - * @return array - */ - protected function getArrayableItems($items) - { - return is_null($items) || is_scalar($items) || $items instanceof UnitEnum - ? Arr::wrap($items) - : Arr::from($items); - } - - /** - * Get an operator checker callback. - * - * @param callable|string $key - * @param string|null $operator - * @param mixed $value - * @return \Closure - */ - protected function operatorForWhere($key, $operator = null, $value = null) - { - if ($this->useAsCallable($key)) { - return $key; - } - - if (func_num_args() === 1) { - $value = true; - - $operator = '='; - } - - if (func_num_args() === 2) { - $value = $operator; - - $operator = '='; - } - - return function ($item) use ($key, $operator, $value) { - $retrieved = enum_value(data_get($item, $key)); - $value = enum_value($value); - - $strings = array_filter([$retrieved, $value], function ($value) { - return match (true) { - is_string($value) => true, - $value instanceof \Stringable => true, - default => false, - }; - }); - - if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) { - return in_array($operator, ['!=', '<>', '!==']); - } - - switch ($operator) { - default: - case '=': - case '==': return $retrieved == $value; - case '!=': - case '<>': return $retrieved != $value; - case '<': return $retrieved < $value; - case '>': return $retrieved > $value; - case '<=': return $retrieved <= $value; - case '>=': return $retrieved >= $value; - case '===': return $retrieved === $value; - case '!==': return $retrieved !== $value; - case '<=>': return $retrieved <=> $value; - } - }; - } - - /** - * Determine if the given value is callable, but not a string. - * - * @param mixed $value - * @return bool - */ - protected function useAsCallable($value) - { - return ! is_string($value) && is_callable($value); - } - - /** - * Get a value retrieving callback. - * - * @param callable|string|null $value - * @return callable - */ - protected function valueRetriever($value) - { - if ($this->useAsCallable($value)) { - return $value; - } - - return fn ($item) => data_get($item, $value); - } - - /** - * Make a function to check an item's equality. - * - * @param mixed $value - * @return \Closure(mixed): bool - */ - protected function equality($value) - { - return fn ($item) => $item === $value; - } - - /** - * Make a function using another function, by negating its result. - * - * @param \Closure $callback - * @return \Closure - */ - protected function negate(Closure $callback) - { - return fn (...$params) => ! $callback(...$params); - } - - /** - * Make a function that returns what's passed to it. - * - * @return \Closure(TValue): TValue - */ - protected function identity() - { - return fn ($value) => $value; - } -} diff --git a/src/Illuminate/Support/Traits/Macroable.php b/src/Illuminate/Support/Traits/Macroable.php deleted file mode 100644 index 2ee06e17..00000000 --- a/src/Illuminate/Support/Traits/Macroable.php +++ /dev/null @@ -1,134 +0,0 @@ -getMethods( - ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED - ); - - foreach ($methods as $method) { - if ($replace || ! static::hasMacro($method->name)) { - static::macro($method->name, $method->invoke($mixin)); - } - } - } - - /** - * Checks if macro is registered. - * - * @param string $name - * @return bool - */ - public static function hasMacro($name) - { - return isset(static::$macros[$name]); - } - - /** - * Flush the existing macros. - * - * @return void - */ - public static function flushMacros() - { - static::$macros = []; - } - - /** - * Dynamically handle calls to the class. - * - * @param string $method - * @param array $parameters - * @return mixed - * - * @throws \BadMethodCallException - */ - public static function __callStatic($method, $parameters) - { - if (! static::hasMacro($method)) { - throw new BadMethodCallException(sprintf( - 'Method %s::%s does not exist.', static::class, $method - )); - } - - $macro = static::$macros[$method]; - - if ($macro instanceof Closure) { - $macro = $macro->bindTo(null, static::class); - } - - return $macro(...$parameters); - } - - /** - * Dynamically handle calls to the class. - * - * @param string $method - * @param array $parameters - * @return mixed - * - * @throws \BadMethodCallException - */ - public function __call($method, $parameters) - { - if (! static::hasMacro($method)) { - throw new BadMethodCallException(sprintf( - 'Method %s::%s does not exist.', static::class, $method - )); - } - - $macro = static::$macros[$method]; - - if ($macro instanceof Closure) { - try { - $macro = $macro->bindTo($this, static::class) ?? throw new RuntimeException; - } catch (Throwable) { - $macro = $macro->bindTo(null, static::class); - } - } - - return $macro(...$parameters); - } -} diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index d2eb3329..adc6e3a2 100755 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -105,7 +105,16 @@ function array_add($array, $key, $value) */ function array_build($array, Closure $callback) { - return Arr::build($array, $callback); + $results = array(); + + foreach ($array as $key => $value) + { + [$innerKey, $innerValue] = call_user_func($callback, $key, $value); + + $results[$innerKey] = $innerValue; + } + + return $results; } } @@ -164,7 +173,22 @@ function array_except($array, $keys) */ function array_fetch($array, $key) { - return Arr::fetch($array, $key); + foreach (explode('.', $key) as $segment) + { + $results = array(); + + foreach ($array as $value) + { + if (array_key_exists($segment, $value = (array) $value)) + { + $results[] = $value[$segment]; + } + } + + $array = array_values($results); + } + + return array_values($results); } } diff --git a/tests/Database/DatabaseEloquentCollectionTest.php b/tests/Database/DatabaseEloquentCollectionTest.php index f488bac9..e8b2d8cd 100755 --- a/tests/Database/DatabaseEloquentCollectionTest.php +++ b/tests/Database/DatabaseEloquentCollectionTest.php @@ -177,13 +177,13 @@ public function testCollectionReturnsUniqueItems() } - public function testLists() + public function testPluck() { $data = new Collection( [(object) ['name' => 'taylor', 'email' => 'foo'], (object) ['name' => 'dayle', 'email' => 'bar']] ); - $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->lists('email', 'name')); - $this->assertEquals(['foo', 'bar'], $data->lists('email')); + $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->pluck('email', 'name')->all()); + $this->assertEquals(['foo', 'bar'], $data->pluck('email')->all()); } diff --git a/tests/Support/SupportArrTest.php b/tests/Support/SupportArrTest.php index 164631d6..971fc2a2 100644 --- a/tests/Support/SupportArrTest.php +++ b/tests/Support/SupportArrTest.php @@ -155,7 +155,7 @@ public function testExists(): void $this->assertTrue(Arr::exists([null], 0)); $this->assertTrue(Arr::exists(['a' => 1], 'a')); $this->assertTrue(Arr::exists(['a' => null], 'a')); - $this->assertFalse(Arr::exists(new Collection(['a' => null]), 'a')); + $this->assertTrue(Arr::exists(new Collection(['a' => null]), 'a')); $this->assertFalse(Arr::exists([1], 1)); $this->assertFalse(Arr::exists([null], 1)); @@ -900,14 +900,6 @@ public function testSet(): void $this->assertEquals([1 => 'hAz'], Arr::set($array, 1, 'hAz')); } - public function testShuffleWithSeed(): void - { - $this->assertEquals( - Arr::shuffle(range(0, 100, 10), 1234), - Arr::shuffle(range(0, 100, 10), 1234) - ); - } - public function testSort(): void { $unsorted = [ diff --git a/tests/Support/SupportCollectionTest.php b/tests/Support/SupportCollectionTest.php index 3100ce36..6dd31818 100755 --- a/tests/Support/SupportCollectionTest.php +++ b/tests/Support/SupportCollectionTest.php @@ -263,11 +263,11 @@ public function testChunk (): void } - public function testListsWithArrayAndObjectValues(): void + public function testPluckWithArrayAndObjectValues(): void { $data = new Collection([(object) ['name' => 'taylor', 'email' => 'foo'], ['name' => 'dayle', 'email' => 'bar']]); - $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->lists('email', 'name')); - $this->assertEquals(['foo', 'bar'], $data->lists('email')); + $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->pluck('email', 'name')->all()); + $this->assertEquals(['foo', 'bar'], $data->pluck('email')->all()); } @@ -340,13 +340,13 @@ public function testSplice(): void } - public function testGetListValueWithAccessors(): void + public function testGetPluckValueWithAccessors(): void { $model = new TestAccessorEloquentTestStub(['some' => 'foo']); $modelTwo = new TestAccessorEloquentTestStub(['some' => 'bar']); $data = new Collection([$model, $modelTwo]); - $this->assertEquals(['foo', 'bar'], $data->lists('some')); + $this->assertEquals(['foo', 'bar'], $data->pluck('some')->all()); } @@ -440,7 +440,7 @@ public function testValueRetrieverAcceptsDotNotation(): void ]); $c = $c->sortBy('foo.bar'); - $this->assertEquals([2, 1], $c->lists('id')); + $this->assertEquals([2, 1], $c->pluck('id')->all()); } @@ -516,6 +516,19 @@ public function __get($attribute) } + public function __isset($attribute) + { + $accessor = 'get'.lcfirst((string) $attribute).'Attribute'; + + if (method_exists($this, $accessor)) + { + return ! is_null($this->$accessor()); + } + + return isset($this->$attribute); + } + + public function getSomeAttribute() { return $this->attributes['some'];