From 3bea58470a37bb36c9a8925e2ccaef92711e2308 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Apr 2020 11:30:44 -0500 Subject: [PATCH 1/3] split collections into a separate package --- bin/release.sh | 2 +- bin/split.sh | 4 + src/Illuminate/Collections/Arr.php | 668 +++++++++ src/Illuminate/Collections/Collection.php | 1335 +++++++++++++++++ src/Illuminate/Collections/Enumerable.php | 928 ++++++++++++ .../EnumeratesValues.php | 36 +- .../HigherOrderCollectionProxy.php | 8 +- src/Illuminate/Collections/LICENSE.md | 21 + src/Illuminate/Collections/LazyCollection.php | 1273 ++++++++++++++++ src/Illuminate/Collections/composer.json | 35 + src/Illuminate/Macroable/LICENSE.md | 21 + src/Illuminate/Macroable/Macroable.php | 116 ++ src/Illuminate/Macroable/composer.json | 33 + src/Illuminate/Support/Arr.php | 664 +------- src/Illuminate/Support/Collection.php | 1318 +--------------- src/Illuminate/Support/Enumerable.php | 924 +----------- src/Illuminate/Support/LazyCollection.php | 1252 +--------------- src/Illuminate/Support/Traits/Macroable.php | 110 +- src/Illuminate/Support/composer.json | 2 + 19 files changed, 4492 insertions(+), 4258 deletions(-) create mode 100644 src/Illuminate/Collections/Arr.php create mode 100644 src/Illuminate/Collections/Collection.php create mode 100644 src/Illuminate/Collections/Enumerable.php rename src/Illuminate/{Support/Traits => Collections}/EnumeratesValues.php (97%) rename src/Illuminate/{Support => Collections}/HigherOrderCollectionProxy.php (86%) create mode 100644 src/Illuminate/Collections/LICENSE.md create mode 100644 src/Illuminate/Collections/LazyCollection.php create mode 100644 src/Illuminate/Collections/composer.json create mode 100644 src/Illuminate/Macroable/LICENSE.md create mode 100644 src/Illuminate/Macroable/Macroable.php create mode 100644 src/Illuminate/Macroable/composer.json diff --git a/bin/release.sh b/bin/release.sh index 889e09d202dd..9f40db21a010 100755 --- a/bin/release.sh +++ b/bin/release.sh @@ -52,7 +52,7 @@ git tag $VERSION git push origin --tags # Tag Components -for REMOTE in auth broadcasting bus cache config console container contracts cookie database encryption events filesystem hashing http log mail notifications pagination pipeline queue redis routing session support testing translation validation view +for REMOTE in auth broadcasting bus cache collections config console container contracts cookie database encryption events filesystem hashing http log macroable mail notifications pagination pipeline queue redis routing session support testing translation validation view do echo "" echo "" diff --git a/bin/split.sh b/bin/split.sh index 70630bac1e31..1e2ea0e98e93 100755 --- a/bin/split.sh +++ b/bin/split.sh @@ -22,6 +22,7 @@ remote auth git@github.com:illuminate/auth.git remote broadcasting git@github.com:illuminate/broadcasting.git remote bus git@github.com:illuminate/bus.git remote cache git@github.com:illuminate/cache.git +remote collections git@github.com:illuminate/collections.git remote config git@github.com:illuminate/config.git remote console git@github.com:illuminate/console.git remote container git@github.com:illuminate/container.git @@ -34,6 +35,7 @@ remote filesystem git@github.com:illuminate/filesystem.git remote hashing git@github.com:illuminate/hashing.git remote http git@github.com:illuminate/http.git remote log git@github.com:illuminate/log.git +remote macroable git@github.com:illuminate/macroable.git remote mail git@github.com:illuminate/mail.git remote notifications git@github.com:illuminate/notifications.git remote pagination git@github.com:illuminate/pagination.git @@ -52,6 +54,7 @@ split 'src/Illuminate/Auth' auth split 'src/Illuminate/Broadcasting' broadcasting split 'src/Illuminate/Bus' bus split 'src/Illuminate/Cache' cache +split 'src/Illuminate/Collections' collections split 'src/Illuminate/Config' config split 'src/Illuminate/Console' console split 'src/Illuminate/Container' container @@ -64,6 +67,7 @@ split 'src/Illuminate/Filesystem' filesystem split 'src/Illuminate/Hashing' hashing split 'src/Illuminate/Http' http split 'src/Illuminate/Log' log +split 'src/Illuminate/Macroable' macroable split 'src/Illuminate/Mail' mail split 'src/Illuminate/Notifications' notifications split 'src/Illuminate/Pagination' pagination diff --git a/src/Illuminate/Collections/Arr.php b/src/Illuminate/Collections/Arr.php new file mode 100644 index 000000000000..f625ae7bb166 --- /dev/null +++ b/src/Illuminate/Collections/Arr.php @@ -0,0 +1,668 @@ +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) + { + $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) + { + return [array_keys($array), array_values($array)]; + } + + /** + * Flatten a multi-dimensional associative array with dots. + * + * @param iterable $array + * @param string $prepend + * @return array + */ + public static function dot($array, $prepend = '') + { + $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; + } + + /** + * Get all of the given array except for a specified array of keys. + * + * @param array $array + * @param array|string $keys + * @return array + */ + public static function except($array, $keys) + { + static::forget($array, $keys); + + return $array; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param \ArrayAccess|array $array + * @param string|int $key + * @return bool + */ + public static function exists($array, $key) + { + if ($array instanceof Enumerable) { + return $array->has($key); + } + + if ($array instanceof ArrayAccess) { + return $array->offsetExists($key); + } + + return array_key_exists($key, $array); + } + + /** + * Return the first element in an array passing a given truth test. + * + * @param iterable $array + * @param callable|null $callback + * @param mixed $default + * @return mixed + */ + public static function first($array, callable $callback = null, $default = null) + { + 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); + } + + /** + * Return the last element in an array passing a given truth test. + * + * @param array $array + * @param callable|null $callback + * @param mixed $default + * @return mixed + */ + public static function last($array, callable $callback = null, $default = null) + { + 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 iterable $array + * @param int $depth + * @return array + */ + public static function flatten($array, $depth = INF) + { + $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]) && is_array($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 $array + * @param string|int|null $key + * @param mixed $default + * @return mixed + */ + public static function get($array, $key, $default = null) + { + if (! static::accessible($array)) { + return value($default); + } + + if (is_null($key)) { + return $array; + } + + if (static::exists($array, $key)) { + return $array[$key]; + } + + if (strpos($key, '.') === false) { + 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 or items exist in an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string|array $keys + * @return bool + */ + public static function has($array, $keys) + { + $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) + { + 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) + { + $keys = array_keys($array); + + return array_keys($keys) !== $keys; + } + + /** + * Get a subset of the items from the given array. + * + * @param array $array + * @param array|string $keys + * @return array + */ + public static function only($array, $keys) + { + return array_intersect_key($array, array_flip((array) $keys)); + } + + /** + * Pluck an array of values from an array. + * + * @param iterable $array + * @param string|array $value + * @param string|array|null $key + * @return array + */ + public static function pluck($array, $value, $key = null) + { + $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) + { + $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, $value, $key = null) + { + if (is_null($key)) { + array_unshift($array, $value); + } else { + $array = [$key => $value] + $array; + } + + return $array; + } + + /** + * 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; + } + + /** + * Get one or a specified number of random values from an array. + * + * @param array $array + * @param int|null $number + * @return mixed + * + * @throws \InvalidArgumentException + */ + public static function random($array, $number = null) + { + $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 = []; + + 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|null $key + * @param mixed $value + * @return array + */ + public static function set(&$array, $key, $value) + { + if (is_null($key)) { + return $array = $value; + } + + $keys = explode('.', $key); + + foreach ($keys as $i => $key) { + if (count($keys) === 1) { + break; + } + + unset($keys[$i]); + + // 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[$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) + { + if (is_null($seed)) { + shuffle($array); + } else { + mt_srand($seed); + shuffle($array); + mt_srand(); + } + + return $array; + } + + /** + * Sort the array using the given callback or "dot" notation. + * + * @param array $array + * @param callable|string|null $callback + * @return array + */ + public static function sort($array, $callback = null) + { + return Collection::make($array)->sortBy($callback)->all(); + } + + /** + * Recursively sort an array by keys and values. + * + * @param array $array + * @return array + */ + public static function sortRecursive($array) + { + foreach ($array as &$value) { + if (is_array($value)) { + $value = static::sortRecursive($value); + } + } + + if (static::isAssoc($array)) { + ksort($array); + } else { + sort($array); + } + + return $array; + } + + /** + * Convert the array into a query string. + * + * @param array $array + * @return string + */ + public static function query($array) + { + return http_build_query($array, null, '&', PHP_QUERY_RFC3986); + } + + /** + * Filter the array using the given callback. + * + * @param array $array + * @param callable $callback + * @return array + */ + public static function where($array, callable $callback) + { + return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); + } + + /** + * 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) + { + if (is_null($value)) { + return []; + } + + return is_array($value) ? $value : [$value]; + } +} diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php new file mode 100644 index 000000000000..924a8f8cb3b1 --- /dev/null +++ b/src/Illuminate/Collections/Collection.php @@ -0,0 +1,1335 @@ +items = $this->getArrayableItems($items); + } + + /** + * Create a new collection by invoking the callback a given amount of times. + * + * @param int $number + * @param callable|null $callback + * @return static + */ + public static function times($number, callable $callback = null) + { + if ($number < 1) { + return new static; + } + + if (is_null($callback)) { + return new static(range(1, $number)); + } + + return (new static(range(1, $number)))->map($callback); + } + + /** + * 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\Collections\LazyCollection + */ + public function lazy() + { + return new LazyCollection($this->items); + } + + /** + * Get the average value of a given key. + * + * @param callable|string|null $callback + * @return mixed + */ + public function avg($callback = null) + { + $callback = $this->valueRetriever($callback); + + $items = $this->map(function ($value) use ($callback) { + return $callback($value); + })->filter(function ($value) { + return ! is_null($value); + }); + + if ($count = $items->count()) { + return $items->sum() / $count; + } + } + + /** + * Get the median of a given key. + * + * @param string|array|null $key + * @return mixed + */ + public function median($key = null) + { + $values = (isset($key) ? $this->pluck($key) : $this) + ->filter(function ($item) { + return ! is_null($item); + })->sort()->values(); + + $count = $values->count(); + + if ($count === 0) { + return; + } + + $middle = (int) ($count / 2); + + if ($count % 2) { + return $values->get($middle); + } + + return (new static([ + $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->count() === 0) { + return; + } + + $collection = isset($key) ? $this->pluck($key) : $this; + + $counts = new self; + + $collection->each(function ($value) use ($counts) { + $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1; + }); + + $sorted = $counts->sort(); + + $highestValue = $sorted->last(); + + return $sorted->filter(function ($value) use ($highestValue) { + return $value == $highestValue; + })->sort()->keys()->all(); + } + + /** + * Collapse the collection of items into a single array. + * + * @return static + */ + public function collapse() + { + return new static(Arr::collapse($this->items)); + } + + /** + * Determine if an item exists in the collection. + * + * @param mixed $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)) { + $placeholder = new stdClass; + + return $this->first($key, $placeholder) !== $placeholder; + } + + return in_array($key, $this->items); + } + + return $this->contains($this->operatorForWhere(...func_get_args())); + } + + /** + * Cross join with the given lists, returning all possible permutations. + * + * @param mixed ...$lists + * @return static + */ + public function crossJoin(...$lists) + { + return new static(Arr::crossJoin( + $this->items, ...array_map([$this, 'getArrayableItems'], $lists) + )); + } + + /** + * Get the items in the collection that are not present in the given items. + * + * @param mixed $items + * @return static + */ + public function diff($items) + { + return new static(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 mixed $items + * @param callable $callback + * @return static + */ + public function diffUsing($items, callable $callback) + { + return new static(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 mixed $items + * @return static + */ + public function diffAssoc($items) + { + return new static(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 mixed $items + * @param callable $callback + * @return static + */ + public function diffAssocUsing($items, callable $callback) + { + return new static(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 mixed $items + * @return static + */ + public function diffKeys($items) + { + return new static(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 mixed $items + * @param callable $callback + * @return static + */ + public function diffKeysUsing($items, callable $callback) + { + return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback)); + } + + /** + * Retrieve duplicate items from the collection. + * + * @param callable|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 = new static; + + 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. + * + * @param callable|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 \Closure + */ + protected function duplicateComparator($strict) + { + if ($strict) { + return function ($a, $b) { + return $a === $b; + }; + } + + return function ($a, $b) { + return $a == $b; + }; + } + + /** + * Get all items except for those with the specified keys. + * + * @param \Illuminate\Collections\Collection|mixed $keys + * @return static + */ + public function except($keys) + { + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } elseif (! is_array($keys)) { + $keys = func_get_args(); + } + + return new static(Arr::except($this->items, $keys)); + } + + /** + * Run a filter over each of the items. + * + * @param callable|null $callback + * @return static + */ + public function filter(callable $callback = null) + { + if ($callback) { + return new static(Arr::where($this->items, $callback)); + } + + return new static(array_filter($this->items)); + } + + /** + * Get the first item from the collection passing the given truth test. + * + * @param callable|null $callback + * @param mixed $default + * @return mixed + */ + 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 new static(Arr::flatten($this->items, $depth)); + } + + /** + * Flip the items in the collection. + * + * @return static + */ + public function flip() + { + return new static(array_flip($this->items)); + } + + /** + * Remove an item from the collection by key. + * + * @param string|array $keys + * @return $this + */ + public function forget($keys) + { + foreach ((array) $keys as $key) { + $this->offsetUnset($key); + } + + return $this; + } + + /** + * Get an item from the collection by key. + * + * @param mixed $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + if (array_key_exists($key, $this->items)) { + return $this->items[$key]; + } + + return value($default); + } + + /** + * Group an associative array by a field or using a callback. + * + * @param array|callable|string $groupBy + * @param bool $preserveKeys + * @return static + */ + 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 = is_bool($groupKey) ? (int) $groupKey : $groupKey; + + if (! array_key_exists($groupKey, $results)) { + $results[$groupKey] = new static; + } + + $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); + } + } + + $result = new static($results); + + if (! empty($nextGroups)) { + return $result->map->groupBy($nextGroups, $preserveKeys); + } + + return $result; + } + + /** + * Key an associative array by a field or using a callback. + * + * @param callable|string $keyBy + * @return static + */ + public function keyBy($keyBy) + { + $keyBy = $this->valueRetriever($keyBy); + + $results = []; + + foreach ($this->items as $key => $item) { + $resolvedKey = $keyBy($item, $key); + + if (is_object($resolvedKey)) { + $resolvedKey = (string) $resolvedKey; + } + + $results[$resolvedKey] = $item; + } + + return new static($results); + } + + /** + * Determine if an item exists in the collection by key. + * + * @param mixed $key + * @return bool + */ + public function has($key) + { + $keys = is_array($key) ? $key : func_get_args(); + + foreach ($keys as $value) { + if (! array_key_exists($value, $this->items)) { + return false; + } + } + + return true; + } + + /** + * Concatenate values of a given key as a string. + * + * @param string $value + * @param string|null $glue + * @return string + */ + public function implode($value, $glue = null) + { + $first = $this->first(); + + if (is_array($first) || is_object($first)) { + return implode($glue, $this->pluck($value)->all()); + } + + return implode($value, $this->items); + } + + /** + * Intersect the collection with the given items. + * + * @param mixed $items + * @return static + */ + public function intersect($items) + { + return new static(array_intersect($this->items, $this->getArrayableItems($items))); + } + + /** + * Intersect the collection with the given items by key. + * + * @param mixed $items + * @return static + */ + public function intersectByKeys($items) + { + return new static(array_intersect_key( + $this->items, $this->getArrayableItems($items) + )); + } + + /** + * Determine if the collection is empty or not. + * + * @return bool + */ + public function isEmpty() + { + return empty($this->items); + } + + /** + * 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 = '') + { + if ($finalGlue === '') { + return $this->implode($glue); + } + + $count = $this->count(); + + if ($count === 0) { + return ''; + } + + if ($count === 1) { + return $this->last(); + } + + $collection = new static($this->items); + + $finalItem = $collection->pop(); + + return $collection->implode($glue).$finalGlue.$finalItem; + } + + /** + * Get the keys of the collection items. + * + * @return static + */ + public function keys() + { + return new static(array_keys($this->items)); + } + + /** + * Get the last item from the collection. + * + * @param callable|null $callback + * @param mixed $default + * @return mixed + */ + public function last(callable $callback = null, $default = null) + { + return Arr::last($this->items, $callback, $default); + } + + /** + * 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(Arr::pluck($this->items, $value, $key)); + } + + /** + * Run a map over each of the items. + * + * @param callable $callback + * @return static + */ + public function map(callable $callback) + { + $keys = array_keys($this->items); + + $items = array_map($callback, $this->items, $keys); + + return new static(array_combine($keys, $items)); + } + + /** + * Run a dictionary map over the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @param callable $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 new static($dictionary); + } + + /** + * Run an associative map over each of the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @param callable $callback + * @return static + */ + public function mapWithKeys(callable $callback) + { + $result = []; + + foreach ($this->items as $key => $value) { + $assoc = $callback($value, $key); + + foreach ($assoc as $mapKey => $mapValue) { + $result[$mapKey] = $mapValue; + } + } + + return new static($result); + } + + /** + * Merge the collection with the given items. + * + * @param mixed $items + * @return static + */ + public function merge($items) + { + return new static(array_merge($this->items, $this->getArrayableItems($items))); + } + + /** + * Recursively merge the collection with the given items. + * + * @param mixed $items + * @return static + */ + public function mergeRecursive($items) + { + return new static(array_merge_recursive($this->items, $this->getArrayableItems($items))); + } + + /** + * Create a collection by using this collection for keys and another for its values. + * + * @param mixed $values + * @return static + */ + public function combine($values) + { + return new static(array_combine($this->all(), $this->getArrayableItems($values))); + } + + /** + * Union the collection with the given items. + * + * @param mixed $items + * @return static + */ + public function union($items) + { + return new static($this->items + $this->getArrayableItems($items)); + } + + /** + * Create a new collection consisting of every n-th element. + * + * @param int $step + * @param int $offset + * @return static + */ + public function nth($step, $offset = 0) + { + $new = []; + + $position = 0; + + foreach ($this->items as $item) { + if ($position % $step === $offset) { + $new[] = $item; + } + + $position++; + } + + return new static($new); + } + + /** + * Get the items with the specified keys. + * + * @param mixed $keys + * @return static + */ + public function only($keys) + { + if (is_null($keys)) { + return new static($this->items); + } + + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } + + $keys = is_array($keys) ? $keys : func_get_args(); + + return new static(Arr::only($this->items, $keys)); + } + + /** + * Get and remove the last item from the collection. + * + * @return mixed + */ + public function pop() + { + return array_pop($this->items); + } + + /** + * Push an item onto the beginning of the collection. + * + * @param mixed $value + * @param mixed $key + * @return $this + */ + public function prepend($value, $key = null) + { + $this->items = Arr::prepend($this->items, $value, $key); + + return $this; + } + + /** + * Push one or more items onto the end of the collection. + * + * @param mixed $values [optional] + * @return $this + */ + public function push(...$values) + { + foreach ($values as $value) { + $this->items[] = $value; + } + + return $this; + } + + /** + * Push all of the given items onto the collection. + * + * @param iterable $source + * @return static + */ + public function concat($source) + { + $result = new static($this); + + foreach ($source as $item) { + $result->push($item); + } + + return $result; + } + + /** + * Get and remove an item from the collection. + * + * @param mixed $key + * @param mixed $default + * @return mixed + */ + public function pull($key, $default = null) + { + return Arr::pull($this->items, $key, $default); + } + + /** + * Put an item in the collection by key. + * + * @param mixed $key + * @param mixed $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 int|null $number + * @return static|mixed + * + * @throws \InvalidArgumentException + */ + public function random($number = null) + { + if (is_null($number)) { + return Arr::random($this->items); + } + + return new static(Arr::random($this->items, $number)); + } + + /** + * Reduce the collection to a single value. + * + * @param callable $callback + * @param mixed $initial + * @return mixed + */ + public function reduce(callable $callback, $initial = null) + { + return array_reduce($this->items, $callback, $initial); + } + + /** + * Replace the collection items with the given items. + * + * @param mixed $items + * @return static + */ + public function replace($items) + { + return new static(array_replace($this->items, $this->getArrayableItems($items))); + } + + /** + * Recursively replace the collection items with the given items. + * + * @param mixed $items + * @return static + */ + public function replaceRecursive($items) + { + return new static(array_replace_recursive($this->items, $this->getArrayableItems($items))); + } + + /** + * Reverse items order. + * + * @return static + */ + public function reverse() + { + return new static(array_reverse($this->items, true)); + } + + /** + * Search the collection for a given value and return the corresponding key if successful. + * + * @param mixed $value + * @param bool $strict + * @return mixed + */ + public function search($value, $strict = false) + { + if (! $this->useAsCallable($value)) { + return array_search($value, $this->items, $strict); + } + + foreach ($this->items as $key => $item) { + if ($value($item, $key)) { + return $key; + } + } + + return false; + } + + /** + * Get and remove the first item from the collection. + * + * @return mixed + */ + public function shift() + { + return array_shift($this->items); + } + + /** + * Shuffle the items in the collection. + * + * @param int|null $seed + * @return static + */ + public function shuffle($seed = null) + { + return new static(Arr::shuffle($this->items, $seed)); + } + + /** + * Skip the first {$count} items. + * + * @param int $count + * @return static + */ + public function skip($count) + { + return $this->slice($count); + } + + /** + * Slice the underlying collection array. + * + * @param int $offset + * @param int|null $length + * @return static + */ + public function slice($offset, $length = null) + { + return new static(array_slice($this->items, $offset, $length, true)); + } + + /** + * Split a collection into a certain number of groups. + * + * @param int $numberOfGroups + * @return static + */ + public function split($numberOfGroups) + { + if ($this->isEmpty()) { + return new static; + } + + $groups = new static; + + $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(new static(array_slice($this->items, $start, $size))); + + $start += $size; + } + } + + return $groups; + } + + /** + * Chunk the collection into chunks of the given size. + * + * @param int $size + * @return static + */ + public function chunk($size) + { + if ($size <= 0) { + return new static; + } + + $chunks = []; + + foreach (array_chunk($this->items, $size, true) as $chunk) { + $chunks[] = new static($chunk); + } + + return new static($chunks); + } + + /** + * Sort through each item with a callback. + * + * @param callable|null $callback + * @return static + */ + public function sort($callback = null) + { + $items = $this->items; + + $callback && is_callable($callback) + ? uasort($items, $callback) + : asort($items, $callback); + + return new static($items); + } + + /** + * Sort items in descending order. + * + * @param int $options + * @return static + */ + public function sortDesc($options = SORT_REGULAR) + { + $items = $this->items; + + arsort($items, $options); + + return new static($items); + } + + /** + * Sort the collection using the given callback. + * + * @param callable|string $callback + * @param int $options + * @param bool $descending + * @return static + */ + public function sortBy($callback, $options = SORT_REGULAR, $descending = false) + { + $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 + // and grab the corresponding values for the sorted keys from this array. + foreach ($this->items as $key => $value) { + $results[$key] = $callback($value, $key); + } + + $descending ? arsort($results, $options) + : asort($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 new static($results); + } + + /** + * Sort the collection in descending order using the given callback. + * + * @param callable|string $callback + * @param int $options + * @return static + */ + public function sortByDesc($callback, $options = SORT_REGULAR) + { + return $this->sortBy($callback, $options, true); + } + + /** + * Sort the collection keys. + * + * @param int $options + * @param bool $descending + * @return static + */ + public function sortKeys($options = SORT_REGULAR, $descending = false) + { + $items = $this->items; + + $descending ? krsort($items, $options) : ksort($items, $options); + + return new static($items); + } + + /** + * Sort the collection keys in descending order. + * + * @param int $options + * @return static + */ + public function sortKeysDesc($options = SORT_REGULAR) + { + return $this->sortKeys($options, true); + } + + /** + * Splice a portion of the underlying collection array. + * + * @param int $offset + * @param int|null $length + * @param mixed $replacement + * @return static + */ + public function splice($offset, $length = null, $replacement = []) + { + if (func_num_args() === 1) { + return new static(array_splice($this->items, $offset)); + } + + return new static(array_splice($this->items, $offset, $length, $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); + } + + /** + * Transform each item in the collection using a callback. + * + * @param callable $callback + * @return $this + */ + public function transform(callable $callback) + { + $this->items = $this->map($callback)->all(); + + return $this; + } + + /** + * Reset the keys on the underlying array. + * + * @return static + */ + public function values() + { + return new static(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]] + * + * @param mixed ...$items + * @return static + */ + public function zip($items) + { + $arrayableItems = array_map(function ($items) { + return $this->getArrayableItems($items); + }, func_get_args()); + + $params = array_merge([function () { + return new static(func_get_args()); + }, $this->items], $arrayableItems); + + return new static(call_user_func_array('array_map', $params)); + } + + /** + * Pad collection to the specified length with a value. + * + * @param int $size + * @param mixed $value + * @return static + */ + public function pad($size, $value) + { + return new static(array_pad($this->items, $size, $value)); + } + + /** + * Get an iterator for the items. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->items); + } + + /** + * Count the number of items in the collection. + * + * @return int + */ + public function count() + { + return count($this->items); + } + + /** + * Add an item to the collection. + * + * @param mixed $item + * @return $this + */ + public function add($item) + { + $this->items[] = $item; + + return $this; + } + + /** + * Get a base Support collection instance from this collection. + * + * @return \Illuminate\Collections\Collection + */ + public function toBase() + { + return new self($this); + } + + /** + * Determine if an item exists at an offset. + * + * @param mixed $key + * @return bool + */ + public function offsetExists($key) + { + return isset($this->items[$key]); + } + + /** + * Get an item at a given offset. + * + * @param mixed $key + * @return mixed + */ + public function offsetGet($key) + { + return $this->items[$key]; + } + + /** + * Set the item at a given offset. + * + * @param mixed $key + * @param mixed $value + * @return void + */ + public function offsetSet($key, $value) + { + if (is_null($key)) { + $this->items[] = $value; + } else { + $this->items[$key] = $value; + } + } + + /** + * Unset the item at a given offset. + * + * @param string $key + * @return void + */ + public function offsetUnset($key) + { + unset($this->items[$key]); + } +} diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php new file mode 100644 index 000000000000..6de4a3bc8b9c --- /dev/null +++ b/src/Illuminate/Collections/Enumerable.php @@ -0,0 +1,928 @@ +source = $source; + } elseif (is_null($source)) { + $this->source = static::empty(); + } else { + $this->source = $this->getArrayableItems($source); + } + } + + /** + * Create a new instance with no items. + * + * @return static + */ + public static function empty() + { + return new static([]); + } + + /** + * Create a new instance by invoking the callback a given amount of times. + * + * @param int $number + * @param callable|null $callback + * @return static + */ + public static function times($number, callable $callback = null) + { + if ($number < 1) { + return new static; + } + + $instance = new static(function () use ($number) { + for ($current = 1; $current <= $number; $current++) { + yield $current; + } + }); + + return is_null($callback) ? $instance : $instance->map($callback); + } + + /** + * Create an enumerable with the given range. + * + * @param int $from + * @param int $to + * @return static + */ + public static function range($from, $to) + { + return new static(function () use ($from, $to) { + for (; $from <= $to; $from++) { + 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 average value of a given key. + * + * @param callable|string|null $callback + * @return mixed + */ + public function avg($callback = null) + { + return $this->collect()->avg($callback); + } + + /** + * Get the median of a given key. + * + * @param string|array|null $key + * @return mixed + */ + 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; + } + } + } + }); + } + + /** + * Determine if an item exists in the enumerable. + * + * @param mixed $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; + + 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())); + } + + /** + * Cross join the given iterables, returning all possible permutations. + * + * @param array ...$arrays + * @return static + */ + public function crossJoin(...$arrays) + { + return $this->passthru('crossJoin', func_get_args()); + } + + /** + * Get the items that are not present in the given items. + * + * @param mixed $items + * @return static + */ + public function diff($items) + { + return $this->passthru('diff', func_get_args()); + } + + /** + * Get the items that are not present in the given items, using the callback. + * + * @param mixed $items + * @param callable $callback + * @return static + */ + public function diffUsing($items, callable $callback) + { + return $this->passthru('diffUsing', func_get_args()); + } + + /** + * Get the items whose keys and values are not present in the given items. + * + * @param mixed $items + * @return static + */ + public function diffAssoc($items) + { + return $this->passthru('diffAssoc', func_get_args()); + } + + /** + * Get the items whose keys and values are not present in the given items, using the callback. + * + * @param mixed $items + * @param callable $callback + * @return static + */ + public function diffAssocUsing($items, callable $callback) + { + return $this->passthru('diffAssocUsing', func_get_args()); + } + + /** + * Get the items whose keys are not present in the given items. + * + * @param mixed $items + * @return static + */ + public function diffKeys($items) + { + return $this->passthru('diffKeys', func_get_args()); + } + + /** + * Get the items whose keys are not present in the given items, using the callback. + * + * @param mixed $items + * @param callable $callback + * @return static + */ + public function diffKeysUsing($items, callable $callback) + { + return $this->passthru('diffKeysUsing', func_get_args()); + } + + /** + * Retrieve duplicate items. + * + * @param callable|null $callback + * @param bool $strict + * @return static + */ + public function duplicates($callback = null, $strict = false) + { + return $this->passthru('duplicates', func_get_args()); + } + + /** + * Retrieve duplicate items using strict comparison. + * + * @param callable|null $callback + * @return static + */ + public function duplicatesStrict($callback = null) + { + return $this->passthru('duplicatesStrict', func_get_args()); + } + + /** + * Get all items except for those with the specified keys. + * + * @param mixed $keys + * @return static + */ + public function except($keys) + { + return $this->passthru('except', func_get_args()); + } + + /** + * Run a filter over each of the items. + * + * @param callable|null $callback + * @return static + */ + public function filter(callable $callback = null) + { + if (is_null($callback)) { + $callback = function ($value) { + return (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. + * + * @param callable|null $callback + * @param mixed $default + * @return mixed + */ + 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) { + yield $value => $key; + } + }); + } + + /** + * Get an item by key. + * + * @param mixed $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + if (is_null($key)) { + return; + } + + foreach ($this as $outerKey => $outerValue) { + if ($outerKey == $key) { + return $outerValue; + } + } + + return value($default); + } + + /** + * Group an associative array by a field or using a callback. + * + * @param array|callable|string $groupBy + * @param bool $preserveKeys + * @return static + */ + public function groupBy($groupBy, $preserveKeys = false) + { + return $this->passthru('groupBy', func_get_args()); + } + + /** + * Key an associative array by a field or using a callback. + * + * @param callable|string $keyBy + * @return static + */ + public function keyBy($keyBy) + { + return new static(function () use ($keyBy) { + $keyBy = $this->valueRetriever($keyBy); + + foreach ($this as $key => $item) { + $resolvedKey = $keyBy($item, $key); + + if (is_object($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()); + $count = count($keys); + + foreach ($this as $key => $value) { + if (array_key_exists($key, $keys) && --$count == 0) { + return true; + } + } + + return false; + } + + /** + * Concatenate values of a given key as a string. + * + * @param string $value + * @param string|null $glue + * @return string + */ + public function implode($value, $glue = null) + { + return $this->collect()->implode(...func_get_args()); + } + + /** + * Intersect the collection with the given items. + * + * @param mixed $items + * @return static + */ + public function intersect($items) + { + return $this->passthru('intersect', func_get_args()); + } + + /** + * Intersect the collection with the given items by key. + * + * @param mixed $items + * @return static + */ + public function intersectByKeys($items) + { + return $this->passthru('intersectByKeys', func_get_args()); + } + + /** + * Determine if the items is empty or not. + * + * @return bool + */ + public function isEmpty() + { + return ! $this->getIterator()->valid(); + } + + /** + * 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. + * + * @param callable|null $callback + * @param mixed $default + * @return mixed + */ + 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 = data_get($item, $value); + + if (is_null($key)) { + yield $itemValue; + } else { + $itemKey = 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. + * + * @param callable $callback + * @return static + */ + public function map(callable $callback) + { + return new static(function () use ($callback) { + foreach ($this as $key => $value) { + yield $key => $callback($value, $key); + } + }); + } + + /** + * Run a dictionary map over the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @param callable $callback + * @return static + */ + public function mapToDictionary(callable $callback) + { + return $this->passthru('mapToDictionary', 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. + * + * @param callable $callback + * @return static + */ + public function mapWithKeys(callable $callback) + { + return new static(function () use ($callback) { + foreach ($this as $key => $value) { + yield from $callback($value, $key); + } + }); + } + + /** + * Merge the collection with the given items. + * + * @param mixed $items + * @return static + */ + public function merge($items) + { + return $this->passthru('merge', func_get_args()); + } + + /** + * Recursively merge the collection with the given items. + * + * @param mixed $items + * @return static + */ + public function mergeRecursive($items) + { + return $this->passthru('mergeRecursive', func_get_args()); + } + + /** + * Create a collection by using this collection for keys and another for its values. + * + * @param mixed $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); + } + }); + } + + /** + * Union the collection with the given items. + * + * @param mixed $items + * @return static + */ + public function union($items) + { + return $this->passthru('union', func_get_args()); + } + + /** + * Create a new collection consisting of every n-th element. + * + * @param int $step + * @param int $offset + * @return static + */ + public function nth($step, $offset = 0) + { + return new static(function () use ($step, $offset) { + $position = 0; + + foreach ($this as $item) { + if ($position % $step === $offset) { + yield $item; + } + + $position++; + } + }); + } + + /** + * Get the items with the specified keys. + * + * @param mixed $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; + } + } + } + } + }); + } + + /** + * Push all of the given items onto the collection. + * + * @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 + * @return static|mixed + * + * @throws \InvalidArgumentException + */ + public function random($number = null) + { + $result = $this->collect()->random(...func_get_args()); + + return is_null($number) ? $result : new static($result); + } + + /** + * Reduce the collection to a single value. + * + * @param callable $callback + * @param mixed $initial + * @return mixed + */ + public function reduce(callable $callback, $initial = null) + { + $result = $initial; + + foreach ($this as $value) { + $result = $callback($result, $value); + } + + return $result; + } + + /** + * Replace the collection items with the given items. + * + * @param mixed $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; + } + }); + } + + /** + * Recursively replace the collection items with the given items. + * + * @param mixed $items + * @return static + */ + public function replaceRecursive($items) + { + return $this->passthru('replaceRecursive', func_get_args()); + } + + /** + * Reverse items order. + * + * @return static + */ + public function reverse() + { + return $this->passthru('reverse', func_get_args()); + } + + /** + * Search the collection for a given value and return the corresponding key if successful. + * + * @param mixed $value + * @param bool $strict + * @return mixed + */ + public function search($value, $strict = false) + { + $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; + } + + /** + * Shuffle the items in the collection. + * + * @param int|null $seed + * @return static + */ + public function shuffle($seed = null) + { + return $this->passthru('shuffle', func_get_args()); + } + + /** + * 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(); + } + }); + } + + /** + * Get a slice of items from the enumerable. + * + * @param int $offset + * @param int|null $length + * @return static + */ + public function slice($offset, $length = null) + { + if ($offset < 0 || $length < 0) { + return $this->passthru('slice', func_get_args()); + } + + $instance = $this->skip($offset); + + return is_null($length) ? $instance : $instance->take($length); + } + + /** + * Split a collection into a certain number of groups. + * + * @param int $numberOfGroups + * @return static + */ + public function split($numberOfGroups) + { + return $this->passthru('split', func_get_args()); + } + + /** + * Chunk the collection into chunks of the given size. + * + * @param int $size + * @return static + */ + public function chunk($size) + { + if ($size <= 0) { + return static::empty(); + } + + return new static(function () use ($size) { + $iterator = $this->getIterator(); + + while ($iterator->valid()) { + $chunk = []; + + while (true) { + $chunk[$iterator->key()] = $iterator->current(); + + if (count($chunk) < $size) { + $iterator->next(); + + if (! $iterator->valid()) { + break; + } + } else { + break; + } + } + + yield new static($chunk); + + $iterator->next(); + } + }); + } + + /** + * Sort through each item with a callback. + * + * @param callable|null|int $callback + * @return static + */ + public function sort($callback = null) + { + return $this->passthru('sort', func_get_args()); + } + + /** + * Sort items in descending order. + * + * @param int $options + * @return static + */ + public function sortDesc($options = SORT_REGULAR) + { + return $this->passthru('sortDesc', func_get_args()); + } + + /** + * Sort the collection using the given callback. + * + * @param callable|string $callback + * @param int $options + * @param bool $descending + * @return static + */ + public function sortBy($callback, $options = SORT_REGULAR, $descending = false) + { + return $this->passthru('sortBy', func_get_args()); + } + + /** + * Sort the collection in descending order using the given callback. + * + * @param callable|string $callback + * @param int $options + * @return static + */ + public function sortByDesc($callback, $options = SORT_REGULAR) + { + return $this->passthru('sortByDesc', func_get_args()); + } + + /** + * Sort the collection keys. + * + * @param int $options + * @param bool $descending + * @return static + */ + public function sortKeys($options = SORT_REGULAR, $descending = false) + { + return $this->passthru('sortKeys', func_get_args()); + } + + /** + * Sort the collection keys in descending order. + * + * @param int $options + * @return static + */ + public function sortKeysDesc($options = SORT_REGULAR) + { + return $this->passthru('sortKeysDesc', func_get_args()); + } + + /** + * Take the first or last {$limit} items. + * + * @param int $limit + * @return static + */ + public function take($limit) + { + if ($limit < 0) { + return $this->passthru('take', func_get_args()); + } + + return new static(function () use ($limit) { + $iterator = $this->getIterator(); + + while ($limit--) { + if (! $iterator->valid()) { + break; + } + + yield $iterator->key() => $iterator->current(); + + if ($limit) { + $iterator->next(); + } + } + }); + } + + /** + * Pass each item in the collection to the given callback, lazily. + * + * @param callable $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; + } + }); + } + + /** + * Reset the keys on the underlying array. + * + * @return static + */ + public function values() + { + return new static(function () { + foreach ($this as $item) { + yield $item; + } + }); + } + + /** + * 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]] + * + * @param mixed ...$items + * @return static + */ + public function zip($items) + { + $iterables = func_get_args(); + + return new static(function () use ($iterables) { + $iterators = Collection::make($iterables)->map(function ($iterable) { + return $this->makeIterator($iterable); + })->prepend($this->getIterator()); + + while ($iterators->contains->valid()) { + yield new static($iterators->map->current()); + + $iterators->each->next(); + } + }); + } + + /** + * Pad collection to the specified length with a value. + * + * @param int $size + * @param mixed $value + * @return static + */ + public function pad($size, $value) + { + if ($size < 0) { + return $this->passthru('pad', 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() + { + return $this->makeIterator($this->source); + } + + /** + * Count the number of items in the collection. + * + * @return int + */ + public function count() + { + if (is_array($this->source)) { + return count($this->source); + } + + return iterator_count($this->getIterator()); + } + + /** + * Make an iterator from the given source. + * + * @param mixed $source + * @return \Traversable + */ + protected function makeIterator($source) + { + if ($source instanceof IteratorAggregate) { + return $source->getIterator(); + } + + if (is_array($source)) { + return new ArrayIterator($source); + } + + return $source(); + } + + /** + * Explode the "value" and "key" arguments passed to "pluck". + * + * @param string|array $value + * @param string|array|null $key + * @return array + */ + protected function explodePluckParameters($value, $key) + { + $value = is_string($value) ? explode('.', $value) : $value; + + $key = is_null($key) || is_array($key) ? $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); + }); + } +} diff --git a/src/Illuminate/Collections/composer.json b/src/Illuminate/Collections/composer.json new file mode 100644 index 000000000000..c53b0d004e41 --- /dev/null +++ b/src/Illuminate/Collections/composer.json @@ -0,0 +1,35 @@ +{ + "name": "illuminate/collections", + "description": "The Illuminate Collections package.", + "license": "MIT", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "require": { + "php": "^7.3", + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0" + }, + "autoload": { + "psr-4": { + "Illuminate\\Collections\\": "" + } + }, + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev" +} diff --git a/src/Illuminate/Macroable/LICENSE.md b/src/Illuminate/Macroable/LICENSE.md new file mode 100644 index 000000000000..79810c848f8b --- /dev/null +++ b/src/Illuminate/Macroable/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Taylor Otwell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Illuminate/Macroable/Macroable.php b/src/Illuminate/Macroable/Macroable.php new file mode 100644 index 000000000000..8c0d7c88e869 --- /dev/null +++ b/src/Illuminate/Macroable/Macroable.php @@ -0,0 +1,116 @@ +getMethods( + ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED + ); + + foreach ($methods as $method) { + if ($replace || ! static::hasMacro($method->name)) { + $method->setAccessible(true); + 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]); + } + + /** + * 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) { + return call_user_func_array(Closure::bind($macro, null, static::class), $parameters); + } + + 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) { + return call_user_func_array($macro->bindTo($this, static::class), $parameters); + } + + return $macro(...$parameters); + } +} diff --git a/src/Illuminate/Macroable/composer.json b/src/Illuminate/Macroable/composer.json new file mode 100644 index 000000000000..767dd190ff05 --- /dev/null +++ b/src/Illuminate/Macroable/composer.json @@ -0,0 +1,33 @@ +{ + "name": "illuminate/macroable", + "description": "The Illuminate Macroable package.", + "license": "MIT", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "require": { + "php": "^7.3" + }, + "autoload": { + "psr-4": { + "Illuminate\\Macroable\\": "" + } + }, + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev" +} diff --git a/src/Illuminate/Support/Arr.php b/src/Illuminate/Support/Arr.php index 7ff8b8d32087..bcb1ad21f6ae 100755 --- a/src/Illuminate/Support/Arr.php +++ b/src/Illuminate/Support/Arr.php @@ -2,667 +2,9 @@ namespace Illuminate\Support; -use ArrayAccess; -use Illuminate\Support\Traits\Macroable; -use InvalidArgumentException; +use Illuminate\Collections\Arr as BaseArr; -class Arr +class Arr extends BaseArr { - use Macroable; - - /** - * Determine whether the given value is array accessible. - * - * @param mixed $value - * @return bool - */ - public static function accessible($value) - { - 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, $key, $value) - { - if (is_null(static::get($array, $key))) { - static::set($array, $key, $value); - } - - return $array; - } - - /** - * Collapse an array of arrays into a single array. - * - * @param iterable $array - * @return array - */ - public static function collapse($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) - { - $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) - { - return [array_keys($array), array_values($array)]; - } - - /** - * Flatten a multi-dimensional associative array with dots. - * - * @param iterable $array - * @param string $prepend - * @return array - */ - public static function dot($array, $prepend = '') - { - $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; - } - - /** - * Get all of the given array except for a specified array of keys. - * - * @param array $array - * @param array|string $keys - * @return array - */ - public static function except($array, $keys) - { - static::forget($array, $keys); - - return $array; - } - - /** - * Determine if the given key exists in the provided array. - * - * @param \ArrayAccess|array $array - * @param string|int $key - * @return bool - */ - public static function exists($array, $key) - { - if ($array instanceof Enumerable) { - return $array->has($key); - } - - if ($array instanceof ArrayAccess) { - return $array->offsetExists($key); - } - - return array_key_exists($key, $array); - } - - /** - * Return the first element in an array passing a given truth test. - * - * @param iterable $array - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public static function first($array, callable $callback = null, $default = null) - { - 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); - } - - /** - * Return the last element in an array passing a given truth test. - * - * @param array $array - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public static function last($array, callable $callback = null, $default = null) - { - 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 iterable $array - * @param int $depth - * @return array - */ - public static function flatten($array, $depth = INF) - { - $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]) && is_array($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 $array - * @param string|int|null $key - * @param mixed $default - * @return mixed - */ - public static function get($array, $key, $default = null) - { - if (! static::accessible($array)) { - return value($default); - } - - if (is_null($key)) { - return $array; - } - - if (static::exists($array, $key)) { - return $array[$key]; - } - - if (strpos($key, '.') === false) { - 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 or items exist in an array using "dot" notation. - * - * @param \ArrayAccess|array $array - * @param string|array $keys - * @return bool - */ - public static function has($array, $keys) - { - $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) - { - 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) - { - $keys = array_keys($array); - - return array_keys($keys) !== $keys; - } - - /** - * Get a subset of the items from the given array. - * - * @param array $array - * @param array|string $keys - * @return array - */ - public static function only($array, $keys) - { - return array_intersect_key($array, array_flip((array) $keys)); - } - - /** - * Pluck an array of values from an array. - * - * @param iterable $array - * @param string|array $value - * @param string|array|null $key - * @return array - */ - public static function pluck($array, $value, $key = null) - { - $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) - { - $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, $value, $key = null) - { - if (is_null($key)) { - array_unshift($array, $value); - } else { - $array = [$key => $value] + $array; - } - - return $array; - } - - /** - * 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; - } - - /** - * Get one or a specified number of random values from an array. - * - * @param array $array - * @param int|null $number - * @return mixed - * - * @throws \InvalidArgumentException - */ - public static function random($array, $number = null) - { - $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 = []; - - 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|null $key - * @param mixed $value - * @return array - */ - public static function set(&$array, $key, $value) - { - if (is_null($key)) { - return $array = $value; - } - - $keys = explode('.', $key); - - foreach ($keys as $i => $key) { - if (count($keys) === 1) { - break; - } - - unset($keys[$i]); - - // 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[$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) - { - if (is_null($seed)) { - shuffle($array); - } else { - mt_srand($seed); - shuffle($array); - mt_srand(); - } - - return $array; - } - - /** - * Sort the array using the given callback or "dot" notation. - * - * @param array $array - * @param callable|string|null $callback - * @return array - */ - public static function sort($array, $callback = null) - { - return Collection::make($array)->sortBy($callback)->all(); - } - - /** - * Recursively sort an array by keys and values. - * - * @param array $array - * @return array - */ - public static function sortRecursive($array) - { - foreach ($array as &$value) { - if (is_array($value)) { - $value = static::sortRecursive($value); - } - } - - if (static::isAssoc($array)) { - ksort($array); - } else { - sort($array); - } - - return $array; - } - - /** - * Convert the array into a query string. - * - * @param array $array - * @return string - */ - public static function query($array) - { - return http_build_query($array, null, '&', PHP_QUERY_RFC3986); - } - - /** - * Filter the array using the given callback. - * - * @param array $array - * @param callable $callback - * @return array - */ - public static function where($array, callable $callback) - { - return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); - } - - /** - * 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) - { - 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 index 27b0a4774234..47a674edc7b6 100644 --- a/src/Illuminate/Support/Collection.php +++ b/src/Illuminate/Support/Collection.php @@ -2,1277 +2,28 @@ namespace Illuminate\Support; -use ArrayAccess; -use ArrayIterator; -use Illuminate\Support\Traits\EnumeratesValues; -use Illuminate\Support\Traits\Macroable; -use stdClass; +use Illuminate\Collections\Collection as BaseCollection; -class Collection implements ArrayAccess, Enumerable +class Collection extends BaseCollection { - use EnumeratesValues, Macroable; - - /** - * The items contained in the collection. - * - * @var array - */ - protected $items = []; - - /** - * Create a new collection. - * - * @param mixed $items - * @return void - */ - public function __construct($items = []) - { - $this->items = $this->getArrayableItems($items); - } - - /** - * Create a new collection by invoking the callback a given amount of times. - * - * @param int $number - * @param callable|null $callback - * @return static - */ - public static function times($number, callable $callback = null) - { - if ($number < 1) { - return new static; - } - - if (is_null($callback)) { - return new static(range(1, $number)); - } - - return (new static(range(1, $number)))->map($callback); - } - - /** - * 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 average value of a given key. - * - * @param callable|string|null $callback - * @return mixed - */ - public function avg($callback = null) - { - $callback = $this->valueRetriever($callback); - - $items = $this->map(function ($value) use ($callback) { - return $callback($value); - })->filter(function ($value) { - return ! is_null($value); - }); - - if ($count = $items->count()) { - return $items->sum() / $count; - } - } - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return mixed - */ - public function median($key = null) - { - $values = (isset($key) ? $this->pluck($key) : $this) - ->filter(function ($item) { - return ! is_null($item); - })->sort()->values(); - - $count = $values->count(); - - if ($count === 0) { - return; - } - - $middle = (int) ($count / 2); - - if ($count % 2) { - return $values->get($middle); - } - - return (new static([ - $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->count() === 0) { - return; - } - - $collection = isset($key) ? $this->pluck($key) : $this; - - $counts = new self; - - $collection->each(function ($value) use ($counts) { - $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1; - }); - - $sorted = $counts->sort(); - - $highestValue = $sorted->last(); - - return $sorted->filter(function ($value) use ($highestValue) { - return $value == $highestValue; - })->sort()->keys()->all(); - } - - /** - * Collapse the collection of items into a single array. - * - * @return static - */ - public function collapse() - { - return new static(Arr::collapse($this->items)); - } - - /** - * Determine if an item exists in the collection. - * - * @param mixed $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)) { - $placeholder = new stdClass; - - return $this->first($key, $placeholder) !== $placeholder; - } - - return in_array($key, $this->items); - } - - return $this->contains($this->operatorForWhere(...func_get_args())); - } - - /** - * Cross join with the given lists, returning all possible permutations. - * - * @param mixed ...$lists - * @return static - */ - public function crossJoin(...$lists) - { - return new static(Arr::crossJoin( - $this->items, ...array_map([$this, 'getArrayableItems'], $lists) - )); - } - - /** - * Get the items in the collection that are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diff($items) - { - return new static(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 mixed $items - * @param callable $callback - * @return static - */ - public function diffUsing($items, callable $callback) - { - return new static(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 mixed $items - * @return static - */ - public function diffAssoc($items) - { - return new static(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 mixed $items - * @param callable $callback - * @return static - */ - public function diffAssocUsing($items, callable $callback) - { - return new static(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 mixed $items - * @return static - */ - public function diffKeys($items) - { - return new static(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 mixed $items - * @param callable $callback - * @return static - */ - public function diffKeysUsing($items, callable $callback) - { - return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Retrieve duplicate items from the collection. - * - * @param callable|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 = new static; - - 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. - * - * @param callable|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 \Closure - */ - protected function duplicateComparator($strict) - { - if ($strict) { - return function ($a, $b) { - return $a === $b; - }; - } - - return function ($a, $b) { - return $a == $b; - }; - } - - /** - * Get all items except for those with the specified keys. - * - * @param \Illuminate\Support\Collection|mixed $keys - * @return static - */ - public function except($keys) - { - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_array($keys)) { - $keys = func_get_args(); - } - - return new static(Arr::except($this->items, $keys)); - } - - /** - * Run a filter over each of the items. - * - * @param callable|null $callback - * @return static - */ - public function filter(callable $callback = null) - { - if ($callback) { - return new static(Arr::where($this->items, $callback)); - } - - return new static(array_filter($this->items)); - } - - /** - * Get the first item from the collection passing the given truth test. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - 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 new static(Arr::flatten($this->items, $depth)); - } - - /** - * Flip the items in the collection. - * - * @return static - */ - public function flip() - { - return new static(array_flip($this->items)); - } - - /** - * Remove an item from the collection by key. - * - * @param string|array $keys - * @return $this - */ - public function forget($keys) - { - foreach ((array) $keys as $key) { - $this->offsetUnset($key); - } - - return $this; - } - - /** - * Get an item from the collection by key. - * - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function get($key, $default = null) - { - if (array_key_exists($key, $this->items)) { - return $this->items[$key]; - } - - return value($default); - } - - /** - * Group an associative array by a field or using a callback. - * - * @param array|callable|string $groupBy - * @param bool $preserveKeys - * @return static - */ - 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 = is_bool($groupKey) ? (int) $groupKey : $groupKey; - - if (! array_key_exists($groupKey, $results)) { - $results[$groupKey] = new static; - } - - $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); - } - } - - $result = new static($results); - - if (! empty($nextGroups)) { - return $result->map->groupBy($nextGroups, $preserveKeys); - } - - return $result; - } - - /** - * Key an associative array by a field or using a callback. - * - * @param callable|string $keyBy - * @return static - */ - public function keyBy($keyBy) - { - $keyBy = $this->valueRetriever($keyBy); - - $results = []; - - foreach ($this->items as $key => $item) { - $resolvedKey = $keyBy($item, $key); - - if (is_object($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - $results[$resolvedKey] = $item; - } - - return new static($results); - } - - /** - * Determine if an item exists in the collection by key. - * - * @param mixed $key - * @return bool - */ - public function has($key) - { - $keys = is_array($key) ? $key : func_get_args(); - - foreach ($keys as $value) { - if (! array_key_exists($value, $this->items)) { - return false; - } - } - - return true; - } - - /** - * Concatenate values of a given key as a string. - * - * @param string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null) - { - $first = $this->first(); - - if (is_array($first) || is_object($first)) { - return implode($glue, $this->pluck($value)->all()); - } - - return implode($value, $this->items); - } - - /** - * Intersect the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function intersect($items) - { - return new static(array_intersect($this->items, $this->getArrayableItems($items))); - } - - /** - * Intersect the collection with the given items by key. - * - * @param mixed $items - * @return static - */ - public function intersectByKeys($items) - { - return new static(array_intersect_key( - $this->items, $this->getArrayableItems($items) - )); - } - - /** - * Determine if the collection is empty or not. - * - * @return bool - */ - public function isEmpty() - { - return empty($this->items); - } - - /** - * 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 = '') - { - if ($finalGlue === '') { - return $this->implode($glue); - } - - $count = $this->count(); - - if ($count === 0) { - return ''; - } - - if ($count === 1) { - return $this->last(); - } - - $collection = new static($this->items); - - $finalItem = $collection->pop(); - - return $collection->implode($glue).$finalGlue.$finalItem; - } - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys() - { - return new static(array_keys($this->items)); - } - - /** - * Get the last item from the collection. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public function last(callable $callback = null, $default = null) - { - return Arr::last($this->items, $callback, $default); - } - - /** - * 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(Arr::pluck($this->items, $value, $key)); - } - - /** - * Run a map over each of the items. - * - * @param callable $callback - * @return static - */ - public function map(callable $callback) - { - $keys = array_keys($this->items); - - $items = array_map($callback, $this->items, $keys); - - return new static(array_combine($keys, $items)); - } - - /** - * Run a dictionary map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param callable $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 new static($dictionary); - } - - /** - * Run an associative map over each of the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param callable $callback - * @return static - */ - public function mapWithKeys(callable $callback) - { - $result = []; - - foreach ($this->items as $key => $value) { - $assoc = $callback($value, $key); - - foreach ($assoc as $mapKey => $mapValue) { - $result[$mapKey] = $mapValue; - } - } - - return new static($result); - } - - /** - * Merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function merge($items) - { - return new static(array_merge($this->items, $this->getArrayableItems($items))); - } - - /** - * Recursively merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function mergeRecursive($items) - { - return new static(array_merge_recursive($this->items, $this->getArrayableItems($items))); - } - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @param mixed $values - * @return static - */ - public function combine($values) - { - return new static(array_combine($this->all(), $this->getArrayableItems($values))); - } - - /** - * Union the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function union($items) - { - return new static($this->items + $this->getArrayableItems($items)); - } - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return static - */ - public function nth($step, $offset = 0) - { - $new = []; - - $position = 0; - - foreach ($this->items as $item) { - if ($position % $step === $offset) { - $new[] = $item; - } - - $position++; - } - - return new static($new); - } - - /** - * Get the items with the specified keys. - * - * @param mixed $keys - * @return static - */ - public function only($keys) - { - if (is_null($keys)) { - return new static($this->items); - } - - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } - - $keys = is_array($keys) ? $keys : func_get_args(); - - return new static(Arr::only($this->items, $keys)); - } - - /** - * Get and remove the last item from the collection. - * - * @return mixed - */ - public function pop() - { - return array_pop($this->items); - } - - /** - * Push an item onto the beginning of the collection. - * - * @param mixed $value - * @param mixed $key - * @return $this - */ - public function prepend($value, $key = null) - { - $this->items = Arr::prepend($this->items, $value, $key); - - return $this; - } - - /** - * Push one or more items onto the end of the collection. - * - * @param mixed $values [optional] - * @return $this - */ - public function push(...$values) - { - foreach ($values as $value) { - $this->items[] = $value; - } - - return $this; - } - - /** - * Push all of the given items onto the collection. - * - * @param iterable $source - * @return static - */ - public function concat($source) - { - $result = new static($this); - - foreach ($source as $item) { - $result->push($item); - } - - return $result; - } - - /** - * Get and remove an item from the collection. - * - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function pull($key, $default = null) - { - return Arr::pull($this->items, $key, $default); - } - - /** - * Put an item in the collection by key. - * - * @param mixed $key - * @param mixed $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 int|null $number - * @return static|mixed - * - * @throws \InvalidArgumentException - */ - public function random($number = null) - { - if (is_null($number)) { - return Arr::random($this->items); - } - - return new static(Arr::random($this->items, $number)); - } - - /** - * Reduce the collection to a single value. - * - * @param callable $callback - * @param mixed $initial - * @return mixed - */ - public function reduce(callable $callback, $initial = null) - { - return array_reduce($this->items, $callback, $initial); - } - - /** - * Replace the collection items with the given items. - * - * @param mixed $items - * @return static - */ - public function replace($items) - { - return new static(array_replace($this->items, $this->getArrayableItems($items))); - } - - /** - * Recursively replace the collection items with the given items. - * - * @param mixed $items - * @return static - */ - public function replaceRecursive($items) - { - return new static(array_replace_recursive($this->items, $this->getArrayableItems($items))); - } - - /** - * Reverse items order. - * - * @return static - */ - public function reverse() - { - return new static(array_reverse($this->items, true)); - } - - /** - * Search the collection for a given value and return the corresponding key if successful. - * - * @param mixed $value - * @param bool $strict - * @return mixed - */ - public function search($value, $strict = false) - { - if (! $this->useAsCallable($value)) { - return array_search($value, $this->items, $strict); - } - - foreach ($this->items as $key => $item) { - if ($value($item, $key)) { - return $key; - } - } - - return false; - } - - /** - * Get and remove the first item from the collection. - * - * @return mixed - */ - public function shift() - { - return array_shift($this->items); - } - - /** - * Shuffle the items in the collection. - * - * @param int|null $seed - * @return static - */ - public function shuffle($seed = null) - { - return new static(Arr::shuffle($this->items, $seed)); - } - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($count) - { - return $this->slice($count); - } - - /** - * Slice the underlying collection array. - * - * @param int $offset - * @param int|null $length - * @return static - */ - public function slice($offset, $length = null) - { - return new static(array_slice($this->items, $offset, $length, true)); - } - - /** - * Split a collection into a certain number of groups. - * - * @param int $numberOfGroups - * @return static - */ - public function split($numberOfGroups) - { - if ($this->isEmpty()) { - return new static; - } - - $groups = new static; - - $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(new static(array_slice($this->items, $start, $size))); - - $start += $size; - } - } - - return $groups; - } - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @return static - */ - public function chunk($size) - { - if ($size <= 0) { - return new static; - } - - $chunks = []; - - foreach (array_chunk($this->items, $size, true) as $chunk) { - $chunks[] = new static($chunk); - } - - return new static($chunks); - } - - /** - * Sort through each item with a callback. - * - * @param callable|null $callback - * @return static - */ - public function sort($callback = null) - { - $items = $this->items; - - $callback && is_callable($callback) - ? uasort($items, $callback) - : asort($items, $callback); - - return new static($items); - } - - /** - * Sort items in descending order. - * - * @param int $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR) - { - $items = $this->items; - - arsort($items, $options); - - return new static($items); - } - - /** - * Sort the collection using the given callback. - * - * @param callable|string $callback - * @param int $options - * @param bool $descending - * @return static - */ - public function sortBy($callback, $options = SORT_REGULAR, $descending = false) - { - $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 - // and grab the corresponding values for the sorted keys from this array. - foreach ($this->items as $key => $value) { - $results[$key] = $callback($value, $key); - } - - $descending ? arsort($results, $options) - : asort($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 new static($results); - } - - /** - * Sort the collection in descending order using the given callback. - * - * @param callable|string $callback - * @param int $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR) - { - return $this->sortBy($callback, $options, true); - } - - /** - * Sort the collection keys. - * - * @param int $options - * @param bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false) - { - $items = $this->items; - - $descending ? krsort($items, $options) : ksort($items, $options); - - return new static($items); - } - - /** - * Sort the collection keys in descending order. - * - * @param int $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR) - { - return $this->sortKeys($options, true); - } - - /** - * Splice a portion of the underlying collection array. - * - * @param int $offset - * @param int|null $length - * @param mixed $replacement - * @return static - */ - public function splice($offset, $length = null, $replacement = []) - { - if (func_num_args() === 1) { - return new static(array_splice($this->items, $offset)); - } - - return new static(array_splice($this->items, $offset, $length, $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); - } - /** - * Transform each item in the collection using a callback. + * Collect the values into a collection. * - * @param callable $callback - * @return $this - */ - public function transform(callable $callback) - { - $this->items = $this->map($callback)->all(); - - return $this; - } - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values() - { - return new static(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]] - * - * @param mixed ...$items - * @return static - */ - public function zip($items) - { - $arrayableItems = array_map(function ($items) { - return $this->getArrayableItems($items); - }, func_get_args()); - - $params = array_merge([function () { - return new static(func_get_args()); - }, $this->items], $arrayableItems); - - return new static(call_user_func_array('array_map', $params)); - } - - /** - * Pad collection to the specified length with a value. - * - * @param int $size - * @param mixed $value - * @return static - */ - public function pad($size, $value) - { - return new static(array_pad($this->items, $size, $value)); - } - - /** - * Get an iterator for the items. - * - * @return \ArrayIterator - */ - public function getIterator() - { - return new ArrayIterator($this->items); - } - - /** - * Count the number of items in the collection. - * - * @return int + * @return \Illuminate\Support\Collection */ - public function count() + public function collect() { - return count($this->items); + return new Collection($this->all()); } /** - * Add an item to the collection. + * Get a lazy collection for the items in this collection. * - * @param mixed $item - * @return $this + * @return \Illuminate\Support\LazyCollection */ - public function add($item) + public function lazy() { - $this->items[] = $item; - - return $this; + return new LazyCollection($this->items); } /** @@ -1284,53 +35,4 @@ public function toBase() { return new self($this); } - - /** - * Determine if an item exists at an offset. - * - * @param mixed $key - * @return bool - */ - public function offsetExists($key) - { - return isset($this->items[$key]); - } - - /** - * Get an item at a given offset. - * - * @param mixed $key - * @return mixed - */ - public function offsetGet($key) - { - return $this->items[$key]; - } - - /** - * Set the item at a given offset. - * - * @param mixed $key - * @param mixed $value - * @return void - */ - public function offsetSet($key, $value) - { - if (is_null($key)) { - $this->items[] = $value; - } else { - $this->items[$key] = $value; - } - } - - /** - * Unset the item at a given offset. - * - * @param string $key - * @return void - */ - public function offsetUnset($key) - { - unset($this->items[$key]); - } } diff --git a/src/Illuminate/Support/Enumerable.php b/src/Illuminate/Support/Enumerable.php index 5c512e46dc9d..a0534b92107b 100644 --- a/src/Illuminate/Support/Enumerable.php +++ b/src/Illuminate/Support/Enumerable.php @@ -2,927 +2,9 @@ namespace Illuminate\Support; -use Countable; -use Illuminate\Contracts\Support\Arrayable; -use Illuminate\Contracts\Support\Jsonable; -use IteratorAggregate; -use JsonSerializable; +use Illuminate\Collections\Enumerable as BaseEnumerable; -interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable +interface Enumerable extends BaseEnumerable { - /** - * Create a new collection instance if the value isn't one already. - * - * @param mixed $items - * @return static - */ - public static function make($items = []); - - /** - * Create a new instance by invoking the callback a given amount of times. - * - * @param int $number - * @param callable|null $callback - * @return static - */ - public static function times($number, callable $callback = null); - - /** - * Wrap the given value in a collection if applicable. - * - * @param mixed $value - * @return static - */ - public static function wrap($value); - - /** - * Get the underlying items from the given collection if applicable. - * - * @param array|static $value - * @return array - */ - public static function unwrap($value); - - /** - * Get all items in the enumerable. - * - * @return array - */ - public function all(); - - /** - * Alias for the "avg" method. - * - * @param callable|string|null $callback - * @return mixed - */ - public function average($callback = null); - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return mixed - */ - 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 mixed $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 mixed $key - * @param mixed $value - * @return bool - */ - public function containsStrict($key, $value = null); - - /** - * Get the average value of a given key. - * - * @param callable|string|null $callback - * @return mixed - */ - public function avg($callback = null); - - /** - * Determine if an item exists in the enumerable. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function contains($key, $operator = null, $value = null); - - /** - * Dump the collection and end the script. - * - * @param mixed ...$args - * @return void - */ - public function dd(...$args); - - /** - * Dump the collection. - * - * @return $this - */ - public function dump(); - - /** - * Get the items that are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diff($items); - - /** - * Get the items that are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffUsing($items, callable $callback); - - /** - * Get the items whose keys and values are not present in the given items. - * - * @param mixed $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 mixed $items - * @param callable $callback - * @return static - */ - public function diffAssocUsing($items, callable $callback); - - /** - * Get the items whose keys are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diffKeys($items); - - /** - * Get the items whose keys are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffKeysUsing($items, callable $callback); - - /** - * Retrieve duplicate items. - * - * @param callable|null $callback - * @param bool $strict - * @return static - */ - public function duplicates($callback = null, $strict = false); - - /** - * Retrieve duplicate items using strict comparison. - * - * @param callable|null $callback - * @return static - */ - public function duplicatesStrict($callback = null); - - /** - * Execute a callback over each item. - * - * @param callable $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 string|callable $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 mixed $keys - * @return static - */ - public function except($keys); - - /** - * Run a filter over each of the items. - * - * @param callable|null $callback - * @return static - */ - public function filter(callable $callback = null); - - /** - * Apply the callback if the value is truthy. - * - * @param bool $value - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function when($value, callable $callback, callable $default = null); - - /** - * Apply the callback if the collection is empty. - * - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function whenEmpty(callable $callback, callable $default = null); - - /** - * Apply the callback if the collection is not empty. - * - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function whenNotEmpty(callable $callback, callable $default = null); - - /** - * Apply the callback if the value is falsy. - * - * @param bool $value - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function unless($value, callable $callback, callable $default = null); - - /** - * Apply the callback unless the collection is empty. - * - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function unlessEmpty(callable $callback, callable $default = null); - - /** - * Apply the callback unless the collection is not empty. - * - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - 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 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 mixed $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 mixed $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 array $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 array $values - * @return static - */ - public function whereNotBetween($key, $values); - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param mixed $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 mixed $values - * @return static - */ - public function whereNotInStrict($key, $values); - - /** - * Filter the items, removing any items that don't match the given type. - * - * @param string $type - * @return static - */ - public function whereInstanceOf($type); - - /** - * Get the first item from the enumerable passing the given truth test. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - 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 mixed - */ - public function firstWhere($key, $operator = null, $value = null); - - /** - * Flip the values with their keys. - * - * @return static - */ - public function flip(); - - /** - * Get an item from the collection by key. - * - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function get($key, $default = null); - - /** - * Group an associative array by a field or using a callback. - * - * @param array|callable|string $groupBy - * @param bool $preserveKeys - * @return static - */ - public function groupBy($groupBy, $preserveKeys = false); - - /** - * Key an associative array by a field or using a callback. - * - * @param callable|string $keyBy - * @return static - */ - public function keyBy($keyBy); - - /** - * Determine if an item exists in the collection by key. - * - * @param mixed $key - * @return bool - */ - public function has($key); - - /** - * Concatenate values of a given key as a string. - * - * @param string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null); - - /** - * Intersect the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function intersect($items); - - /** - * Intersect the collection with the given items by key. - * - * @param mixed $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(); - - /** - * 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. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public function last(callable $callback = null, $default = null); - - /** - * Run a map over each of the items. - * - * @param callable $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. - * - * @param callable $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. - * - * @param callable $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. - * - * @param callable $callback - * @return static - */ - public function mapWithKeys(callable $callback); - - /** - * Map a collection and flatten the result by a single level. - * - * @param callable $callback - * @return static - */ - public function flatMap(callable $callback); - - /** - * Map the values into a new class. - * - * @param string $class - * @return static - */ - public function mapInto($class); - - /** - * Merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function merge($items); - - /** - * Recursively merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function mergeRecursive($items); - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @param mixed $values - * @return static - */ - public function combine($values); - - /** - * Union the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function union($items); - - /** - * Get the min value of a given key. - * - * @param callable|string|null $callback - * @return mixed - */ - public function min($callback = null); - - /** - * Get the max value of a given key. - * - * @param callable|string|null $callback - * @return 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 mixed $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|string $key - * @param mixed $operator - * @param mixed $value - * @return static - */ - public function partition($key, $operator = null, $value = null); - - /** - * Push all of the given items onto the collection. - * - * @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 static|mixed - * - * @throws \InvalidArgumentException - */ - public function random($number = null); - - /** - * Reduce the collection to a single value. - * - * @param callable $callback - * @param mixed $initial - * @return mixed - */ - public function reduce(callable $callback, $initial = null); - - /** - * Replace the collection items with the given items. - * - * @param mixed $items - * @return static - */ - public function replace($items); - - /** - * Recursively replace the collection items with the given items. - * - * @param mixed $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 mixed $value - * @param bool $strict - * @return mixed - */ - public function search($value, $strict = false); - - /** - * Shuffle the items in the collection. - * - * @param int|null $seed - * @return static - */ - public function shuffle($seed = null); - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($count); - - /** - * 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); - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @return static - */ - public function chunk($size); - - /** - * Sort through each item with a callback. - * - * @param callable|null|int $callback - * @return static - */ - public function sort($callback = null); - - /** - * Sort items in descending order. - * - * @param int $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR); - - /** - * Sort the collection using the given callback. - * - * @param callable|string $callback - * @param int $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 callable|string $callback - * @param int $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR); - - /** - * Sort the collection keys. - * - * @param int $options - * @param bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false); - - /** - * Sort the collection keys in descending order. - * - * @param int $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR); - - /** - * Get the sum of the given values. - * - * @param callable|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); - - /** - * Pass the collection to the given callback and then return it. - * - * @param callable $callback - * @return $this - */ - public function tap(callable $callback); - - /** - * Pass the enumerable to the given callback and return the result. - * - * @param callable $callback - * @return mixed - */ - public function pipe(callable $callback); - - /** - * 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|mixed $callback - * @return static - */ - public function reject($callback = true); - - /** - * Return only unique items from the collection array. - * - * @param string|callable|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 string|callable|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. - * - * @param int $size - * @param mixed $value - * @return static - */ - public function pad($size, $value); - - /** - * Count the number of items in the collection using a given truth test. - * - * @param callable|null $callback - * @return static - */ - public function countBy($callback = null); - - /** - * Collect the values into a collection. - * - * @return \Illuminate\Support\Collection - */ - public function collect(); - - /** - * Convert the collection to its string representation. - * - * @return string - */ - public function __toString(); - - /** - * 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/LazyCollection.php b/src/Illuminate/Support/LazyCollection.php index 22830e7d1f38..ff9c167fb12d 100644 --- a/src/Illuminate/Support/LazyCollection.php +++ b/src/Illuminate/Support/LazyCollection.php @@ -2,24 +2,11 @@ namespace Illuminate\Support; -use ArrayIterator; use Closure; -use Illuminate\Support\Traits\EnumeratesValues; -use Illuminate\Support\Traits\Macroable; -use IteratorAggregate; -use stdClass; +use Illuminate\Collections\LazyCollection as BaseLazyCollection; -class LazyCollection implements Enumerable +class LazyCollection extends BaseLazyCollection { - use EnumeratesValues, Macroable; - - /** - * The source from which to generate items. - * - * @var callable|static - */ - public $source; - /** * Create a new lazy collection instance. * @@ -28,7 +15,7 @@ class LazyCollection implements Enumerable */ public function __construct($source = null) { - if ($source instanceof Closure || $source instanceof self) { + if ($source instanceof Closure || $source instanceof self || $source instanceof BaseLazyCollection) { $this->source = $source; } elseif (is_null($source)) { $this->source = static::empty(); @@ -38,1237 +25,12 @@ public function __construct($source = null) } /** - * Create a new instance with no items. - * - * @return static - */ - public static function empty() - { - return new static([]); - } - - /** - * Create a new instance by invoking the callback a given amount of times. - * - * @param int $number - * @param callable|null $callback - * @return static - */ - public static function times($number, callable $callback = null) - { - if ($number < 1) { - return new static; - } - - $instance = new static(function () use ($number) { - for ($current = 1; $current <= $number; $current++) { - yield $current; - } - }); - - return is_null($callback) ? $instance : $instance->map($callback); - } - - /** - * Create an enumerable with the given range. - * - * @param int $from - * @param int $to - * @return static - */ - public static function range($from, $to) - { - return new static(function () use ($from, $to) { - for (; $from <= $to; $from++) { - 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 average value of a given key. - * - * @param callable|string|null $callback - * @return mixed - */ - public function avg($callback = null) - { - return $this->collect()->avg($callback); - } - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return mixed - */ - 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; - } - } - } - }); - } - - /** - * Determine if an item exists in the enumerable. - * - * @param mixed $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; - - 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())); - } - - /** - * Cross join the given iterables, returning all possible permutations. - * - * @param array ...$arrays - * @return static - */ - public function crossJoin(...$arrays) - { - return $this->passthru('crossJoin', func_get_args()); - } - - /** - * Get the items that are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diff($items) - { - return $this->passthru('diff', func_get_args()); - } - - /** - * Get the items that are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffUsing($items, callable $callback) - { - return $this->passthru('diffUsing', func_get_args()); - } - - /** - * Get the items whose keys and values are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diffAssoc($items) - { - return $this->passthru('diffAssoc', func_get_args()); - } - - /** - * Get the items whose keys and values are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffAssocUsing($items, callable $callback) - { - return $this->passthru('diffAssocUsing', func_get_args()); - } - - /** - * Get the items whose keys are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diffKeys($items) - { - return $this->passthru('diffKeys', func_get_args()); - } - - /** - * Get the items whose keys are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffKeysUsing($items, callable $callback) - { - return $this->passthru('diffKeysUsing', func_get_args()); - } - - /** - * Retrieve duplicate items. - * - * @param callable|null $callback - * @param bool $strict - * @return static - */ - public function duplicates($callback = null, $strict = false) - { - return $this->passthru('duplicates', func_get_args()); - } - - /** - * Retrieve duplicate items using strict comparison. - * - * @param callable|null $callback - * @return static - */ - public function duplicatesStrict($callback = null) - { - return $this->passthru('duplicatesStrict', func_get_args()); - } - - /** - * Get all items except for those with the specified keys. - * - * @param mixed $keys - * @return static - */ - public function except($keys) - { - return $this->passthru('except', func_get_args()); - } - - /** - * Run a filter over each of the items. - * - * @param callable|null $callback - * @return static - */ - public function filter(callable $callback = null) - { - if (is_null($callback)) { - $callback = function ($value) { - return (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. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - 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) { - yield $value => $key; - } - }); - } - - /** - * Get an item by key. - * - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function get($key, $default = null) - { - if (is_null($key)) { - return; - } - - foreach ($this as $outerKey => $outerValue) { - if ($outerKey == $key) { - return $outerValue; - } - } - - return value($default); - } - - /** - * Group an associative array by a field or using a callback. - * - * @param array|callable|string $groupBy - * @param bool $preserveKeys - * @return static - */ - public function groupBy($groupBy, $preserveKeys = false) - { - return $this->passthru('groupBy', func_get_args()); - } - - /** - * Key an associative array by a field or using a callback. - * - * @param callable|string $keyBy - * @return static - */ - public function keyBy($keyBy) - { - return new static(function () use ($keyBy) { - $keyBy = $this->valueRetriever($keyBy); - - foreach ($this as $key => $item) { - $resolvedKey = $keyBy($item, $key); - - if (is_object($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()); - $count = count($keys); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $keys) && --$count == 0) { - return true; - } - } - - return false; - } - - /** - * Concatenate values of a given key as a string. - * - * @param string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null) - { - return $this->collect()->implode(...func_get_args()); - } - - /** - * Intersect the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function intersect($items) - { - return $this->passthru('intersect', func_get_args()); - } - - /** - * Intersect the collection with the given items by key. - * - * @param mixed $items - * @return static - */ - public function intersectByKeys($items) - { - return $this->passthru('intersectByKeys', func_get_args()); - } - - /** - * Determine if the items is empty or not. - * - * @return bool - */ - public function isEmpty() - { - return ! $this->getIterator()->valid(); - } - - /** - * 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. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - 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 = data_get($item, $value); - - if (is_null($key)) { - yield $itemValue; - } else { - $itemKey = 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. - * - * @param callable $callback - * @return static - */ - public function map(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - yield $key => $callback($value, $key); - } - }); - } - - /** - * Run a dictionary map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param callable $callback - * @return static - */ - public function mapToDictionary(callable $callback) - { - return $this->passthru('mapToDictionary', 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. - * - * @param callable $callback - * @return static - */ - public function mapWithKeys(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - yield from $callback($value, $key); - } - }); - } - - /** - * Merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function merge($items) - { - return $this->passthru('merge', func_get_args()); - } - - /** - * Recursively merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function mergeRecursive($items) - { - return $this->passthru('mergeRecursive', func_get_args()); - } - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @param mixed $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); - } - }); - } - - /** - * Union the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function union($items) - { - return $this->passthru('union', func_get_args()); - } - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return static - */ - public function nth($step, $offset = 0) - { - return new static(function () use ($step, $offset) { - $position = 0; - - foreach ($this as $item) { - if ($position % $step === $offset) { - yield $item; - } - - $position++; - } - }); - } - - /** - * Get the items with the specified keys. - * - * @param mixed $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; - } - } - } - } - }); - } - - /** - * Push all of the given items onto the collection. - * - * @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 - * @return static|mixed - * - * @throws \InvalidArgumentException - */ - public function random($number = null) - { - $result = $this->collect()->random(...func_get_args()); - - return is_null($number) ? $result : new static($result); - } - - /** - * Reduce the collection to a single value. - * - * @param callable $callback - * @param mixed $initial - * @return mixed - */ - public function reduce(callable $callback, $initial = null) - { - $result = $initial; - - foreach ($this as $value) { - $result = $callback($result, $value); - } - - return $result; - } - - /** - * Replace the collection items with the given items. - * - * @param mixed $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; - } - }); - } - - /** - * Recursively replace the collection items with the given items. - * - * @param mixed $items - * @return static - */ - public function replaceRecursive($items) - { - return $this->passthru('replaceRecursive', func_get_args()); - } - - /** - * Reverse items order. - * - * @return static - */ - public function reverse() - { - return $this->passthru('reverse', func_get_args()); - } - - /** - * Search the collection for a given value and return the corresponding key if successful. - * - * @param mixed $value - * @param bool $strict - * @return mixed - */ - public function search($value, $strict = false) - { - $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; - } - - /** - * Shuffle the items in the collection. - * - * @param int|null $seed - * @return static - */ - public function shuffle($seed = null) - { - return $this->passthru('shuffle', func_get_args()); - } - - /** - * 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(); - } - }); - } - - /** - * Get a slice of items from the enumerable. - * - * @param int $offset - * @param int|null $length - * @return static - */ - public function slice($offset, $length = null) - { - if ($offset < 0 || $length < 0) { - return $this->passthru('slice', func_get_args()); - } - - $instance = $this->skip($offset); - - return is_null($length) ? $instance : $instance->take($length); - } - - /** - * Split a collection into a certain number of groups. - * - * @param int $numberOfGroups - * @return static - */ - public function split($numberOfGroups) - { - return $this->passthru('split', func_get_args()); - } - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @return static - */ - public function chunk($size) - { - if ($size <= 0) { - return static::empty(); - } - - return new static(function () use ($size) { - $iterator = $this->getIterator(); - - while ($iterator->valid()) { - $chunk = []; - - while (true) { - $chunk[$iterator->key()] = $iterator->current(); - - if (count($chunk) < $size) { - $iterator->next(); - - if (! $iterator->valid()) { - break; - } - } else { - break; - } - } - - yield new static($chunk); - - $iterator->next(); - } - }); - } - - /** - * Sort through each item with a callback. - * - * @param callable|null|int $callback - * @return static - */ - public function sort($callback = null) - { - return $this->passthru('sort', func_get_args()); - } - - /** - * Sort items in descending order. - * - * @param int $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR) - { - return $this->passthru('sortDesc', func_get_args()); - } - - /** - * Sort the collection using the given callback. - * - * @param callable|string $callback - * @param int $options - * @param bool $descending - * @return static - */ - public function sortBy($callback, $options = SORT_REGULAR, $descending = false) - { - return $this->passthru('sortBy', func_get_args()); - } - - /** - * Sort the collection in descending order using the given callback. - * - * @param callable|string $callback - * @param int $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR) - { - return $this->passthru('sortByDesc', func_get_args()); - } - - /** - * Sort the collection keys. - * - * @param int $options - * @param bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false) - { - return $this->passthru('sortKeys', func_get_args()); - } - - /** - * Sort the collection keys in descending order. - * - * @param int $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR) - { - return $this->passthru('sortKeysDesc', func_get_args()); - } - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit) - { - if ($limit < 0) { - return $this->passthru('take', func_get_args()); - } - - return new static(function () use ($limit) { - $iterator = $this->getIterator(); - - while ($limit--) { - if (! $iterator->valid()) { - break; - } - - yield $iterator->key() => $iterator->current(); - - if ($limit) { - $iterator->next(); - } - } - }); - } - - /** - * Pass each item in the collection to the given callback, lazily. - * - * @param callable $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; - } - }); - } - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values() - { - return new static(function () { - foreach ($this as $item) { - yield $item; - } - }); - } - - /** - * 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]] - * - * @param mixed ...$items - * @return static - */ - public function zip($items) - { - $iterables = func_get_args(); - - return new static(function () use ($iterables) { - $iterators = Collection::make($iterables)->map(function ($iterable) { - return $this->makeIterator($iterable); - })->prepend($this->getIterator()); - - while ($iterators->contains->valid()) { - yield new static($iterators->map->current()); - - $iterators->each->next(); - } - }); - } - - /** - * Pad collection to the specified length with a value. - * - * @param int $size - * @param mixed $value - * @return static - */ - public function pad($size, $value) - { - if ($size < 0) { - return $this->passthru('pad', 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() - { - return $this->makeIterator($this->source); - } - - /** - * Count the number of items in the collection. - * - * @return int - */ - public function count() - { - if (is_array($this->source)) { - return count($this->source); - } - - return iterator_count($this->getIterator()); - } - - /** - * Make an iterator from the given source. - * - * @param mixed $source - * @return \Traversable - */ - protected function makeIterator($source) - { - if ($source instanceof IteratorAggregate) { - return $source->getIterator(); - } - - if (is_array($source)) { - return new ArrayIterator($source); - } - - return $source(); - } - - /** - * Explode the "value" and "key" arguments passed to "pluck". - * - * @param string|array $value - * @param string|array|null $key - * @return array - */ - protected function explodePluckParameters($value, $key) - { - $value = is_string($value) ? explode('.', $value) : $value; - - $key = is_null($key) || is_array($key) ? $key : explode('.', $key); - - return [$value, $key]; - } - - /** - * Pass this lazy collection through a method on the collection class. + * Collect the values into a collection. * - * @param string $method - * @param array $params - * @return static + * @return \Illuminate\Support\Collection */ - protected function passthru($method, array $params) + public function collect() { - return new static(function () use ($method, $params) { - yield from $this->collect()->$method(...$params); - }); + return new Collection($this->all()); } } diff --git a/src/Illuminate/Support/Traits/Macroable.php b/src/Illuminate/Support/Traits/Macroable.php index 0c2112c7fba5..8c5b7ae206fc 100644 --- a/src/Illuminate/Support/Traits/Macroable.php +++ b/src/Illuminate/Support/Traits/Macroable.php @@ -2,115 +2,9 @@ namespace Illuminate\Support\Traits; -use BadMethodCallException; -use Closure; -use ReflectionClass; -use ReflectionMethod; +use Illuminate\Macroable\Macroable as BaseMacroable; trait Macroable { - /** - * The registered string macros. - * - * @var array - */ - protected static $macros = []; - - /** - * Register a custom macro. - * - * @param string $name - * @param object|callable $macro - * @return void - */ - public static function macro($name, $macro) - { - static::$macros[$name] = $macro; - } - - /** - * Mix another object into the class. - * - * @param object $mixin - * @param bool $replace - * @return void - * - * @throws \ReflectionException - */ - public static function mixin($mixin, $replace = true) - { - $methods = (new ReflectionClass($mixin))->getMethods( - ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED - ); - - foreach ($methods as $method) { - if ($replace || ! static::hasMacro($method->name)) { - $method->setAccessible(true); - 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]); - } - - /** - * 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) { - return call_user_func_array(Closure::bind($macro, null, static::class), $parameters); - } - - 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) { - return call_user_func_array($macro->bindTo($this, static::class), $parameters); - } - - return $macro(...$parameters); - } + use BaseMacroable; } diff --git a/src/Illuminate/Support/composer.json b/src/Illuminate/Support/composer.json index 2adac0992c71..b492a7110fe2 100644 --- a/src/Illuminate/Support/composer.json +++ b/src/Illuminate/Support/composer.json @@ -18,7 +18,9 @@ "ext-json": "*", "ext-mbstring": "*", "doctrine/inflector": "^1.1", + "illuminate/collections": "^8.0", "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", "nesbot/carbon": "^2.17", "voku/portable-ascii": "^1.4.8" }, From 9c96b4636848a80ca81831b3672b06e3451486aa Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Apr 2020 11:32:47 -0500 Subject: [PATCH 2/3] Apply fixes from StyleCI (#32477) --- src/Illuminate/Collections/EnumeratesValues.php | 3 --- src/Illuminate/Support/Collection.php | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Illuminate/Collections/EnumeratesValues.php b/src/Illuminate/Collections/EnumeratesValues.php index 1682c0f1ddd5..b1dceab60491 100644 --- a/src/Illuminate/Collections/EnumeratesValues.php +++ b/src/Illuminate/Collections/EnumeratesValues.php @@ -6,9 +6,6 @@ use Exception; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Contracts\Support\Jsonable; -use Illuminate\Collections\Arr; -use Illuminate\Collections\Enumerable; -use Illuminate\Collections\HigherOrderCollectionProxy; use JsonSerializable; use Symfony\Component\VarDumper\VarDumper; use Traversable; diff --git a/src/Illuminate/Support/Collection.php b/src/Illuminate/Support/Collection.php index 47a674edc7b6..6fc9f640e928 100644 --- a/src/Illuminate/Support/Collection.php +++ b/src/Illuminate/Support/Collection.php @@ -13,7 +13,7 @@ class Collection extends BaseCollection */ public function collect() { - return new Collection($this->all()); + return new self($this->all()); } /** From c3b8ee2bb85acdc9acd03d15e7f9d90c184369ec Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Apr 2020 11:40:26 -0500 Subject: [PATCH 3/3] move when proxy --- .../{Support => Collections}/HigherOrderWhenProxy.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/Illuminate/{Support => Collections}/HigherOrderWhenProxy.php (85%) diff --git a/src/Illuminate/Support/HigherOrderWhenProxy.php b/src/Illuminate/Collections/HigherOrderWhenProxy.php similarity index 85% rename from src/Illuminate/Support/HigherOrderWhenProxy.php rename to src/Illuminate/Collections/HigherOrderWhenProxy.php index 6653c03a656f..5d2853c17d22 100644 --- a/src/Illuminate/Support/HigherOrderWhenProxy.php +++ b/src/Illuminate/Collections/HigherOrderWhenProxy.php @@ -1,16 +1,16 @@