package and depencies
This commit is contained in:
		| @@ -14,6 +14,13 @@ class AuthorizationException extends Exception | ||||
|      */ | ||||
|     protected $response; | ||||
|  | ||||
|     /** | ||||
|      * The HTTP response status code. | ||||
|      * | ||||
|      * @var int|null | ||||
|      */ | ||||
|     protected $status; | ||||
|  | ||||
|     /** | ||||
|      * Create a new authorization exception instance. | ||||
|      * | ||||
| @@ -52,6 +59,49 @@ class AuthorizationException extends Exception | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the HTTP response status code. | ||||
|      * | ||||
|      * @param  int|null  $status | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function withStatus($status) | ||||
|     { | ||||
|         $this->status = $status; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the HTTP response status code to 404. | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function asNotFound() | ||||
|     { | ||||
|         return $this->withStatus(404); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the HTTP status code has been set. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function hasStatus() | ||||
|     { | ||||
|         return $this->status !== null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the HTTP status code. | ||||
|      * | ||||
|      * @return int|null | ||||
|      */ | ||||
|     public function status() | ||||
|     { | ||||
|         return $this->status; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a deny response object from this exception. | ||||
|      * | ||||
| @@ -59,6 +109,6 @@ class AuthorizationException extends Exception | ||||
|      */ | ||||
|     public function toResponse() | ||||
|     { | ||||
|         return Response::deny($this->message, $this->code); | ||||
|         return Response::deny($this->message, $this->code)->withStatus($this->status); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ namespace Illuminate\Auth\Access; | ||||
|  | ||||
| use Closure; | ||||
| use Exception; | ||||
| use Illuminate\Auth\Access\Events\GateEvaluated; | ||||
| use Illuminate\Contracts\Auth\Access\Gate as GateContract; | ||||
| use Illuminate\Contracts\Container\Container; | ||||
| use Illuminate\Contracts\Events\Dispatcher; | ||||
| @@ -180,7 +181,7 @@ class Gate implements GateContract | ||||
|      * Define a new ability. | ||||
|      * | ||||
|      * @param  string  $ability | ||||
|      * @param  callable|string  $callback | ||||
|      * @param  callable|array|string  $callback | ||||
|      * @return $this | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
| @@ -198,7 +199,7 @@ class Gate implements GateContract | ||||
|  | ||||
|             $this->abilities[$ability] = $this->buildAbilityCallback($ability, $callback); | ||||
|         } else { | ||||
|             throw new InvalidArgumentException("Callback must be a callable or a 'Class@method' string."); | ||||
|             throw new InvalidArgumentException("Callback must be a callable, callback array, or a 'Class@method' string."); | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
| @@ -239,7 +240,7 @@ class Gate implements GateContract | ||||
|     protected function buildAbilityCallback($ability, $callback) | ||||
|     { | ||||
|         return function () use ($ability, $callback) { | ||||
|             if (Str::contains($callback, '@')) { | ||||
|             if (str_contains($callback, '@')) { | ||||
|                 [$class, $method] = Str::parseCallback($callback); | ||||
|             } else { | ||||
|                 $class = $callback; | ||||
| @@ -338,9 +339,9 @@ class Gate implements GateContract | ||||
|      */ | ||||
|     public function check($abilities, $arguments = []) | ||||
|     { | ||||
|         return collect($abilities)->every(function ($ability) use ($arguments) { | ||||
|             return $this->inspect($ability, $arguments)->allowed(); | ||||
|         }); | ||||
|         return collect($abilities)->every( | ||||
|             fn ($ability) => $this->inspect($ability, $arguments)->allowed() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -352,9 +353,7 @@ class Gate implements GateContract | ||||
|      */ | ||||
|     public function any($abilities, $arguments = []) | ||||
|     { | ||||
|         return collect($abilities)->contains(function ($ability) use ($arguments) { | ||||
|             return $this->check($ability, $arguments); | ||||
|         }); | ||||
|         return collect($abilities)->contains(fn ($ability) => $this->check($ability, $arguments)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -575,7 +574,7 @@ class Gate implements GateContract | ||||
|  | ||||
|             $afterResult = $after($user, $ability, $result, $arguments); | ||||
|  | ||||
|             $result = $result ?? $afterResult; | ||||
|             $result ??= $afterResult; | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
| @@ -594,7 +593,7 @@ class Gate implements GateContract | ||||
|     { | ||||
|         if ($this->container->bound(Dispatcher::class)) { | ||||
|             $this->container->make(Dispatcher::class)->dispatch( | ||||
|                 new Events\GateEvaluated($user, $ability, $result, $arguments) | ||||
|                 new GateEvaluated($user, $ability, $result, $arguments) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| @@ -808,7 +807,7 @@ class Gate implements GateContract | ||||
|      */ | ||||
|     protected function formatAbilityToMethod($ability) | ||||
|     { | ||||
|         return strpos($ability, '-') !== false ? Str::camel($ability) : $ability; | ||||
|         return str_contains($ability, '-') ? Str::camel($ability) : $ability; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -819,9 +818,7 @@ class Gate implements GateContract | ||||
|      */ | ||||
|     public function forUser($user) | ||||
|     { | ||||
|         $callback = function () use ($user) { | ||||
|             return $user; | ||||
|         }; | ||||
|         $callback = fn () => $user; | ||||
|  | ||||
|         return new static( | ||||
|             $this->container, $callback, $this->abilities, | ||||
|   | ||||
| @@ -27,4 +27,29 @@ trait HandlesAuthorization | ||||
|     { | ||||
|         return Response::deny($message, $code); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Deny with a HTTP status code. | ||||
|      * | ||||
|      * @param  int  $status | ||||
|      * @param  ?string  $message | ||||
|      * @param  ?int  $code | ||||
|      * @return \Illuminate\Auth\Access\Response | ||||
|      */ | ||||
|     public function denyWithStatus($status, $message = null, $code = null) | ||||
|     { | ||||
|         return Response::denyWithStatus($status, $message, $code); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Deny with a 404 HTTP status code. | ||||
|      * | ||||
|      * @param  ?string  $message | ||||
|      * @param  ?int  $code | ||||
|      * @return \Illuminate\Auth\Access\Response | ||||
|      */ | ||||
|     public function denyAsNotFound($message = null, $code = null) | ||||
|     { | ||||
|         return Response::denyWithStatus(404, $message, $code); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -27,6 +27,13 @@ class Response implements Arrayable | ||||
|      */ | ||||
|     protected $code; | ||||
|  | ||||
|     /** | ||||
|      * The HTTP response status code. | ||||
|      * | ||||
|      * @var int|null | ||||
|      */ | ||||
|     protected $status; | ||||
|  | ||||
|     /** | ||||
|      * Create a new response. | ||||
|      * | ||||
| @@ -66,6 +73,31 @@ class Response implements Arrayable | ||||
|         return new static(false, $message, $code); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new "deny" Response with a HTTP status code. | ||||
|      * | ||||
|      * @param  int  $status | ||||
|      * @param  string|null  $message | ||||
|      * @param  mixed  $code | ||||
|      * @return \Illuminate\Auth\Access\Response | ||||
|      */ | ||||
|     public static function denyWithStatus($status, $message = null, $code = null) | ||||
|     { | ||||
|         return static::deny($message, $code)->withStatus($status); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new "deny" Response with a 404 HTTP status code. | ||||
|      * | ||||
|      * @param  string|null  $message | ||||
|      * @param  mixed  $code | ||||
|      * @return \Illuminate\Auth\Access\Response | ||||
|      */ | ||||
|     public static function denyAsNotFound($message = null, $code = null) | ||||
|     { | ||||
|         return static::denyWithStatus(404, $message, $code); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the response was allowed. | ||||
|      * | ||||
| @@ -117,12 +149,46 @@ class Response implements Arrayable | ||||
|     { | ||||
|         if ($this->denied()) { | ||||
|             throw (new AuthorizationException($this->message(), $this->code())) | ||||
|                         ->setResponse($this); | ||||
|                 ->setResponse($this) | ||||
|                 ->withStatus($this->status); | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the HTTP response status code. | ||||
|      * | ||||
|      * @param  null|int  $status | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function withStatus($status) | ||||
|     { | ||||
|         $this->status = $status; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the HTTP response status code to 404. | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function asNotFound() | ||||
|     { | ||||
|         return $this->withStatus(404); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the HTTP status code. | ||||
|      * | ||||
|      * @return int|null | ||||
|      */ | ||||
|     public function status() | ||||
|     { | ||||
|         return $this->status; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert the response to an array. | ||||
|      * | ||||
|   | ||||
| @@ -6,6 +6,10 @@ use Closure; | ||||
| use Illuminate\Contracts\Auth\Factory as FactoryContract; | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| /** | ||||
|  * @mixin \Illuminate\Contracts\Auth\Guard | ||||
|  * @mixin \Illuminate\Contracts\Auth\StatefulGuard | ||||
|  */ | ||||
| class AuthManager implements FactoryContract | ||||
| { | ||||
|     use CreatesUserProviders; | ||||
| @@ -50,9 +54,7 @@ class AuthManager implements FactoryContract | ||||
|     { | ||||
|         $this->app = $app; | ||||
|  | ||||
|         $this->userResolver = function ($guard = null) { | ||||
|             return $this->guard($guard)->user(); | ||||
|         }; | ||||
|         $this->userResolver = fn ($guard = null) => $this->guard($guard)->user(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -208,9 +210,7 @@ class AuthManager implements FactoryContract | ||||
|  | ||||
|         $this->setDefaultDriver($name); | ||||
|  | ||||
|         $this->userResolver = function ($name = null) { | ||||
|             return $this->guard($name)->user(); | ||||
|         }; | ||||
|         $this->userResolver = fn ($name = null) => $this->guard($name)->user(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -34,13 +34,9 @@ class AuthServiceProvider extends ServiceProvider | ||||
|      */ | ||||
|     protected function registerAuthenticator() | ||||
|     { | ||||
|         $this->app->singleton('auth', function ($app) { | ||||
|             return new AuthManager($app); | ||||
|         }); | ||||
|         $this->app->singleton('auth', fn ($app) => new AuthManager($app)); | ||||
|  | ||||
|         $this->app->singleton('auth.driver', function ($app) { | ||||
|             return $app['auth']->guard(); | ||||
|         }); | ||||
|         $this->app->singleton('auth.driver', fn ($app) => $app['auth']->guard()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -50,9 +46,7 @@ class AuthServiceProvider extends ServiceProvider | ||||
|      */ | ||||
|     protected function registerUserResolver() | ||||
|     { | ||||
|         $this->app->bind(AuthenticatableContract::class, function ($app) { | ||||
|             return call_user_func($app['auth']->userResolver()); | ||||
|         }); | ||||
|         $this->app->bind(AuthenticatableContract::class, fn ($app) => call_user_func($app['auth']->userResolver())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -63,9 +57,7 @@ class AuthServiceProvider extends ServiceProvider | ||||
|     protected function registerAccessGate() | ||||
|     { | ||||
|         $this->app->singleton(GateContract::class, function ($app) { | ||||
|             return new Gate($app, function () use ($app) { | ||||
|                 return call_user_func($app['auth']->userResolver()); | ||||
|             }); | ||||
|             return new Gate($app, fn () => call_user_func($app['auth']->userResolver())); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,9 @@ | ||||
| namespace Illuminate\Auth\Console; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
|  | ||||
| #[AsCommand(name: 'auth:clear-resets')] | ||||
| class ClearResetsCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -13,6 +15,17 @@ class ClearResetsCommand extends Command | ||||
|      */ | ||||
|     protected $signature = 'auth:clear-resets {name? : The name of the password broker}'; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'auth:clear-resets'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
| @@ -29,6 +42,6 @@ class ClearResetsCommand extends Command | ||||
|     { | ||||
|         $this->laravel['auth.password']->broker($this->argument('name'))->getRepository()->deleteExpired(); | ||||
|  | ||||
|         $this->info('Expired reset tokens cleared!'); | ||||
|         $this->components->info('Expired reset tokens cleared successfully.'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,10 +12,6 @@ | ||||
|     <!-- Scripts --> | ||||
|     <script src="{{ asset('js/app.js') }}" defer></script> | ||||
|  | ||||
|     <!-- Fonts --> | ||||
|     <link rel="preconnect" href="https://fonts.gstatic.com"> | ||||
|     <link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet"> | ||||
|  | ||||
|     <!-- Styles --> | ||||
|     <link href="{{ asset('css/app.css') }}" rel="stylesheet"> | ||||
| </head> | ||||
|   | ||||
| @@ -33,16 +33,13 @@ trait CreatesUserProviders | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         switch ($driver) { | ||||
|             case 'database': | ||||
|                 return $this->createDatabaseProvider($config); | ||||
|             case 'eloquent': | ||||
|                 return $this->createEloquentProvider($config); | ||||
|             default: | ||||
|                 throw new InvalidArgumentException( | ||||
|                     "Authentication user provider [{$driver}] is not defined." | ||||
|                 ); | ||||
|         } | ||||
|         return match ($driver) { | ||||
|             'database' => $this->createDatabaseProvider($config), | ||||
|             'eloquent' => $this->createEloquentProvider($config), | ||||
|             default => throw new InvalidArgumentException( | ||||
|                 "Authentication user provider [{$driver}] is not defined." | ||||
|             ), | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -8,7 +8,6 @@ use Illuminate\Contracts\Auth\UserProvider; | ||||
| use Illuminate\Contracts\Hashing\Hasher as HasherContract; | ||||
| use Illuminate\Contracts\Support\Arrayable; | ||||
| use Illuminate\Database\ConnectionInterface; | ||||
| use Illuminate\Support\Str; | ||||
|  | ||||
| class DatabaseUserProvider implements UserProvider | ||||
| { | ||||
| @@ -17,7 +16,7 @@ class DatabaseUserProvider implements UserProvider | ||||
|      * | ||||
|      * @var \Illuminate\Database\ConnectionInterface | ||||
|      */ | ||||
|     protected $conn; | ||||
|     protected $connection; | ||||
|  | ||||
|     /** | ||||
|      * The hasher implementation. | ||||
| @@ -36,14 +35,14 @@ class DatabaseUserProvider implements UserProvider | ||||
|     /** | ||||
|      * Create a new database user provider. | ||||
|      * | ||||
|      * @param  \Illuminate\Database\ConnectionInterface  $conn | ||||
|      * @param  \Illuminate\Database\ConnectionInterface  $connection | ||||
|      * @param  \Illuminate\Contracts\Hashing\Hasher  $hasher | ||||
|      * @param  string  $table | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(ConnectionInterface $conn, HasherContract $hasher, $table) | ||||
|     public function __construct(ConnectionInterface $connection, HasherContract $hasher, $table) | ||||
|     { | ||||
|         $this->conn = $conn; | ||||
|         $this->connection = $connection; | ||||
|         $this->table = $table; | ||||
|         $this->hasher = $hasher; | ||||
|     } | ||||
| @@ -56,7 +55,7 @@ class DatabaseUserProvider implements UserProvider | ||||
|      */ | ||||
|     public function retrieveById($identifier) | ||||
|     { | ||||
|         $user = $this->conn->table($this->table)->find($identifier); | ||||
|         $user = $this->connection->table($this->table)->find($identifier); | ||||
|  | ||||
|         return $this->getGenericUser($user); | ||||
|     } | ||||
| @@ -71,7 +70,7 @@ class DatabaseUserProvider implements UserProvider | ||||
|     public function retrieveByToken($identifier, $token) | ||||
|     { | ||||
|         $user = $this->getGenericUser( | ||||
|             $this->conn->table($this->table)->find($identifier) | ||||
|             $this->connection->table($this->table)->find($identifier) | ||||
|         ); | ||||
|  | ||||
|         return $user && $user->getRememberToken() && hash_equals($user->getRememberToken(), $token) | ||||
| @@ -87,7 +86,7 @@ class DatabaseUserProvider implements UserProvider | ||||
|      */ | ||||
|     public function updateRememberToken(UserContract $user, $token) | ||||
|     { | ||||
|         $this->conn->table($this->table) | ||||
|         $this->connection->table($this->table) | ||||
|                 ->where($user->getAuthIdentifierName(), $user->getAuthIdentifier()) | ||||
|                 ->update([$user->getRememberTokenName() => $token]); | ||||
|     } | ||||
| @@ -100,22 +99,22 @@ class DatabaseUserProvider implements UserProvider | ||||
|      */ | ||||
|     public function retrieveByCredentials(array $credentials) | ||||
|     { | ||||
|         if (empty($credentials) || | ||||
|            (count($credentials) === 1 && | ||||
|             array_key_exists('password', $credentials))) { | ||||
|         $credentials = array_filter( | ||||
|             $credentials, | ||||
|             fn ($key) => ! str_contains($key, 'password'), | ||||
|             ARRAY_FILTER_USE_KEY | ||||
|         ); | ||||
|  | ||||
|         if (empty($credentials)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // First we will add each credential element to the query as a where clause. | ||||
|         // Then we can execute the query and, if we found a user, return it in a | ||||
|         // generic "user" object that will be utilized by the Guard instances. | ||||
|         $query = $this->conn->table($this->table); | ||||
|         $query = $this->connection->table($this->table); | ||||
|  | ||||
|         foreach ($credentials as $key => $value) { | ||||
|             if (Str::contains($key, 'password')) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (is_array($value) || $value instanceof Arrayable) { | ||||
|                 $query->whereIn($key, $value); | ||||
|             } elseif ($value instanceof Closure) { | ||||
| @@ -125,9 +124,9 @@ class DatabaseUserProvider implements UserProvider | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Now we are ready to execute the query to see if we have an user matching | ||||
|         // the given credentials. If not, we will just return nulls and indicate | ||||
|         // that there are no matching users for these given credential arrays. | ||||
|         // Now we are ready to execute the query to see if we have a user matching | ||||
|         // the given credentials. If not, we will just return null and indicate | ||||
|         // that there are no matching users from the given credential arrays. | ||||
|         $user = $query->first(); | ||||
|  | ||||
|         return $this->getGenericUser($user); | ||||
|   | ||||
| @@ -7,7 +7,6 @@ use Illuminate\Contracts\Auth\Authenticatable as UserContract; | ||||
| use Illuminate\Contracts\Auth\UserProvider; | ||||
| use Illuminate\Contracts\Hashing\Hasher as HasherContract; | ||||
| use Illuminate\Contracts\Support\Arrayable; | ||||
| use Illuminate\Support\Str; | ||||
|  | ||||
| class EloquentUserProvider implements UserProvider | ||||
| { | ||||
| @@ -25,6 +24,13 @@ class EloquentUserProvider implements UserProvider | ||||
|      */ | ||||
|     protected $model; | ||||
|  | ||||
|     /** | ||||
|      * The callback that may modify the user retrieval queries. | ||||
|      * | ||||
|      * @var (\Closure(\Illuminate\Database\Eloquent\Builder):mixed)|null | ||||
|      */ | ||||
|     protected $queryCallback; | ||||
|  | ||||
|     /** | ||||
|      * Create a new database user provider. | ||||
|      * | ||||
| @@ -74,14 +80,13 @@ class EloquentUserProvider implements UserProvider | ||||
|  | ||||
|         $rememberToken = $retrievedModel->getRememberToken(); | ||||
|  | ||||
|         return $rememberToken && hash_equals($rememberToken, $token) | ||||
|                         ? $retrievedModel : null; | ||||
|         return $rememberToken && hash_equals($rememberToken, $token) ? $retrievedModel : null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the "remember me" token for the given user in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Auth\Authenticatable|\Illuminate\Database\Eloquent\Model  $user | ||||
|      * @param  \Illuminate\Contracts\Auth\Authenticatable  $user | ||||
|      * @param  string  $token | ||||
|      * @return void | ||||
|      */ | ||||
| @@ -106,9 +111,13 @@ class EloquentUserProvider implements UserProvider | ||||
|      */ | ||||
|     public function retrieveByCredentials(array $credentials) | ||||
|     { | ||||
|         if (empty($credentials) || | ||||
|            (count($credentials) === 1 && | ||||
|             Str::contains($this->firstCredentialKey($credentials), 'password'))) { | ||||
|         $credentials = array_filter( | ||||
|             $credentials, | ||||
|             fn ($key) => ! str_contains($key, 'password'), | ||||
|             ARRAY_FILTER_USE_KEY | ||||
|         ); | ||||
|  | ||||
|         if (empty($credentials)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -118,10 +127,6 @@ class EloquentUserProvider implements UserProvider | ||||
|         $query = $this->newModelQuery(); | ||||
|  | ||||
|         foreach ($credentials as $key => $value) { | ||||
|             if (Str::contains($key, 'password')) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (is_array($value) || $value instanceof Arrayable) { | ||||
|                 $query->whereIn($key, $value); | ||||
|             } elseif ($value instanceof Closure) { | ||||
| @@ -134,19 +139,6 @@ class EloquentUserProvider implements UserProvider | ||||
|         return $query->first(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the first key from the credential array. | ||||
|      * | ||||
|      * @param  array  $credentials | ||||
|      * @return string|null | ||||
|      */ | ||||
|     protected function firstCredentialKey(array $credentials) | ||||
|     { | ||||
|         foreach ($credentials as $key => $value) { | ||||
|             return $key; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validate a user against the given credentials. | ||||
|      * | ||||
| @@ -156,7 +148,9 @@ class EloquentUserProvider implements UserProvider | ||||
|      */ | ||||
|     public function validateCredentials(UserContract $user, array $credentials) | ||||
|     { | ||||
|         $plain = $credentials['password']; | ||||
|         if (is_null($plain = $credentials['password'])) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return $this->hasher->check($plain, $user->getAuthPassword()); | ||||
|     } | ||||
| @@ -169,9 +163,13 @@ class EloquentUserProvider implements UserProvider | ||||
|      */ | ||||
|     protected function newModelQuery($model = null) | ||||
|     { | ||||
|         return is_null($model) | ||||
|         $query = is_null($model) | ||||
|                 ? $this->createModel()->newQuery() | ||||
|                 : $model->newQuery(); | ||||
|  | ||||
|         with($query, $this->queryCallback); | ||||
|  | ||||
|         return $query; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -231,4 +229,27 @@ class EloquentUserProvider implements UserProvider | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the callback that modifies the query before retrieving users. | ||||
|      * | ||||
|      * @return \Closure|null | ||||
|      */ | ||||
|     public function getQueryCallback() | ||||
|     { | ||||
|         return $this->queryCallback; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the callback to modify the query before retrieving users. | ||||
|      * | ||||
|      * @param  (\Closure(\Illuminate\Database\Eloquent\Builder):mixed)|null  $queryCallback | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function withQuery($queryCallback = null) | ||||
|     { | ||||
|         $this->queryCallback = $queryCallback; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -95,6 +95,18 @@ trait GuardHelpers | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Forget the current user. | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function forgetUser() | ||||
|     { | ||||
|         $this->user = null; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the user provider used by the guard. | ||||
|      * | ||||
|   | ||||
| @@ -75,7 +75,7 @@ class Authorize | ||||
|         if ($this->isClassName($model)) { | ||||
|             return trim($model); | ||||
|         } else { | ||||
|             return $request->route($model, null) ?: | ||||
|             return $request->route($model, null) ?? | ||||
|                 ((preg_match("/^['\"](.*)['\"]$/", trim($model), $matches)) ? $matches[1] : null); | ||||
|         } | ||||
|     } | ||||
| @@ -88,6 +88,6 @@ class Authorize | ||||
|      */ | ||||
|     protected function isClassName($value) | ||||
|     { | ||||
|         return strpos($value, '\\') !== false; | ||||
|         return str_contains($value, '\\'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -50,11 +50,12 @@ class RequirePassword | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  \Closure  $next | ||||
|      * @param  string|null  $redirectToRoute | ||||
|      * @param  int|null  $passwordTimeoutSeconds | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function handle($request, Closure $next, $redirectToRoute = null) | ||||
|     public function handle($request, Closure $next, $redirectToRoute = null, $passwordTimeoutSeconds = null) | ||||
|     { | ||||
|         if ($this->shouldConfirmPassword($request)) { | ||||
|         if ($this->shouldConfirmPassword($request, $passwordTimeoutSeconds)) { | ||||
|             if ($request->expectsJson()) { | ||||
|                 return $this->responseFactory->json([ | ||||
|                     'message' => 'Password confirmation required.', | ||||
| @@ -73,12 +74,13 @@ class RequirePassword | ||||
|      * Determine if the confirmation timeout has expired. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  int|null  $passwordTimeoutSeconds | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function shouldConfirmPassword($request) | ||||
|     protected function shouldConfirmPassword($request, $passwordTimeoutSeconds = null) | ||||
|     { | ||||
|         $confirmedAt = time() - $request->session()->get('auth.password_confirmed_at', 0); | ||||
|  | ||||
|         return $confirmedAt > $this->passwordTimeout; | ||||
|         return $confirmedAt > ($passwordTimeoutSeconds ?? $this->passwordTimeout); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -18,14 +18,14 @@ class ResetPassword extends Notification | ||||
|     /** | ||||
|      * The callback that should be used to create the reset password URL. | ||||
|      * | ||||
|      * @var \Closure|null | ||||
|      * @var (\Closure(mixed, string): string)|null | ||||
|      */ | ||||
|     public static $createUrlCallback; | ||||
|  | ||||
|     /** | ||||
|      * The callback that should be used to build the mail message. | ||||
|      * | ||||
|      * @var \Closure|null | ||||
|      * @var (\Closure(mixed, string): \Illuminate\Notifications\Messages\MailMessage)|null | ||||
|      */ | ||||
|     public static $toMailCallback; | ||||
|  | ||||
| @@ -103,7 +103,7 @@ class ResetPassword extends Notification | ||||
|     /** | ||||
|      * Set a callback that should be used when creating the reset password button URL. | ||||
|      * | ||||
|      * @param  \Closure  $callback | ||||
|      * @param  \Closure(mixed, string): string  $callback | ||||
|      * @return void | ||||
|      */ | ||||
|     public static function createUrlUsing($callback) | ||||
| @@ -114,7 +114,7 @@ class ResetPassword extends Notification | ||||
|     /** | ||||
|      * Set a callback that should be used when building the notification mail message. | ||||
|      * | ||||
|      * @param  \Closure  $callback | ||||
|      * @param  \Closure(mixed, string): \Illuminate\Notifications\Messages\MailMessage  $callback | ||||
|      * @return void | ||||
|      */ | ||||
|     public static function toMailUsing($callback) | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| namespace Illuminate\Auth\Passwords; | ||||
|  | ||||
| use Illuminate\Contracts\Auth\PasswordBrokerFactory as FactoryContract; | ||||
| use Illuminate\Support\Str; | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| /** | ||||
| @@ -84,7 +83,7 @@ class PasswordBrokerManager implements FactoryContract | ||||
|     { | ||||
|         $key = $this->app['config']['app.key']; | ||||
|  | ||||
|         if (Str::startsWith($key, 'base64:')) { | ||||
|         if (str_starts_with($key, 'base64:')) { | ||||
|             $key = base64_decode(substr($key, 7)); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -2,8 +2,6 @@ | ||||
|  | ||||
| namespace Illuminate\Auth; | ||||
|  | ||||
| use Illuminate\Support\Str; | ||||
|  | ||||
| class Recaller | ||||
| { | ||||
|     /** | ||||
| @@ -51,7 +49,7 @@ class Recaller | ||||
|      */ | ||||
|     public function hash() | ||||
|     { | ||||
|         return explode('|', $this->recaller, 3)[2]; | ||||
|         return explode('|', $this->recaller, 4)[2]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -71,7 +69,7 @@ class Recaller | ||||
|      */ | ||||
|     protected function properString() | ||||
|     { | ||||
|         return is_string($this->recaller) && Str::contains($this->recaller, '|'); | ||||
|         return is_string($this->recaller) && str_contains($this->recaller, '|'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -83,6 +81,16 @@ class Recaller | ||||
|     { | ||||
|         $segments = explode('|', $this->recaller); | ||||
|  | ||||
|         return count($segments) === 3 && trim($segments[0]) !== '' && trim($segments[1]) !== ''; | ||||
|         return count($segments) >= 3 && trim($segments[0]) !== '' && trim($segments[1]) !== ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the recaller's segments. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function segments() | ||||
|     { | ||||
|         return explode('|', $this->recaller); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -59,7 +59,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      * | ||||
|      * @var int | ||||
|      */ | ||||
|     protected $rememberDuration = 2628000; | ||||
|     protected $rememberDuration = 576000; | ||||
|  | ||||
|     /** | ||||
|      * The session used by the guard. | ||||
| @@ -287,6 +287,8 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      * @param  string  $field | ||||
|      * @param  array  $extraConditions | ||||
|      * @return \Symfony\Component\HttpFoundation\Response|null | ||||
|      * | ||||
|      * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException | ||||
|      */ | ||||
|     public function basic($field = 'email', $extraConditions = []) | ||||
|     { | ||||
| @@ -310,6 +312,8 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      * @param  string  $field | ||||
|      * @param  array  $extraConditions | ||||
|      * @return \Symfony\Component\HttpFoundation\Response|null | ||||
|      * | ||||
|      * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException | ||||
|      */ | ||||
|     public function onceBasic($field = 'email', $extraConditions = []) | ||||
|     { | ||||
| @@ -397,8 +401,8 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      * Attempt to authenticate a user with credentials and additional callbacks. | ||||
|      * | ||||
|      * @param  array  $credentials | ||||
|      * @param  array|callable  $callbacks | ||||
|      * @param  false  $remember | ||||
|      * @param  array|callable|null  $callbacks | ||||
|      * @param  bool  $remember | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function attemptWhen(array $credentials = [], $callbacks = null, $remember = false) | ||||
| @@ -624,9 +628,12 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|     { | ||||
|         $this->session->remove($this->getName()); | ||||
|  | ||||
|         $this->getCookieJar()->unqueue($this->getRecallerName()); | ||||
|  | ||||
|         if (! is_null($this->recaller())) { | ||||
|             $this->getCookieJar()->queue($this->getCookieJar() | ||||
|                     ->forget($this->getRecallerName())); | ||||
|             $this->getCookieJar()->queue( | ||||
|                 $this->getCookieJar()->forget($this->getRecallerName()) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -700,9 +707,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      */ | ||||
|     public function attempting($callback) | ||||
|     { | ||||
|         if (isset($this->events)) { | ||||
|             $this->events->listen(Events\Attempting::class, $callback); | ||||
|         } | ||||
|         $this->events?->listen(Events\Attempting::class, $callback); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -714,11 +719,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      */ | ||||
|     protected function fireAttemptEvent(array $credentials, $remember = false) | ||||
|     { | ||||
|         if (isset($this->events)) { | ||||
|             $this->events->dispatch(new Attempting( | ||||
|                 $this->name, $credentials, $remember | ||||
|             )); | ||||
|         } | ||||
|         $this->events?->dispatch(new Attempting($this->name, $credentials, $remember)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -729,11 +730,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      */ | ||||
|     protected function fireValidatedEvent($user) | ||||
|     { | ||||
|         if (isset($this->events)) { | ||||
|             $this->events->dispatch(new Validated( | ||||
|                 $this->name, $user | ||||
|             )); | ||||
|         } | ||||
|         $this->events?->dispatch(new Validated($this->name, $user)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -745,11 +742,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      */ | ||||
|     protected function fireLoginEvent($user, $remember = false) | ||||
|     { | ||||
|         if (isset($this->events)) { | ||||
|             $this->events->dispatch(new Login( | ||||
|                 $this->name, $user, $remember | ||||
|             )); | ||||
|         } | ||||
|         $this->events?->dispatch(new Login($this->name, $user, $remember)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -760,11 +753,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      */ | ||||
|     protected function fireAuthenticatedEvent($user) | ||||
|     { | ||||
|         if (isset($this->events)) { | ||||
|             $this->events->dispatch(new Authenticated( | ||||
|                 $this->name, $user | ||||
|             )); | ||||
|         } | ||||
|         $this->events?->dispatch(new Authenticated($this->name, $user)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -775,11 +764,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      */ | ||||
|     protected function fireOtherDeviceLogoutEvent($user) | ||||
|     { | ||||
|         if (isset($this->events)) { | ||||
|             $this->events->dispatch(new OtherDeviceLogout( | ||||
|                 $this->name, $user | ||||
|             )); | ||||
|         } | ||||
|         $this->events?->dispatch(new OtherDeviceLogout($this->name, $user)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -791,11 +776,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth | ||||
|      */ | ||||
|     protected function fireFailedEvent($user, array $credentials) | ||||
|     { | ||||
|         if (isset($this->events)) { | ||||
|             $this->events->dispatch(new Failed( | ||||
|                 $this->name, $user, $credentials | ||||
|             )); | ||||
|         } | ||||
|         $this->events?->dispatch(new Failed($this->name, $user, $credentials)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -14,13 +14,13 @@ | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": "^7.3|^8.0", | ||||
|         "illuminate/collections": "^8.0", | ||||
|         "illuminate/contracts": "^8.0", | ||||
|         "illuminate/http": "^8.0", | ||||
|         "illuminate/macroable": "^8.0", | ||||
|         "illuminate/queue": "^8.0", | ||||
|         "illuminate/support": "^8.0" | ||||
|         "php": "^8.0.2", | ||||
|         "illuminate/collections": "^9.0", | ||||
|         "illuminate/contracts": "^9.0", | ||||
|         "illuminate/http": "^9.0", | ||||
|         "illuminate/macroable": "^9.0", | ||||
|         "illuminate/queue": "^9.0", | ||||
|         "illuminate/support": "^9.0" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
| @@ -29,13 +29,13 @@ | ||||
|     }, | ||||
|     "extra": { | ||||
|         "branch-alias": { | ||||
|             "dev-master": "8.x-dev" | ||||
|             "dev-master": "9.x-dev" | ||||
|         } | ||||
|     }, | ||||
|     "suggest": { | ||||
|         "illuminate/console": "Required to use the auth:clear-resets command (^8.0).", | ||||
|         "illuminate/queue": "Required to fire login / logout events (^8.0).", | ||||
|         "illuminate/session": "Required to use the session based guard (^8.0)." | ||||
|         "illuminate/console": "Required to use the auth:clear-resets command (^9.0).", | ||||
|         "illuminate/queue": "Required to fire login / logout events (^9.0).", | ||||
|         "illuminate/session": "Required to use the session based guard (^9.0)." | ||||
|     }, | ||||
|     "config": { | ||||
|         "sort-packages": true | ||||
|   | ||||
| @@ -5,6 +5,7 @@ namespace Illuminate\Broadcasting; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Routing\Controller; | ||||
| use Illuminate\Support\Facades\Broadcast; | ||||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||||
|  | ||||
| class BroadcastController extends Controller | ||||
| { | ||||
| @@ -22,4 +23,22 @@ class BroadcastController extends Controller | ||||
|  | ||||
|         return Broadcast::auth($request); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Authenticate the current user. | ||||
|      * | ||||
|      * See: https://pusher.com/docs/channels/server_api/authenticating-users/#user-authentication. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function authenticateUser(Request $request) | ||||
|     { | ||||
|         if ($request->hasSession()) { | ||||
|             $request->session()->reflash(); | ||||
|         } | ||||
|  | ||||
|         return Broadcast::resolveAuthenticatedUser($request) | ||||
|                     ?? throw new AccessDeniedHttpException; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,6 +35,13 @@ class BroadcastEvent implements ShouldQueue | ||||
|      */ | ||||
|     public $timeout; | ||||
|  | ||||
|     /** | ||||
|      * The number of seconds to wait before retrying the job when encountering an uncaught exception. | ||||
|      * | ||||
|      * @var int | ||||
|      */ | ||||
|     public $backoff; | ||||
|  | ||||
|     /** | ||||
|      * Create a new job handler instance. | ||||
|      * | ||||
| @@ -46,6 +53,7 @@ class BroadcastEvent implements ShouldQueue | ||||
|         $this->event = $event; | ||||
|         $this->tries = property_exists($event, 'tries') ? $event->tries : null; | ||||
|         $this->timeout = property_exists($event, 'timeout') ? $event->timeout : null; | ||||
|         $this->backoff = property_exists($event, 'backoff') ? $event->backoff : null; | ||||
|         $this->afterCommit = property_exists($event, 'afterCommit') ? $event->afterCommit : null; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -4,14 +4,18 @@ namespace Illuminate\Broadcasting; | ||||
|  | ||||
| use Ably\AblyRest; | ||||
| use Closure; | ||||
| use GuzzleHttp\Client as GuzzleClient; | ||||
| use Illuminate\Broadcasting\Broadcasters\AblyBroadcaster; | ||||
| use Illuminate\Broadcasting\Broadcasters\LogBroadcaster; | ||||
| use Illuminate\Broadcasting\Broadcasters\NullBroadcaster; | ||||
| use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster; | ||||
| use Illuminate\Broadcasting\Broadcasters\RedisBroadcaster; | ||||
| use Illuminate\Bus\UniqueLock; | ||||
| use Illuminate\Contracts\Broadcasting\Factory as FactoryContract; | ||||
| use Illuminate\Contracts\Broadcasting\ShouldBeUnique; | ||||
| use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; | ||||
| use Illuminate\Contracts\Bus\Dispatcher as BusDispatcherContract; | ||||
| use Illuminate\Contracts\Cache\Repository as Cache; | ||||
| use Illuminate\Contracts\Foundation\CachesRoutes; | ||||
| use InvalidArgumentException; | ||||
| use Psr\Log\LoggerInterface; | ||||
| @@ -55,7 +59,7 @@ class BroadcastManager implements FactoryContract | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register the routes for handling broadcast authentication and sockets. | ||||
|      * Register the routes for handling broadcast channel authentication and sockets. | ||||
|      * | ||||
|      * @param  array|null  $attributes | ||||
|      * @return void | ||||
| @@ -76,6 +80,41 @@ class BroadcastManager implements FactoryContract | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register the routes for handling broadcast user authentication. | ||||
|      * | ||||
|      * @param  array|null  $attributes | ||||
|      * @return void | ||||
|      */ | ||||
|     public function userRoutes(array $attributes = null) | ||||
|     { | ||||
|         if ($this->app instanceof CachesRoutes && $this->app->routesAreCached()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $attributes = $attributes ?: ['middleware' => ['web']]; | ||||
|  | ||||
|         $this->app['router']->group($attributes, function ($router) { | ||||
|             $router->match( | ||||
|                 ['get', 'post'], '/broadcasting/user-auth', | ||||
|                 '\\'.BroadcastController::class.'@authenticateUser' | ||||
|             )->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register the routes for handling broadcast authentication and sockets. | ||||
|      * | ||||
|      * Alias of "routes" method. | ||||
|      * | ||||
|      * @param  array|null  $attributes | ||||
|      * @return void | ||||
|      */ | ||||
|     public function channelRoutes(array $attributes = null) | ||||
|     { | ||||
|         return $this->routes($attributes); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the socket ID for the given request. | ||||
|      * | ||||
| @@ -129,9 +168,34 @@ class BroadcastManager implements FactoryContract | ||||
|             $queue = $event->queue; | ||||
|         } | ||||
|  | ||||
|         $this->app->make('queue')->connection($event->connection ?? null)->pushOn( | ||||
|             $queue, new BroadcastEvent(clone $event) | ||||
|         ); | ||||
|         $broadcastEvent = new BroadcastEvent(clone $event); | ||||
|  | ||||
|         if ($event instanceof ShouldBeUnique) { | ||||
|             $broadcastEvent = new UniqueBroadcastEvent(clone $event); | ||||
|  | ||||
|             if ($this->mustBeUniqueAndCannotAcquireLock($broadcastEvent)) { | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $this->app->make('queue') | ||||
|             ->connection($event->connection ?? null) | ||||
|             ->pushOn($queue, $broadcastEvent); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the broadcastable event must be unique and determine if we can acquire the necessary lock. | ||||
|      * | ||||
|      * @param  mixed  $event | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function mustBeUniqueAndCannotAcquireLock($event) | ||||
|     { | ||||
|         return ! (new UniqueLock( | ||||
|             method_exists($event, 'uniqueVia') | ||||
|                 ? $event->uniqueVia() | ||||
|                 : $this->app->make(Cache::class) | ||||
|         ))->acquire($event); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -181,6 +245,10 @@ class BroadcastManager implements FactoryContract | ||||
|     { | ||||
|         $config = $this->getConfig($name); | ||||
|  | ||||
|         if (is_null($config)) { | ||||
|             throw new InvalidArgumentException("Broadcast connection [{$name}] is not defined."); | ||||
|         } | ||||
|  | ||||
|         if (isset($this->customCreators[$config['driver']])) { | ||||
|             return $this->callCustomCreator($config); | ||||
|         } | ||||
| @@ -212,17 +280,33 @@ class BroadcastManager implements FactoryContract | ||||
|      * @return \Illuminate\Contracts\Broadcasting\Broadcaster | ||||
|      */ | ||||
|     protected function createPusherDriver(array $config) | ||||
|     { | ||||
|         return new PusherBroadcaster($this->pusher($config)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a Pusher instance for the given configuration. | ||||
|      * | ||||
|      * @param  array  $config | ||||
|      * @return \Pusher\Pusher | ||||
|      */ | ||||
|     public function pusher(array $config) | ||||
|     { | ||||
|         $pusher = new Pusher( | ||||
|             $config['key'], $config['secret'], | ||||
|             $config['app_id'], $config['options'] ?? [] | ||||
|             $config['key'], | ||||
|             $config['secret'], | ||||
|             $config['app_id'], | ||||
|             $config['options'] ?? [], | ||||
|             isset($config['client_options']) && ! empty($config['client_options']) | ||||
|                     ? new GuzzleClient($config['client_options']) | ||||
|                     : null, | ||||
|         ); | ||||
|  | ||||
|         if ($config['log'] ?? false) { | ||||
|             $pusher->setLogger($this->app->make(LoggerInterface::class)); | ||||
|         } | ||||
|  | ||||
|         return new PusherBroadcaster($pusher); | ||||
|         return $pusher; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -233,7 +317,18 @@ class BroadcastManager implements FactoryContract | ||||
|      */ | ||||
|     protected function createAblyDriver(array $config) | ||||
|     { | ||||
|         return new AblyBroadcaster(new AblyRest($config)); | ||||
|         return new AblyBroadcaster($this->ably($config)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get an Ably instance for the given configuration. | ||||
|      * | ||||
|      * @param  array  $config | ||||
|      * @return \Ably\AblyRest | ||||
|      */ | ||||
|     public function ably(array $config) | ||||
|     { | ||||
|         return new AblyRest($config); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -318,7 +413,7 @@ class BroadcastManager implements FactoryContract | ||||
|      */ | ||||
|     public function purge($name = null) | ||||
|     { | ||||
|         $name = $name ?? $this->getDefaultDriver(); | ||||
|         $name ??= $this->getDefaultDriver(); | ||||
|  | ||||
|         unset($this->drivers[$name]); | ||||
|     } | ||||
|   | ||||
| @@ -3,7 +3,9 @@ | ||||
| namespace Illuminate\Broadcasting\Broadcasters; | ||||
|  | ||||
| use Ably\AblyRest; | ||||
| use Ably\Exceptions\AblyException; | ||||
| use Ably\Models\Message as AblyMessage; | ||||
| use Illuminate\Broadcasting\BroadcastException; | ||||
| use Illuminate\Support\Str; | ||||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||||
|  | ||||
| @@ -63,7 +65,7 @@ class AblyBroadcaster extends Broadcaster | ||||
|      */ | ||||
|     public function validAuthenticationResponse($request, $result) | ||||
|     { | ||||
|         if (Str::startsWith($request->channel_name, 'private')) { | ||||
|         if (str_starts_with($request->channel_name, 'private')) { | ||||
|             $signature = $this->generateAblySignature( | ||||
|                 $request->channel_name, $request->socket_id | ||||
|             ); | ||||
| @@ -118,12 +120,20 @@ class AblyBroadcaster extends Broadcaster | ||||
|      * @param  string  $event | ||||
|      * @param  array  $payload | ||||
|      * @return void | ||||
|      * | ||||
|      * @throws \Illuminate\Broadcasting\BroadcastException | ||||
|      */ | ||||
|     public function broadcast(array $channels, $event, array $payload = []) | ||||
|     { | ||||
|         foreach ($this->formatChannels($channels) as $channel) { | ||||
|             $this->ably->channels->get($channel)->publish( | ||||
|                 $this->buildAblyMessage($event, $payload) | ||||
|         try { | ||||
|             foreach ($this->formatChannels($channels) as $channel) { | ||||
|                 $this->ably->channels->get($channel)->publish( | ||||
|                     $this->buildAblyMessage($event, $payload) | ||||
|                 ); | ||||
|             } | ||||
|         } catch (AblyException $e) { | ||||
|             throw new BroadcastException( | ||||
|                 sprintf('Ably error: %s', $e->getMessage()) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| @@ -164,7 +174,7 @@ class AblyBroadcaster extends Broadcaster | ||||
|     public function normalizeChannelName($channel) | ||||
|     { | ||||
|         if ($this->isGuardedChannel($channel)) { | ||||
|             return Str::startsWith($channel, 'private-') | ||||
|             return str_starts_with($channel, 'private-') | ||||
|                         ? Str::replaceFirst('private-', '', $channel) | ||||
|                         : Str::replaceFirst('presence-', '', $channel); | ||||
|         } | ||||
| @@ -184,7 +194,7 @@ class AblyBroadcaster extends Broadcaster | ||||
|             $channel = (string) $channel; | ||||
|  | ||||
|             if (Str::startsWith($channel, ['private-', 'presence-'])) { | ||||
|                 return Str::startsWith($channel, 'private-') | ||||
|                 return str_starts_with($channel, 'private-') | ||||
|                     ? Str::replaceFirst('private-', 'private:', $channel) | ||||
|                     : Str::replaceFirst('presence-', 'presence:', $channel); | ||||
|             } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace Illuminate\Broadcasting\Broadcasters; | ||||
|  | ||||
| use Closure; | ||||
| use Exception; | ||||
| use Illuminate\Container\Container; | ||||
| use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract; | ||||
| @@ -10,13 +11,19 @@ use Illuminate\Contracts\Routing\BindingRegistrar; | ||||
| use Illuminate\Contracts\Routing\UrlRoutable; | ||||
| use Illuminate\Support\Arr; | ||||
| use Illuminate\Support\Reflector; | ||||
| use Illuminate\Support\Str; | ||||
| use ReflectionClass; | ||||
| use ReflectionFunction; | ||||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||||
|  | ||||
| abstract class Broadcaster implements BroadcasterContract | ||||
| { | ||||
|     /** | ||||
|      * The callback to resolve the authenticated user information. | ||||
|      * | ||||
|      * @var \Closure|null | ||||
|      */ | ||||
|     protected $authenticatedUserCallback = null; | ||||
|  | ||||
|     /** | ||||
|      * The registered channel authenticators. | ||||
|      * | ||||
| @@ -38,6 +45,34 @@ abstract class Broadcaster implements BroadcasterContract | ||||
|      */ | ||||
|     protected $bindingRegistrar; | ||||
|  | ||||
|     /** | ||||
|      * Resolve the authenticated user payload for the incoming connection request. | ||||
|      * | ||||
|      * See: https://pusher.com/docs/channels/library_auth_reference/auth-signatures/#user-authentication. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return array|null | ||||
|      */ | ||||
|     public function resolveAuthenticatedUser($request) | ||||
|     { | ||||
|         if ($this->authenticatedUserCallback) { | ||||
|             return $this->authenticatedUserCallback->__invoke($request); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register the user retrieval callback used to authenticate connections. | ||||
|      * | ||||
|      * See: https://pusher.com/docs/channels/library_auth_reference/auth-signatures/#user-authentication. | ||||
|      * | ||||
|      * @param  \Closure  $callback | ||||
|      * @return void | ||||
|      */ | ||||
|     public function resolveAuthenticatedUserUsing(Closure $callback) | ||||
|     { | ||||
|         $this->authenticatedUserCallback = $callback; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register a channel authenticator. | ||||
|      * | ||||
| @@ -81,7 +116,11 @@ abstract class Broadcaster implements BroadcasterContract | ||||
|  | ||||
|             $handler = $this->normalizeChannelHandlerToCallable($callback); | ||||
|  | ||||
|             if ($result = $handler($this->retrieveUser($request, $channel), ...$parameters)) { | ||||
|             $result = $handler($this->retrieveUser($request, $channel), ...$parameters); | ||||
|  | ||||
|             if ($result === false) { | ||||
|                 throw new AccessDeniedHttpException; | ||||
|             } elseif ($result) { | ||||
|                 return $this->validAuthenticationResponse($request, $result); | ||||
|             } | ||||
|         } | ||||
| @@ -332,6 +371,6 @@ abstract class Broadcaster implements BroadcasterContract | ||||
|      */ | ||||
|     protected function channelNameMatchesPattern($channel, $pattern) | ||||
|     { | ||||
|         return Str::is(preg_replace('/\{(.*?)\}/', '*', $pattern), $channel); | ||||
|         return preg_match('/'.preg_replace('/\{(.*?)\}/', '([^\.]+)', $pattern).'$/', $channel); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ namespace Illuminate\Broadcasting\Broadcasters; | ||||
|  | ||||
| use Illuminate\Broadcasting\BroadcastException; | ||||
| use Illuminate\Support\Arr; | ||||
| use Illuminate\Support\Str; | ||||
| use Illuminate\Support\Collection; | ||||
| use Pusher\ApiErrorException; | ||||
| use Pusher\Pusher; | ||||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||||
| @@ -31,6 +31,39 @@ class PusherBroadcaster extends Broadcaster | ||||
|         $this->pusher = $pusher; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Resolve the authenticated user payload for an incoming connection request. | ||||
|      * | ||||
|      * See: https://pusher.com/docs/channels/library_auth_reference/auth-signatures/#user-authentication | ||||
|      * See: https://pusher.com/docs/channels/server_api/authenticating-users/#response | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return array|null | ||||
|      */ | ||||
|     public function resolveAuthenticatedUser($request) | ||||
|     { | ||||
|         if (! $user = parent::resolveAuthenticatedUser($request)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (method_exists($this->pusher, 'authenticateUser')) { | ||||
|             return $this->pusher->authenticateUser($request->socket_id, $user); | ||||
|         } | ||||
|  | ||||
|         $settings = $this->pusher->getSettings(); | ||||
|         $encodedUser = json_encode($user); | ||||
|         $decodedString = "{$request->socket_id}::user::{$encodedUser}"; | ||||
|  | ||||
|         $auth = $settings['auth_key'].':'.hash_hmac( | ||||
|             'sha256', $decodedString, $settings['secret'] | ||||
|         ); | ||||
|  | ||||
|         return [ | ||||
|             'auth' => $auth, | ||||
|             'user_data' => $encodedUser, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Authenticate the incoming request for a given channel. | ||||
|      * | ||||
| @@ -63,9 +96,12 @@ class PusherBroadcaster extends Broadcaster | ||||
|      */ | ||||
|     public function validAuthenticationResponse($request, $result) | ||||
|     { | ||||
|         if (Str::startsWith($request->channel_name, 'private')) { | ||||
|         if (str_starts_with($request->channel_name, 'private')) { | ||||
|             return $this->decodePusherResponse( | ||||
|                 $request, $this->pusher->socket_auth($request->channel_name, $request->socket_id) | ||||
|                 $request, | ||||
|                 method_exists($this->pusher, 'authorizeChannel') | ||||
|                     ? $this->pusher->authorizeChannel($request->channel_name, $request->socket_id) | ||||
|                     : $this->pusher->socket_auth($request->channel_name, $request->socket_id) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
| @@ -79,10 +115,9 @@ class PusherBroadcaster extends Broadcaster | ||||
|  | ||||
|         return $this->decodePusherResponse( | ||||
|             $request, | ||||
|             $this->pusher->presence_auth( | ||||
|                 $request->channel_name, $request->socket_id, | ||||
|                 $broadcastIdentifier, $result | ||||
|             ) | ||||
|             method_exists($this->pusher, 'authorizePresenceChannel') | ||||
|                 ? $this->pusher->authorizePresenceChannel($request->channel_name, $request->socket_id, $broadcastIdentifier, $result) | ||||
|                 : $this->pusher->presence_auth($request->channel_name, $request->socket_id, $broadcastIdentifier, $result) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @@ -117,46 +152,21 @@ class PusherBroadcaster extends Broadcaster | ||||
|     { | ||||
|         $socket = Arr::pull($payload, 'socket'); | ||||
|  | ||||
|         if ($this->pusherServerIsVersionFiveOrGreater()) { | ||||
|             $parameters = $socket !== null ? ['socket_id' => $socket] : []; | ||||
|         $parameters = $socket !== null ? ['socket_id' => $socket] : []; | ||||
|  | ||||
|             try { | ||||
|                 $this->pusher->trigger( | ||||
|                     $this->formatChannels($channels), $event, $payload, $parameters | ||||
|                 ); | ||||
|             } catch (ApiErrorException $e) { | ||||
|                 throw new BroadcastException( | ||||
|                     sprintf('Pusher error: %s.', $e->getMessage()) | ||||
|                 ); | ||||
|             } | ||||
|         } else { | ||||
|             $response = $this->pusher->trigger( | ||||
|                 $this->formatChannels($channels), $event, $payload, $socket, true | ||||
|             ); | ||||
|  | ||||
|             if ((is_array($response) && $response['status'] >= 200 && $response['status'] <= 299) | ||||
|                 || $response === true) { | ||||
|                 return; | ||||
|             } | ||||
|         $channels = Collection::make($this->formatChannels($channels)); | ||||
|  | ||||
|         try { | ||||
|             $channels->chunk(100)->each(function ($channels) use ($event, $payload, $parameters) { | ||||
|                 $this->pusher->trigger($channels->toArray(), $event, $payload, $parameters); | ||||
|             }); | ||||
|         } catch (ApiErrorException $e) { | ||||
|             throw new BroadcastException( | ||||
|                 ! empty($response['body']) | ||||
|                     ? sprintf('Pusher error: %s.', $response['body']) | ||||
|                     : 'Failed to connect to Pusher.' | ||||
|                 sprintf('Pusher error: %s.', $e->getMessage()) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the Pusher PHP server is version 5.0 or greater. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function pusherServerIsVersionFiveOrGreater() | ||||
|     { | ||||
|         return class_exists(ApiErrorException::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the Pusher SDK instance. | ||||
|      * | ||||
|   | ||||
| @@ -2,8 +2,11 @@ | ||||
|  | ||||
| namespace Illuminate\Broadcasting\Broadcasters; | ||||
|  | ||||
| use Illuminate\Broadcasting\BroadcastException; | ||||
| use Illuminate\Contracts\Redis\Factory as Redis; | ||||
| use Illuminate\Support\Arr; | ||||
| use Predis\Connection\ConnectionException; | ||||
| use RedisException; | ||||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||||
|  | ||||
| class RedisBroadcaster extends Broadcaster | ||||
| @@ -105,6 +108,8 @@ class RedisBroadcaster extends Broadcaster | ||||
|      * @param  string  $event | ||||
|      * @param  array  $payload | ||||
|      * @return void | ||||
|      * | ||||
|      * @throws \Illuminate\Broadcasting\BroadcastException | ||||
|      */ | ||||
|     public function broadcast(array $channels, $event, array $payload = []) | ||||
|     { | ||||
| @@ -120,10 +125,16 @@ class RedisBroadcaster extends Broadcaster | ||||
|             'socket' => Arr::pull($payload, 'socket'), | ||||
|         ]); | ||||
|  | ||||
|         $connection->eval( | ||||
|             $this->broadcastMultipleChannelsScript(), | ||||
|             0, $payload, ...$this->formatChannels($channels) | ||||
|         ); | ||||
|         try { | ||||
|             $connection->eval( | ||||
|                 $this->broadcastMultipleChannelsScript(), | ||||
|                 0, $payload, ...$this->formatChannels($channels) | ||||
|             ); | ||||
|         } catch (ConnectionException|RedisException $e) { | ||||
|             throw new BroadcastException( | ||||
|                 sprintf('Redis error: %s.', $e->getMessage()) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										61
									
								
								vendor/laravel/framework/src/Illuminate/Broadcasting/UniqueBroadcastEvent.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/laravel/framework/src/Illuminate/Broadcasting/UniqueBroadcastEvent.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Broadcasting; | ||||
|  | ||||
| use Illuminate\Container\Container; | ||||
| use Illuminate\Contracts\Cache\Repository; | ||||
| use Illuminate\Contracts\Queue\ShouldBeUnique; | ||||
|  | ||||
| class UniqueBroadcastEvent extends BroadcastEvent implements ShouldBeUnique | ||||
| { | ||||
|     /** | ||||
|      * The unique lock identifier. | ||||
|      * | ||||
|      * @var mixed | ||||
|      */ | ||||
|     public $uniqueId; | ||||
|  | ||||
|     /** | ||||
|      * The number of seconds the unique lock should be maintained. | ||||
|      * | ||||
|      * @var int | ||||
|      */ | ||||
|     public $uniqueFor; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param  mixed  $event | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($event) | ||||
|     { | ||||
|         $this->uniqueId = get_class($event); | ||||
|  | ||||
|         if (method_exists($event, 'uniqueId')) { | ||||
|             $this->uniqueId .= $event->uniqueId(); | ||||
|         } elseif (property_exists($event, 'uniqueId')) { | ||||
|             $this->uniqueId .= $event->uniqueId; | ||||
|         } | ||||
|  | ||||
|         if (method_exists($event, 'uniqueFor')) { | ||||
|             $this->uniqueFor = $event->uniqueFor(); | ||||
|         } elseif (property_exists($event, 'uniqueFor')) { | ||||
|             $this->uniqueFor = $event->uniqueFor; | ||||
|         } | ||||
|  | ||||
|         parent::__construct($event); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Resolve the cache implementation that should manage the event's uniqueness. | ||||
|      * | ||||
|      * @return \Illuminate\Contracts\Cache\Repository | ||||
|      */ | ||||
|     public function uniqueVia() | ||||
|     { | ||||
|         return method_exists($this->event, 'uniqueVia') | ||||
|                 ? $this->event->uniqueVia() | ||||
|                 : Container::getInstance()->make(Repository::class); | ||||
|     } | ||||
| } | ||||
| @@ -14,14 +14,15 @@ | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": "^7.3|^8.0", | ||||
|         "php": "^8.0.2", | ||||
|         "ext-json": "*", | ||||
|         "psr/log": "^1.0|^2.0", | ||||
|         "illuminate/bus": "^8.0", | ||||
|         "illuminate/collections": "^8.0", | ||||
|         "illuminate/contracts": "^8.0", | ||||
|         "illuminate/queue": "^8.0", | ||||
|         "illuminate/support": "^8.0" | ||||
|         "psr/log": "^1.0|^2.0|^3.0", | ||||
|         "illuminate/bus": "^9.0", | ||||
|         "illuminate/collections": "^9.0", | ||||
|         "illuminate/container": "^9.0", | ||||
|         "illuminate/contracts": "^9.0", | ||||
|         "illuminate/queue": "^9.0", | ||||
|         "illuminate/support": "^9.0" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
| @@ -30,12 +31,12 @@ | ||||
|     }, | ||||
|     "extra": { | ||||
|         "branch-alias": { | ||||
|             "dev-master": "8.x-dev" | ||||
|             "dev-master": "9.x-dev" | ||||
|         } | ||||
|     }, | ||||
|     "suggest": { | ||||
|         "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", | ||||
|         "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0|^7.0)." | ||||
|         "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0)." | ||||
|     }, | ||||
|     "config": { | ||||
|         "sort-packages": true | ||||
|   | ||||
| @@ -155,7 +155,7 @@ class Batch implements Arrayable, JsonSerializable | ||||
|     /** | ||||
|      * Add additional jobs to the batch. | ||||
|      * | ||||
|      * @param  \Illuminate\Support\Enumerable|array  $jobs | ||||
|      * @param  \Illuminate\Support\Enumerable|object|array  $jobs | ||||
|      * @return self | ||||
|      */ | ||||
|     public function add($jobs) | ||||
| @@ -462,8 +462,7 @@ class Batch implements Arrayable, JsonSerializable | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function jsonSerialize() | ||||
|     public function jsonSerialize(): array | ||||
|     { | ||||
|         return $this->toArray(); | ||||
|     } | ||||
|   | ||||
| @@ -2,7 +2,10 @@ | ||||
|  | ||||
| namespace Illuminate\Bus; | ||||
|  | ||||
| use Carbon\CarbonImmutable; | ||||
| use Illuminate\Container\Container; | ||||
| use Illuminate\Support\Str; | ||||
| use Illuminate\Support\Testing\Fakes\BatchFake; | ||||
|  | ||||
| trait Batchable | ||||
| { | ||||
| @@ -13,6 +16,13 @@ trait Batchable | ||||
|      */ | ||||
|     public $batchId; | ||||
|  | ||||
|     /** | ||||
|      * The fake batch, if applicable. | ||||
|      * | ||||
|      * @var \Illuminate\Support\Testing\BatchFake | ||||
|      */ | ||||
|     private $fakeBatch; | ||||
|  | ||||
|     /** | ||||
|      * Get the batch instance for the job, if applicable. | ||||
|      * | ||||
| @@ -20,6 +30,10 @@ trait Batchable | ||||
|      */ | ||||
|     public function batch() | ||||
|     { | ||||
|         if ($this->fakeBatch) { | ||||
|             return $this->fakeBatch; | ||||
|         } | ||||
|  | ||||
|         if ($this->batchId) { | ||||
|             return Container::getInstance()->make(BatchRepository::class)->find($this->batchId); | ||||
|         } | ||||
| @@ -49,4 +63,46 @@ trait Batchable | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Indicate that the job should use a fake batch. | ||||
|      * | ||||
|      * @param  string  $id | ||||
|      * @param  string  $name | ||||
|      * @param  int  $totalJobs | ||||
|      * @param  int  $pendingJobs | ||||
|      * @param  int  $failedJobs | ||||
|      * @param  array  $failedJobIds | ||||
|      * @param  array  $options | ||||
|      * @param  \Carbon\CarbonImmutable  $createdAt | ||||
|      * @param  \Carbon\CarbonImmutable|null  $cancelledAt | ||||
|      * @param  \Carbon\CarbonImmutable|null  $finishedAt | ||||
|      * @return array{0: $this, 1: \Illuminate\Support\Testing\BatchFake} | ||||
|      */ | ||||
|     public function withFakeBatch(string $id = '', | ||||
|                                   string $name = '', | ||||
|                                   int $totalJobs = 0, | ||||
|                                   int $pendingJobs = 0, | ||||
|                                   int $failedJobs = 0, | ||||
|                                   array $failedJobIds = [], | ||||
|                                   array $options = [], | ||||
|                                   CarbonImmutable $createdAt = null, | ||||
|                                   ?CarbonImmutable $cancelledAt = null, | ||||
|                                   ?CarbonImmutable $finishedAt = null) | ||||
|     { | ||||
|         $this->fakeBatch = new BatchFake( | ||||
|             empty($id) ? (string) Str::uuid() : $id, | ||||
|             $name, | ||||
|             $totalJobs, | ||||
|             $pendingJobs, | ||||
|             $failedJobs, | ||||
|             $failedJobIds, | ||||
|             $options, | ||||
|             $createdAt ?? CarbonImmutable::now(), | ||||
|             $cancelledAt, | ||||
|             $finishedAt, | ||||
|         ); | ||||
|  | ||||
|         return [$this, $this->fakeBatch]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -64,6 +64,7 @@ class BusServiceProvider extends ServiceProvider implements DeferrableProvider | ||||
|             DispatcherContract::class, | ||||
|             QueueingDispatcherContract::class, | ||||
|             BatchRepository::class, | ||||
|             DatabaseBatchRepository::class, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -59,9 +59,7 @@ class DatabaseBatchRepository implements PrunableBatchRepository | ||||
|         return $this->connection->table($this->table) | ||||
|                             ->orderByDesc('id') | ||||
|                             ->take($limit) | ||||
|                             ->when($before, function ($q) use ($before) { | ||||
|                                 return $q->where('id', '<', $before); | ||||
|                             }) | ||||
|                             ->when($before, fn ($q) => $q->where('id', '<', $before)) | ||||
|                             ->get() | ||||
|                             ->map(function ($batch) { | ||||
|                                 return $this->toBatch($batch); | ||||
| @@ -78,6 +76,7 @@ class DatabaseBatchRepository implements PrunableBatchRepository | ||||
|     public function find(string $batchId) | ||||
|     { | ||||
|         $batch = $this->connection->table($this->table) | ||||
|                             ->useWritePdo() | ||||
|                             ->where('id', $batchId) | ||||
|                             ->first(); | ||||
|  | ||||
| @@ -278,6 +277,29 @@ class DatabaseBatchRepository implements PrunableBatchRepository | ||||
|         return $totalDeleted; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prune all of the cancelled entries older than the given date. | ||||
|      * | ||||
|      * @param  \DateTimeInterface  $before | ||||
|      * @return int | ||||
|      */ | ||||
|     public function pruneCancelled(DateTimeInterface $before) | ||||
|     { | ||||
|         $query = $this->connection->table($this->table) | ||||
|             ->whereNotNull('cancelled_at') | ||||
|             ->where('created_at', '<', $before->getTimestamp()); | ||||
|  | ||||
|         $totalDeleted = 0; | ||||
|  | ||||
|         do { | ||||
|             $deleted = $query->take(1000)->delete(); | ||||
|  | ||||
|             $totalDeleted += $deleted; | ||||
|         } while ($deleted !== 0); | ||||
|  | ||||
|         return $totalDeleted; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the given Closure within a storage specific transaction. | ||||
|      * | ||||
| @@ -286,9 +308,7 @@ class DatabaseBatchRepository implements PrunableBatchRepository | ||||
|      */ | ||||
|     public function transaction(Closure $callback) | ||||
|     { | ||||
|         return $this->connection->transaction(function () use ($callback) { | ||||
|             return $callback(); | ||||
|         }); | ||||
|         return $this->connection->transaction(fn () => $callback()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -344,4 +364,25 @@ class DatabaseBatchRepository implements PrunableBatchRepository | ||||
|             $batch->finished_at ? CarbonImmutable::createFromTimestamp($batch->finished_at) : $batch->finished_at | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the underlying database connection. | ||||
|      * | ||||
|      * @return \Illuminate\Database\Connection | ||||
|      */ | ||||
|     public function getConnection() | ||||
|     { | ||||
|         return $this->connection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the underlying database connection. | ||||
|      * | ||||
|      * @param  \Illuminate\Database\Connection  $connection | ||||
|      * @return void | ||||
|      */ | ||||
|     public function setConnection(Connection $connection) | ||||
|     { | ||||
|         $this->connection = $connection; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,9 +6,9 @@ use Closure; | ||||
| use Illuminate\Bus\Events\BatchDispatched; | ||||
| use Illuminate\Contracts\Container\Container; | ||||
| use Illuminate\Contracts\Events\Dispatcher as EventDispatcher; | ||||
| use Illuminate\Queue\SerializableClosureFactory; | ||||
| use Illuminate\Support\Arr; | ||||
| use Illuminate\Support\Collection; | ||||
| use Laravel\SerializableClosure\SerializableClosure; | ||||
| use Throwable; | ||||
|  | ||||
| class PendingBatch | ||||
| @@ -57,11 +57,13 @@ class PendingBatch | ||||
|     /** | ||||
|      * Add jobs to the batch. | ||||
|      * | ||||
|      * @param  iterable  $jobs | ||||
|      * @param  iterable|object|array  $jobs | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function add($jobs) | ||||
|     { | ||||
|         $jobs = is_iterable($jobs) ? $jobs : Arr::wrap($jobs); | ||||
|  | ||||
|         foreach ($jobs as $job) { | ||||
|             $this->jobs->push($job); | ||||
|         } | ||||
| @@ -78,7 +80,7 @@ class PendingBatch | ||||
|     public function then($callback) | ||||
|     { | ||||
|         $this->options['then'][] = $callback instanceof Closure | ||||
|                         ? SerializableClosureFactory::make($callback) | ||||
|                         ? new SerializableClosure($callback) | ||||
|                         : $callback; | ||||
|  | ||||
|         return $this; | ||||
| @@ -103,7 +105,7 @@ class PendingBatch | ||||
|     public function catch($callback) | ||||
|     { | ||||
|         $this->options['catch'][] = $callback instanceof Closure | ||||
|                     ? SerializableClosureFactory::make($callback) | ||||
|                     ? new SerializableClosure($callback) | ||||
|                     : $callback; | ||||
|  | ||||
|         return $this; | ||||
| @@ -128,7 +130,7 @@ class PendingBatch | ||||
|     public function finally($callback) | ||||
|     { | ||||
|         $this->options['finally'][] = $callback instanceof Closure | ||||
|                     ? SerializableClosureFactory::make($callback) | ||||
|                     ? new SerializableClosure($callback) | ||||
|                     : $callback; | ||||
|  | ||||
|         return $this; | ||||
| @@ -269,4 +271,49 @@ class PendingBatch | ||||
|  | ||||
|         return $batch; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Dispatch the batch after the response is sent to the browser. | ||||
|      * | ||||
|      * @return \Illuminate\Bus\Batch | ||||
|      */ | ||||
|     public function dispatchAfterResponse() | ||||
|     { | ||||
|         $repository = $this->container->make(BatchRepository::class); | ||||
|  | ||||
|         $batch = $repository->store($this); | ||||
|  | ||||
|         if ($batch) { | ||||
|             $this->container->terminating(function () use ($batch) { | ||||
|                 $this->dispatchExistingBatch($batch); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         return $batch; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Dispatch an existing batch. | ||||
|      * | ||||
|      * @param  \Illuminate\Bus\Batch  $batch | ||||
|      * @return void | ||||
|      * | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     protected function dispatchExistingBatch($batch) | ||||
|     { | ||||
|         try { | ||||
|             $batch = $batch->add($this->jobs); | ||||
|         } catch (Throwable $e) { | ||||
|             if (isset($batch)) { | ||||
|                 $batch->delete(); | ||||
|             } | ||||
|  | ||||
|             throw $e; | ||||
|         } | ||||
|  | ||||
|         $this->container->make(EventDispatcher::class)->dispatch( | ||||
|             new BatchDispatched($batch) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -47,7 +47,7 @@ trait Queueable | ||||
|     /** | ||||
|      * The number of seconds before the job should be made available. | ||||
|      * | ||||
|      * @var \DateTimeInterface|\DateInterval|int|null | ||||
|      * @var \DateTimeInterface|\DateInterval|array|int|null | ||||
|      */ | ||||
|     public $delay; | ||||
|  | ||||
| @@ -127,9 +127,9 @@ trait Queueable | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the desired delay for the job. | ||||
|      * Set the desired delay in seconds for the job. | ||||
|      * | ||||
|      * @param  \DateTimeInterface|\DateInterval|int|null  $delay | ||||
|      * @param  \DateTimeInterface|\DateInterval|array|int|null  $delay | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function delay($delay) | ||||
| @@ -191,6 +191,32 @@ trait Queueable | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prepend a job to the current chain so that it is run after the currently running job. | ||||
|      * | ||||
|      * @param  mixed  $job | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function prependToChain($job) | ||||
|     { | ||||
|         $this->chained = Arr::prepend($this->chained, $this->serializeJob($job)); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Append a job to the end of the current chain. | ||||
|      * | ||||
|      * @param  mixed  $job | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function appendToChain($job) | ||||
|     { | ||||
|         $this->chained = array_merge($this->chained, [$this->serializeJob($job)]); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Serialize a job for queuing. | ||||
|      * | ||||
|   | ||||
| @@ -32,17 +32,44 @@ class UniqueLock | ||||
|      */ | ||||
|     public function acquire($job) | ||||
|     { | ||||
|         $uniqueId = method_exists($job, 'uniqueId') | ||||
|                     ? $job->uniqueId() | ||||
|                     : ($job->uniqueId ?? ''); | ||||
|         $uniqueFor = method_exists($job, 'uniqueFor') | ||||
|                     ? $job->uniqueFor() | ||||
|                     : ($job->uniqueFor ?? 0); | ||||
|  | ||||
|         $cache = method_exists($job, 'uniqueVia') | ||||
|                     ? $job->uniqueVia() | ||||
|                     : $this->cache; | ||||
|  | ||||
|         return (bool) $cache->lock( | ||||
|             $key = 'laravel_unique_job:'.get_class($job).$uniqueId, | ||||
|             $job->uniqueFor ?? 0 | ||||
|         )->get(); | ||||
|         return (bool) $cache->lock($this->getKey($job), $uniqueFor)->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Release the lock for the given job. | ||||
|      * | ||||
|      * @param  mixed  $job | ||||
|      * @return void | ||||
|      */ | ||||
|     public function release($job) | ||||
|     { | ||||
|         $cache = method_exists($job, 'uniqueVia') | ||||
|                     ? $job->uniqueVia() | ||||
|                     : $this->cache; | ||||
|  | ||||
|         $cache->lock($this->getKey($job))->forceRelease(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate the lock key for the given job. | ||||
|      * | ||||
|      * @param  mixed  $job | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function getKey($job) | ||||
|     { | ||||
|         $uniqueId = method_exists($job, 'uniqueId') | ||||
|                     ? $job->uniqueId() | ||||
|                     : ($job->uniqueId ?? ''); | ||||
|  | ||||
|         return 'laravel_unique_job:'.get_class($job).$uniqueId; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,11 +14,11 @@ | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": "^7.3|^8.0", | ||||
|         "illuminate/collections": "^8.0", | ||||
|         "illuminate/contracts": "^8.0", | ||||
|         "illuminate/pipeline": "^8.0", | ||||
|         "illuminate/support": "^8.0" | ||||
|         "php": "^8.0.2", | ||||
|         "illuminate/collections": "^9.0", | ||||
|         "illuminate/contracts": "^9.0", | ||||
|         "illuminate/pipeline": "^9.0", | ||||
|         "illuminate/support": "^9.0" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
| @@ -27,7 +27,7 @@ | ||||
|     }, | ||||
|     "extra": { | ||||
|         "branch-alias": { | ||||
|             "dev-master": "8.x-dev" | ||||
|             "dev-master": "9.x-dev" | ||||
|         } | ||||
|     }, | ||||
|     "suggest": { | ||||
|   | ||||
| @@ -46,7 +46,7 @@ class CacheLock extends Lock | ||||
|  | ||||
|         return ($this->seconds > 0) | ||||
|                 ? $this->store->put($this->name, $this->owner, $this->seconds) | ||||
|                 : $this->store->forever($this->name, $this->owner, $this->seconds); | ||||
|                 : $this->store->forever($this->name, $this->owner); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -11,7 +11,8 @@ use Illuminate\Support\Arr; | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| /** | ||||
|  * @mixin \Illuminate\Contracts\Cache\Repository | ||||
|  * @mixin \Illuminate\Cache\Repository | ||||
|  * @mixin \Illuminate\Contracts\Cache\LockProvider | ||||
|  */ | ||||
| class CacheManager implements FactoryContract | ||||
| { | ||||
| @@ -254,7 +255,7 @@ class CacheManager implements FactoryContract | ||||
|     /** | ||||
|      * Create new DynamoDb Client instance. | ||||
|      * | ||||
|      * @return DynamoDbClient | ||||
|      * @return \Aws\DynamoDb\DynamoDbClient | ||||
|      */ | ||||
|     protected function newDynamodbClient(array $config) | ||||
|     { | ||||
| @@ -264,7 +265,7 @@ class CacheManager implements FactoryContract | ||||
|             'endpoint' => $config['endpoint'] ?? null, | ||||
|         ]; | ||||
|  | ||||
|         if (isset($config['key']) && isset($config['secret'])) { | ||||
|         if (isset($config['key'], $config['secret'])) { | ||||
|             $dynamoConfig['credentials'] = Arr::only( | ||||
|                 $config, ['key', 'secret', 'token'] | ||||
|             ); | ||||
| @@ -328,7 +329,7 @@ class CacheManager implements FactoryContract | ||||
|      * Get the cache connection configuration. | ||||
|      * | ||||
|      * @param  string  $name | ||||
|      * @return array | ||||
|      * @return array|null | ||||
|      */ | ||||
|     protected function getConfig($name) | ||||
|     { | ||||
| @@ -368,7 +369,7 @@ class CacheManager implements FactoryContract | ||||
|      */ | ||||
|     public function forgetDriver($name = null) | ||||
|     { | ||||
|         $name = $name ?? $this->getDefaultDriver(); | ||||
|         $name ??= $this->getDefaultDriver(); | ||||
|  | ||||
|         foreach ((array) $name as $cacheName) { | ||||
|             if (isset($this->stores[$cacheName])) { | ||||
| @@ -387,7 +388,7 @@ class CacheManager implements FactoryContract | ||||
|      */ | ||||
|     public function purge($name = null) | ||||
|     { | ||||
|         $name = $name ?? $this->getDefaultDriver(); | ||||
|         $name ??= $this->getDefaultDriver(); | ||||
|  | ||||
|         unset($this->stores[$name]); | ||||
|     } | ||||
|   | ||||
| @@ -5,7 +5,9 @@ namespace Illuminate\Cache\Console; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Filesystem\Filesystem; | ||||
| use Illuminate\Support\Composer; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
|  | ||||
| #[AsCommand(name: 'cache:table')] | ||||
| class CacheTableCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -15,6 +17,17 @@ class CacheTableCommand extends Command | ||||
|      */ | ||||
|     protected $name = 'cache:table'; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'cache:table'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
| @@ -60,7 +73,7 @@ class CacheTableCommand extends Command | ||||
|  | ||||
|         $this->files->put($fullPath, $this->files->get(__DIR__.'/stubs/cache.stub')); | ||||
|  | ||||
|         $this->info('Migration created successfully!'); | ||||
|         $this->components->info('Migration created successfully.'); | ||||
|  | ||||
|         $this->composer->dumpAutoloads(); | ||||
|     } | ||||
|   | ||||
| @@ -5,9 +5,11 @@ namespace Illuminate\Cache\Console; | ||||
| use Illuminate\Cache\CacheManager; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Filesystem\Filesystem; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
| use Symfony\Component\Console\Input\InputArgument; | ||||
| use Symfony\Component\Console\Input\InputOption; | ||||
|  | ||||
| #[AsCommand(name: 'cache:clear')] | ||||
| class ClearCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -17,6 +19,17 @@ class ClearCommand extends Command | ||||
|      */ | ||||
|     protected $name = 'cache:clear'; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'cache:clear'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
| @@ -69,14 +82,14 @@ class ClearCommand extends Command | ||||
|         $this->flushFacades(); | ||||
|  | ||||
|         if (! $successful) { | ||||
|             return $this->error('Failed to clear cache. Make sure you have the appropriate permissions.'); | ||||
|             return $this->components->error('Failed to clear cache. Make sure you have the appropriate permissions.'); | ||||
|         } | ||||
|  | ||||
|         $this->laravel['events']->dispatch( | ||||
|             'cache:cleared', [$this->argument('store'), $this->tags()] | ||||
|         ); | ||||
|  | ||||
|         $this->info('Application cache cleared!'); | ||||
|         $this->components->info('Application cache cleared successfully.'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -4,7 +4,9 @@ namespace Illuminate\Cache\Console; | ||||
|  | ||||
| use Illuminate\Cache\CacheManager; | ||||
| use Illuminate\Console\Command; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
|  | ||||
| #[AsCommand(name: 'cache:forget')] | ||||
| class ForgetCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -14,6 +16,17 @@ class ForgetCommand extends Command | ||||
|      */ | ||||
|     protected $signature = 'cache:forget {key : The key to remove} {store? : The store to remove the key from}'; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'cache:forget'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
| @@ -52,6 +65,6 @@ class ForgetCommand extends Command | ||||
|             $this->argument('key') | ||||
|         ); | ||||
|  | ||||
|         $this->info('The ['.$this->argument('key').'] key has been removed from the cache.'); | ||||
|         $this->components->info('The ['.$this->argument('key').'] key has been removed from the cache.'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Support\Facades\Schema; | ||||
|  | ||||
| class CreateCacheTable extends Migration | ||||
| return new class extends Migration | ||||
| { | ||||
|     /** | ||||
|      * Run the migrations. | ||||
| @@ -36,4 +36,4 @@ class CreateCacheTable extends Migration | ||||
|         Schema::dropIfExists('cache'); | ||||
|         Schema::dropIfExists('cache_locks'); | ||||
|     } | ||||
| } | ||||
| }; | ||||
|   | ||||
| @@ -56,8 +56,6 @@ class DatabaseLock extends Lock | ||||
|      */ | ||||
|     public function acquire() | ||||
|     { | ||||
|         $acquired = false; | ||||
|  | ||||
|         try { | ||||
|             $this->connection->table($this->table)->insert([ | ||||
|                 'key' => $this->name, | ||||
|   | ||||
| @@ -371,7 +371,7 @@ class DatabaseStore implements LockProvider, Store | ||||
|     { | ||||
|         $result = serialize($value); | ||||
|  | ||||
|         if ($this->connection instanceof PostgresConnection && Str::contains($result, "\0")) { | ||||
|         if ($this->connection instanceof PostgresConnection && str_contains($result, "\0")) { | ||||
|             $result = base64_encode($result); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -211,7 +211,7 @@ class DynamoDbStore implements LockProvider, Store | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store multiple items in the cache for a given number of $seconds. | ||||
|      * Store multiple items in the cache for a given number of seconds. | ||||
|      * | ||||
|      * @param  array  $values | ||||
|      * @param  int  $seconds | ||||
| @@ -284,7 +284,7 @@ class DynamoDbStore implements LockProvider, Store | ||||
|  | ||||
|             return true; | ||||
|         } catch (DynamoDbException $e) { | ||||
|             if (Str::contains($e->getMessage(), 'ConditionalCheckFailed')) { | ||||
|             if (str_contains($e->getMessage(), 'ConditionalCheckFailed')) { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
| @@ -329,7 +329,7 @@ class DynamoDbStore implements LockProvider, Store | ||||
|  | ||||
|             return (int) $response['Attributes'][$this->valueAttribute]['N']; | ||||
|         } catch (DynamoDbException $e) { | ||||
|             if (Str::contains($e->getMessage(), 'ConditionalCheckFailed')) { | ||||
|             if (str_contains($e->getMessage(), 'ConditionalCheckFailed')) { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
| @@ -374,7 +374,7 @@ class DynamoDbStore implements LockProvider, Store | ||||
|  | ||||
|             return (int) $response['Attributes'][$this->valueAttribute]['N']; | ||||
|         } catch (DynamoDbException $e) { | ||||
|             if (Str::contains($e->getMessage(), 'ConditionalCheckFailed')) { | ||||
|             if (str_contains($e->getMessage(), 'ConditionalCheckFailed')) { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
| @@ -529,7 +529,7 @@ class DynamoDbStore implements LockProvider, Store | ||||
|     /** | ||||
|      * Get the DynamoDb Client instance. | ||||
|      * | ||||
|      * @return DynamoDbClient | ||||
|      * @return \Aws\DynamoDb\DynamoDbClient | ||||
|      */ | ||||
|     public function getClient() | ||||
|     { | ||||
|   | ||||
| @@ -89,10 +89,8 @@ class RateLimiter | ||||
|      */ | ||||
|     public function tooManyAttempts($key, $maxAttempts) | ||||
|     { | ||||
|         $key = $this->cleanRateLimiterKey($key); | ||||
|  | ||||
|         if ($this->attempts($key) >= $maxAttempts) { | ||||
|             if ($this->cache->has($key.':timer')) { | ||||
|             if ($this->cache->has($this->cleanRateLimiterKey($key).':timer')) { | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ class Limit | ||||
|     /** | ||||
|      * The rate limit signature key. | ||||
|      * | ||||
|      * @var mixed|string | ||||
|      * @var mixed | ||||
|      */ | ||||
|     public $key; | ||||
|  | ||||
| @@ -35,7 +35,7 @@ class Limit | ||||
|     /** | ||||
|      * Create a new limit instance. | ||||
|      * | ||||
|      * @param  mixed|string  $key | ||||
|      * @param  mixed  $key | ||||
|      * @param  int  $maxAttempts | ||||
|      * @param  int  $decayMinutes | ||||
|      * @return void | ||||
| @@ -107,7 +107,7 @@ class Limit | ||||
|     /** | ||||
|      * Set the key of the rate limit. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  mixed  $key | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function by($key) | ||||
|   | ||||
| @@ -62,10 +62,10 @@ class Repository implements ArrayAccess, CacheContract | ||||
|     /** | ||||
|      * Determine if an item exists in the cache. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  array|string  $key | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function has($key) | ||||
|     public function has($key): bool | ||||
|     { | ||||
|         return ! is_null($this->get($key)); | ||||
|     } | ||||
| @@ -84,11 +84,13 @@ class Repository implements ArrayAccess, CacheContract | ||||
|     /** | ||||
|      * Retrieve an item from the cache by key. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TCacheValue | ||||
|      * | ||||
|      * @param  array|string  $key | ||||
|      * @param  TCacheValue|(\Closure(): TCacheValue)  $default | ||||
|      * @return (TCacheValue is null ? mixed : TCacheValue) | ||||
|      */ | ||||
|     public function get($key, $default = null) | ||||
|     public function get($key, $default = null): mixed | ||||
|     { | ||||
|         if (is_array($key)) { | ||||
|             return $this->many($key); | ||||
| @@ -134,7 +136,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * | ||||
|      * @return iterable | ||||
|      */ | ||||
|     public function getMultiple($keys, $default = null) | ||||
|     public function getMultiple($keys, $default = null): iterable | ||||
|     { | ||||
|         $defaults = []; | ||||
|  | ||||
| @@ -175,9 +177,11 @@ class Repository implements ArrayAccess, CacheContract | ||||
|     /** | ||||
|      * Retrieve an item from the cache and delete it. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TCacheValue | ||||
|      * | ||||
|      * @param  array|string  $key | ||||
|      * @param  TCacheValue|(\Closure(): TCacheValue)  $default | ||||
|      * @return (TCacheValue is null ? mixed : TCacheValue) | ||||
|      */ | ||||
|     public function pull($key, $default = null) | ||||
|     { | ||||
| @@ -189,7 +193,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|     /** | ||||
|      * Store an item in the cache. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  array|string  $key | ||||
|      * @param  mixed  $value | ||||
|      * @param  \DateTimeInterface|\DateInterval|int|null  $ttl | ||||
|      * @return bool | ||||
| @@ -224,7 +228,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function set($key, $value, $ttl = null) | ||||
|     public function set($key, $value, $ttl = null): bool | ||||
|     { | ||||
|         return $this->put($key, $value, $ttl); | ||||
|     } | ||||
| @@ -283,7 +287,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function setMultiple($values, $ttl = null) | ||||
|     public function setMultiple($values, $ttl = null): bool | ||||
|     { | ||||
|         return $this->putMany(is_array($values) ? $values : iterator_to_array($values), $ttl); | ||||
|     } | ||||
| @@ -372,10 +376,12 @@ class Repository implements ArrayAccess, CacheContract | ||||
|     /** | ||||
|      * Get an item from the cache, or execute the given Closure and store the result. | ||||
|      * | ||||
|      * @template TCacheValue | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  \Closure|\DateTimeInterface|\DateInterval|int|null  $ttl | ||||
|      * @param  \Closure  $callback | ||||
|      * @return mixed | ||||
|      * @param  \Closure(): TCacheValue  $callback | ||||
|      * @return TCacheValue | ||||
|      */ | ||||
|     public function remember($key, $ttl, Closure $callback) | ||||
|     { | ||||
| @@ -396,9 +402,11 @@ class Repository implements ArrayAccess, CacheContract | ||||
|     /** | ||||
|      * Get an item from the cache, or execute the given Closure and store the result forever. | ||||
|      * | ||||
|      * @template TCacheValue | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  \Closure  $callback | ||||
|      * @return mixed | ||||
|      * @param  \Closure(): TCacheValue  $callback | ||||
|      * @return TCacheValue | ||||
|      */ | ||||
|     public function sear($key, Closure $callback) | ||||
|     { | ||||
| @@ -408,9 +416,11 @@ class Repository implements ArrayAccess, CacheContract | ||||
|     /** | ||||
|      * Get an item from the cache, or execute the given Closure and store the result forever. | ||||
|      * | ||||
|      * @template TCacheValue | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  \Closure  $callback | ||||
|      * @return mixed | ||||
|      * @param  \Closure(): TCacheValue  $callback | ||||
|      * @return TCacheValue | ||||
|      */ | ||||
|     public function rememberForever($key, Closure $callback) | ||||
|     { | ||||
| @@ -448,7 +458,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete($key) | ||||
|     public function delete($key): bool | ||||
|     { | ||||
|         return $this->forget($key); | ||||
|     } | ||||
| @@ -458,7 +468,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function deleteMultiple($keys) | ||||
|     public function deleteMultiple($keys): bool | ||||
|     { | ||||
|         $result = true; | ||||
|  | ||||
| @@ -476,7 +486,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function clear() | ||||
|     public function clear(): bool | ||||
|     { | ||||
|         return $this->store->flush(); | ||||
|     } | ||||
| @@ -583,9 +593,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      */ | ||||
|     protected function event($event) | ||||
|     { | ||||
|         if (isset($this->events)) { | ||||
|             $this->events->dispatch($event); | ||||
|         } | ||||
|         $this->events?->dispatch($event); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -615,8 +623,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * @param  string  $key | ||||
|      * @return bool | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetExists($key) | ||||
|     public function offsetExists($key): bool | ||||
|     { | ||||
|         return $this->has($key); | ||||
|     } | ||||
| @@ -627,8 +634,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * @param  string  $key | ||||
|      * @return mixed | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetGet($key) | ||||
|     public function offsetGet($key): mixed | ||||
|     { | ||||
|         return $this->get($key); | ||||
|     } | ||||
| @@ -640,8 +646,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * @param  mixed  $value | ||||
|      * @return void | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetSet($key, $value) | ||||
|     public function offsetSet($key, $value): void | ||||
|     { | ||||
|         $this->put($key, $value, $this->default); | ||||
|     } | ||||
| @@ -652,8 +657,7 @@ class Repository implements ArrayAccess, CacheContract | ||||
|      * @param  string  $key | ||||
|      * @return void | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetUnset($key) | ||||
|     public function offsetUnset($key): void | ||||
|     { | ||||
|         $this->forget($key); | ||||
|     } | ||||
|   | ||||
| @@ -14,14 +14,14 @@ | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": "^7.3|^8.0", | ||||
|         "illuminate/collections": "^8.0", | ||||
|         "illuminate/contracts": "^8.0", | ||||
|         "illuminate/macroable": "^8.0", | ||||
|         "illuminate/support": "^8.0" | ||||
|         "php": "^8.0.2", | ||||
|         "illuminate/collections": "^9.0", | ||||
|         "illuminate/contracts": "^9.0", | ||||
|         "illuminate/macroable": "^9.0", | ||||
|         "illuminate/support": "^9.0" | ||||
|     }, | ||||
|     "provide": { | ||||
|         "psr/simple-cache-implementation": "1.0" | ||||
|         "psr/simple-cache-implementation": "1.0|2.0|3.0" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
| @@ -30,15 +30,15 @@ | ||||
|     }, | ||||
|     "extra": { | ||||
|         "branch-alias": { | ||||
|             "dev-master": "8.x-dev" | ||||
|             "dev-master": "9.x-dev" | ||||
|         } | ||||
|     }, | ||||
|     "suggest": { | ||||
|         "ext-memcached": "Required to use the memcache cache driver.", | ||||
|         "illuminate/database": "Required to use the database cache driver (^8.0).", | ||||
|         "illuminate/filesystem": "Required to use the file cache driver (^8.0).", | ||||
|         "illuminate/redis": "Required to use the redis cache driver (^8.0).", | ||||
|         "symfony/cache": "Required to PSR-6 cache bridge (^5.4)." | ||||
|         "illuminate/database": "Required to use the database cache driver (^9.0).", | ||||
|         "illuminate/filesystem": "Required to use the file cache driver (^9.0).", | ||||
|         "illuminate/redis": "Required to use the redis cache driver (^9.0).", | ||||
|         "symfony/cache": "Required to use PSR-6 cache bridge (^6.0)." | ||||
|     }, | ||||
|     "config": { | ||||
|         "sort-packages": true | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace Illuminate\Support; | ||||
|  | ||||
| use ArgumentCountError; | ||||
| use ArrayAccess; | ||||
| use Illuminate\Support\Traits\Macroable; | ||||
| use InvalidArgumentException; | ||||
| @@ -25,7 +26,7 @@ class Arr | ||||
|      * Add an element to an array using "dot" notation if it doesn't exist. | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  string  $key | ||||
|      * @param  string|int|float  $key | ||||
|      * @param  mixed  $value | ||||
|      * @return array | ||||
|      */ | ||||
| @@ -142,7 +143,7 @@ class Arr | ||||
|      * Get all of the given array except for a specified array of keys. | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  array|string  $keys | ||||
|      * @param  array|string|int|float  $keys | ||||
|      * @return array | ||||
|      */ | ||||
|     public static function except($array, $keys) | ||||
| @@ -169,6 +170,10 @@ class Arr | ||||
|             return $array->offsetExists($key); | ||||
|         } | ||||
|  | ||||
|         if (is_float($key)) { | ||||
|             $key = (string) $key; | ||||
|         } | ||||
|  | ||||
|         return array_key_exists($key, $array); | ||||
|     } | ||||
|  | ||||
| @@ -252,7 +257,7 @@ class Arr | ||||
|      * Remove one or many array items from a given array using "dot" notation. | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  array|string  $keys | ||||
|      * @param  array|string|int|float  $keys | ||||
|      * @return void | ||||
|      */ | ||||
|     public static function forget(&$array, $keys) | ||||
| @@ -281,7 +286,7 @@ class Arr | ||||
|             while (count($parts) > 1) { | ||||
|                 $part = array_shift($parts); | ||||
|  | ||||
|                 if (isset($array[$part]) && is_array($array[$part])) { | ||||
|                 if (isset($array[$part]) && static::accessible($array[$part])) { | ||||
|                     $array = &$array[$part]; | ||||
|                 } else { | ||||
|                     continue 2; | ||||
| @@ -314,7 +319,7 @@ class Arr | ||||
|             return $array[$key]; | ||||
|         } | ||||
|  | ||||
|         if (strpos($key, '.') === false) { | ||||
|         if (! str_contains($key, '.')) { | ||||
|             return $array[$key] ?? value($default); | ||||
|         } | ||||
|  | ||||
| @@ -423,6 +428,59 @@ class Arr | ||||
|         return ! self::isAssoc($array); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Join all items using a string. The final items can use a separate glue string. | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  string  $glue | ||||
|      * @param  string  $finalGlue | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function join($array, $glue, $finalGlue = '') | ||||
|     { | ||||
|         if ($finalGlue === '') { | ||||
|             return implode($glue, $array); | ||||
|         } | ||||
|  | ||||
|         if (count($array) === 0) { | ||||
|             return ''; | ||||
|         } | ||||
|  | ||||
|         if (count($array) === 1) { | ||||
|             return end($array); | ||||
|         } | ||||
|  | ||||
|         $finalItem = array_pop($array); | ||||
|  | ||||
|         return implode($glue, $array).$finalGlue.$finalItem; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Key an associative array by a field or using a callback. | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  callable|array|string  $keyBy | ||||
|      * @return array | ||||
|      */ | ||||
|     public static function keyBy($array, $keyBy) | ||||
|     { | ||||
|         return Collection::make($array)->keyBy($keyBy)->all(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prepend the key names of an associative array. | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  string  $prependWith | ||||
|      * @return array | ||||
|      */ | ||||
|     public static function prependKeysWith($array, $prependWith) | ||||
|     { | ||||
|         return Collection::make($array)->mapWithKeys(function ($item, $key) use ($prependWith) { | ||||
|             return [$prependWith.$key => $item]; | ||||
|         })->all(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a subset of the items from the given array. | ||||
|      * | ||||
| @@ -487,6 +545,26 @@ class Arr | ||||
|         return [$value, $key]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run a map over each of the items in the array. | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  callable  $callback | ||||
|      * @return array | ||||
|      */ | ||||
|     public static function map(array $array, callable $callback) | ||||
|     { | ||||
|         $keys = array_keys($array); | ||||
|  | ||||
|         try { | ||||
|             $items = array_map($callback, $array, $keys); | ||||
|         } catch (ArgumentCountError) { | ||||
|             $items = array_map($callback, $array); | ||||
|         } | ||||
|  | ||||
|         return array_combine($keys, $items); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Push an item onto the beginning of an array. | ||||
|      * | ||||
| @@ -539,7 +617,7 @@ class Arr | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  int|null  $number | ||||
|      * @param  bool|false  $preserveKeys | ||||
|      * @param  bool  $preserveKeys | ||||
|      * @return mixed | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
| @@ -587,7 +665,7 @@ class Arr | ||||
|      * If no key is given to the method, the entire array will be replaced. | ||||
|      * | ||||
|      * @param  array  $array | ||||
|      * @param  string|null  $key | ||||
|      * @param  string|int|null  $key | ||||
|      * @param  mixed  $value | ||||
|      * @return array | ||||
|      */ | ||||
|   | ||||
| @@ -8,22 +8,33 @@ use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString; | ||||
| use Illuminate\Support\Traits\EnumeratesValues; | ||||
| use Illuminate\Support\Traits\Macroable; | ||||
| use stdClass; | ||||
| use Traversable; | ||||
|  | ||||
| /** | ||||
|  * @template TKey of array-key | ||||
|  * @template TValue | ||||
|  * | ||||
|  * @implements \ArrayAccess<TKey, TValue> | ||||
|  * @implements \Illuminate\Support\Enumerable<TKey, TValue> | ||||
|  */ | ||||
| class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable | ||||
| { | ||||
|     /** | ||||
|      * @use \Illuminate\Support\Traits\EnumeratesValues<TKey, TValue> | ||||
|      */ | ||||
|     use EnumeratesValues, Macroable; | ||||
|  | ||||
|     /** | ||||
|      * The items contained in the collection. | ||||
|      * | ||||
|      * @var array | ||||
|      * @var array<TKey, TValue> | ||||
|      */ | ||||
|     protected $items = []; | ||||
|  | ||||
|     /** | ||||
|      * Create a new collection. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>|null  $items | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($items = []) | ||||
| @@ -36,7 +47,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * | ||||
|      * @param  int  $from | ||||
|      * @param  int  $to | ||||
|      * @return static | ||||
|      * @return static<int, int> | ||||
|      */ | ||||
|     public static function range($from, $to) | ||||
|     { | ||||
| @@ -46,7 +57,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get all of the items in the collection. | ||||
|      * | ||||
|      * @return array | ||||
|      * @return array<TKey, TValue> | ||||
|      */ | ||||
|     public function all() | ||||
|     { | ||||
| @@ -56,7 +67,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get a lazy collection for the items in this collection. | ||||
|      * | ||||
|      * @return \Illuminate\Support\LazyCollection | ||||
|      * @return \Illuminate\Support\LazyCollection<TKey, TValue> | ||||
|      */ | ||||
|     public function lazy() | ||||
|     { | ||||
| @@ -66,8 +77,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the average value of a given key. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @return mixed | ||||
|      * @param  (callable(TValue): float|int)|string|null  $callback | ||||
|      * @return float|int|null | ||||
|      */ | ||||
|     public function avg($callback = null) | ||||
|     { | ||||
| @@ -87,15 +98,14 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the median of a given key. | ||||
|      * | ||||
|      * @param  string|array|null  $key | ||||
|      * @return mixed | ||||
|      * @param  string|array<array-key, string>|null  $key | ||||
|      * @return float|int|null | ||||
|      */ | ||||
|     public function median($key = null) | ||||
|     { | ||||
|         $values = (isset($key) ? $this->pluck($key) : $this) | ||||
|             ->filter(function ($item) { | ||||
|                 return ! is_null($item); | ||||
|             })->sort()->values(); | ||||
|             ->filter(fn ($item) => ! is_null($item)) | ||||
|             ->sort()->values(); | ||||
|  | ||||
|         $count = $values->count(); | ||||
|  | ||||
| @@ -117,8 +127,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the mode of a given key. | ||||
|      * | ||||
|      * @param  string|array|null  $key | ||||
|      * @return array|null | ||||
|      * @param  string|array<array-key, string>|null  $key | ||||
|      * @return array<int, float|int>|null | ||||
|      */ | ||||
|     public function mode($key = null) | ||||
|     { | ||||
| @@ -130,23 +140,20 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|  | ||||
|         $counts = new static; | ||||
|  | ||||
|         $collection->each(function ($value) use ($counts) { | ||||
|             $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1; | ||||
|         }); | ||||
|         $collection->each(fn ($value) => $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(); | ||||
|         return $sorted->filter(fn ($value) => $value == $highestValue) | ||||
|             ->sort()->keys()->all(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Collapse the collection of items into a single array. | ||||
|      * | ||||
|      * @return static | ||||
|      * @return static<int, mixed> | ||||
|      */ | ||||
|     public function collapse() | ||||
|     { | ||||
| @@ -156,7 +163,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Determine if an item exists in the collection. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  (callable(TValue, TKey): bool)|TValue|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return bool | ||||
| @@ -176,6 +183,26 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|         return $this->contains($this->operatorForWhere(...func_get_args())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if an item exists, using strict comparison. | ||||
|      * | ||||
|      * @param  (callable(TValue): bool)|TValue|array-key  $key | ||||
|      * @param  TValue|null  $value | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function containsStrict($key, $value = null) | ||||
|     { | ||||
|         if (func_num_args() === 2) { | ||||
|             return $this->contains(fn ($item) => data_get($item, $key) === $value); | ||||
|         } | ||||
|  | ||||
|         if ($this->useAsCallable($key)) { | ||||
|             return ! is_null($this->first($key)); | ||||
|         } | ||||
|  | ||||
|         return in_array($key, $this->items, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if an item is not contained in the collection. | ||||
|      * | ||||
| @@ -192,8 +219,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Cross join with the given lists, returning all possible permutations. | ||||
|      * | ||||
|      * @param  mixed  ...$lists | ||||
|      * @return static | ||||
|      * @template TCrossJoinKey | ||||
|      * @template TCrossJoinValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TCrossJoinKey, TCrossJoinValue>|iterable<TCrossJoinKey, TCrossJoinValue>  ...$lists | ||||
|      * @return static<int, array<int, TValue|TCrossJoinValue>> | ||||
|      */ | ||||
|     public function crossJoin(...$lists) | ||||
|     { | ||||
| @@ -205,7 +235,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the items in the collection that are not present in the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<array-key, TValue>|iterable<array-key, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diff($items) | ||||
| @@ -216,8 +246,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the items in the collection that are not present in the given items, using the callback. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  callable  $callback | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<array-key, TValue>|iterable<array-key, TValue>  $items | ||||
|      * @param  callable(TValue, TValue): int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffUsing($items, callable $callback) | ||||
| @@ -228,7 +258,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the items in the collection whose keys and values are not present in the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffAssoc($items) | ||||
| @@ -239,8 +269,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * 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 | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @param  callable(TKey, TKey): int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffAssocUsing($items, callable $callback) | ||||
| @@ -251,7 +281,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the items in the collection whose keys are not present in the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffKeys($items) | ||||
| @@ -262,8 +292,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the items in the collection whose keys are not present in the given items, using the callback. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  callable  $callback | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @param  callable(TKey, TKey): int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffKeysUsing($items, callable $callback) | ||||
| @@ -274,7 +304,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Retrieve duplicate items from the collection. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @param  (callable(TValue): bool)|string|null  $callback | ||||
|      * @param  bool  $strict | ||||
|      * @return static | ||||
|      */ | ||||
| @@ -302,7 +332,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Retrieve duplicate items from the collection using strict comparison. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @param  (callable(TValue): bool)|string|null  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function duplicatesStrict($callback = null) | ||||
| @@ -314,7 +344,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * Get the comparison function to detect duplicates. | ||||
|      * | ||||
|      * @param  bool  $strict | ||||
|      * @return \Closure | ||||
|      * @return callable(TValue, TValue): bool | ||||
|      */ | ||||
|     protected function duplicateComparator($strict) | ||||
|     { | ||||
| @@ -332,7 +362,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get all items except for those with the specified keys. | ||||
|      * | ||||
|      * @param  \Illuminate\Support\Collection|mixed  $keys | ||||
|      * @param  \Illuminate\Support\Enumerable<array-key, TKey>|array<array-key, TKey>  $keys | ||||
|      * @return static | ||||
|      */ | ||||
|     public function except($keys) | ||||
| @@ -349,7 +379,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Run a filter over each of the items. | ||||
|      * | ||||
|      * @param  callable|null  $callback | ||||
|      * @param (callable(TValue, TKey): bool)|null  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function filter(callable $callback = null) | ||||
| @@ -364,9 +394,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the first item from the collection passing the given truth test. | ||||
|      * | ||||
|      * @param  callable|null  $callback | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TFirstDefault | ||||
|      * | ||||
|      * @param  (callable(TValue, TKey): bool)|null  $callback | ||||
|      * @param  TFirstDefault|(\Closure(): TFirstDefault)  $default | ||||
|      * @return TValue|TFirstDefault | ||||
|      */ | ||||
|     public function first(callable $callback = null, $default = null) | ||||
|     { | ||||
| @@ -377,7 +409,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * Get a flattened array of the items in the collection. | ||||
|      * | ||||
|      * @param  int  $depth | ||||
|      * @return static | ||||
|      * @return static<int, mixed> | ||||
|      */ | ||||
|     public function flatten($depth = INF) | ||||
|     { | ||||
| @@ -387,7 +419,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Flip the items in the collection. | ||||
|      * | ||||
|      * @return static | ||||
|      * @return static<TValue, TKey> | ||||
|      */ | ||||
|     public function flip() | ||||
|     { | ||||
| @@ -397,7 +429,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Remove an item from the collection by key. | ||||
|      * | ||||
|      * @param  string|int|array  $keys | ||||
|      * @param  TKey|array<array-key, TKey>  $keys | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function forget($keys) | ||||
| @@ -412,9 +444,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get an item from the collection by key. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TGetDefault | ||||
|      * | ||||
|      * @param  TKey  $key | ||||
|      * @param  TGetDefault|(\Closure(): TGetDefault)  $default | ||||
|      * @return TValue|TGetDefault | ||||
|      */ | ||||
|     public function get($key, $default = null) | ||||
|     { | ||||
| @@ -446,9 +480,9 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Group an associative array by a field or using a callback. | ||||
|      * | ||||
|      * @param  array|callable|string  $groupBy | ||||
|      * @param  (callable(TValue, TKey): array-key)|array|string  $groupBy | ||||
|      * @param  bool  $preserveKeys | ||||
|      * @return static | ||||
|      * @return static<array-key, static<array-key, TValue>> | ||||
|      */ | ||||
|     public function groupBy($groupBy, $preserveKeys = false) | ||||
|     { | ||||
| @@ -470,7 +504,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|             } | ||||
|  | ||||
|             foreach ($groupKeys as $groupKey) { | ||||
|                 $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey; | ||||
|                 $groupKey = match (true) { | ||||
|                     is_bool($groupKey) => (int) $groupKey, | ||||
|                     $groupKey instanceof \Stringable => (string) $groupKey, | ||||
|                     default => $groupKey, | ||||
|                 }; | ||||
|  | ||||
|                 if (! array_key_exists($groupKey, $results)) { | ||||
|                     $results[$groupKey] = new static; | ||||
| @@ -492,8 +530,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Key an associative array by a field or using a callback. | ||||
|      * | ||||
|      * @param  callable|string  $keyBy | ||||
|      * @return static | ||||
|      * @param  (callable(TValue, TKey): array-key)|array|string  $keyBy | ||||
|      * @return static<array-key, TValue> | ||||
|      */ | ||||
|     public function keyBy($keyBy) | ||||
|     { | ||||
| @@ -517,7 +555,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Determine if an item exists in the collection by key. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  TKey|array<array-key, TKey>  $key | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function has($key) | ||||
| @@ -559,12 +597,16 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Concatenate values of a given key as a string. | ||||
|      * | ||||
|      * @param  string  $value | ||||
|      * @param  callable|string  $value | ||||
|      * @param  string|null  $glue | ||||
|      * @return string | ||||
|      */ | ||||
|     public function implode($value, $glue = null) | ||||
|     { | ||||
|         if ($this->useAsCallable($value)) { | ||||
|             return implode($glue ?? '', $this->map($value)->all()); | ||||
|         } | ||||
|  | ||||
|         $first = $this->first(); | ||||
|  | ||||
|         if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) { | ||||
| @@ -577,7 +619,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Intersect the collection with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function intersect($items) | ||||
| @@ -588,7 +630,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Intersect the collection with the given items by key. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function intersectByKeys($items) | ||||
| @@ -651,7 +693,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the keys of the collection items. | ||||
|      * | ||||
|      * @return static | ||||
|      * @return static<int, TKey> | ||||
|      */ | ||||
|     public function keys() | ||||
|     { | ||||
| @@ -661,9 +703,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the last item from the collection. | ||||
|      * | ||||
|      * @param  callable|null  $callback | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TLastDefault | ||||
|      * | ||||
|      * @param  (callable(TValue, TKey): bool)|null  $callback | ||||
|      * @param  TLastDefault|(\Closure(): TLastDefault)  $default | ||||
|      * @return TValue|TLastDefault | ||||
|      */ | ||||
|     public function last(callable $callback = null, $default = null) | ||||
|     { | ||||
| @@ -673,9 +717,9 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the values of a given key. | ||||
|      * | ||||
|      * @param  string|array|int|null  $value | ||||
|      * @param  string|int|array<array-key, string>  $value | ||||
|      * @param  string|null  $key | ||||
|      * @return static | ||||
|      * @return static<int, mixed> | ||||
|      */ | ||||
|     public function pluck($value, $key = null) | ||||
|     { | ||||
| @@ -685,16 +729,14 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Run a map over each of the items. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TMapValue | ||||
|      * | ||||
|      * @param  callable(TValue, TKey): TMapValue  $callback | ||||
|      * @return static<TKey, TMapValue> | ||||
|      */ | ||||
|     public function map(callable $callback) | ||||
|     { | ||||
|         $keys = array_keys($this->items); | ||||
|  | ||||
|         $items = array_map($callback, $this->items, $keys); | ||||
|  | ||||
|         return new static(array_combine($keys, $items)); | ||||
|         return new static(Arr::map($this->items, $callback)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -702,8 +744,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * | ||||
|      * The callback should return an associative array with a single key/value pair. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TMapToDictionaryKey of array-key | ||||
|      * @template TMapToDictionaryValue | ||||
|      * | ||||
|      * @param  callable(TValue, TKey): array<TMapToDictionaryKey, TMapToDictionaryValue>  $callback | ||||
|      * @return static<TMapToDictionaryKey, array<int, TMapToDictionaryValue>> | ||||
|      */ | ||||
|     public function mapToDictionary(callable $callback) | ||||
|     { | ||||
| @@ -731,8 +776,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * | ||||
|      * The callback should return an associative array with a single key/value pair. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TMapWithKeysKey of array-key | ||||
|      * @template TMapWithKeysValue | ||||
|      * | ||||
|      * @param  callable(TValue, TKey): array<TMapWithKeysKey, TMapWithKeysValue>  $callback | ||||
|      * @return static<TMapWithKeysKey, TMapWithKeysValue> | ||||
|      */ | ||||
|     public function mapWithKeys(callable $callback) | ||||
|     { | ||||
| @@ -752,7 +800,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Merge the collection with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function merge($items) | ||||
| @@ -763,8 +811,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Recursively merge the collection with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @return static | ||||
|      * @template TMergeRecursiveValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TMergeRecursiveValue>|iterable<TKey, TMergeRecursiveValue>  $items | ||||
|      * @return static<TKey, TValue|TMergeRecursiveValue> | ||||
|      */ | ||||
|     public function mergeRecursive($items) | ||||
|     { | ||||
| @@ -774,8 +824,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Create a collection by using this collection for keys and another for its values. | ||||
|      * | ||||
|      * @param  mixed  $values | ||||
|      * @return static | ||||
|      * @template TCombineValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<array-key, TCombineValue>|iterable<array-key, TCombineValue>  $values | ||||
|      * @return static<TValue, TCombineValue> | ||||
|      */ | ||||
|     public function combine($values) | ||||
|     { | ||||
| @@ -785,7 +837,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Union the collection with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function union($items) | ||||
| @@ -820,7 +872,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the items with the specified keys. | ||||
|      * | ||||
|      * @param  mixed  $keys | ||||
|      * @param  \Illuminate\Support\Enumerable<array-key, TKey>|array<array-key, TKey>|string  $keys | ||||
|      * @return static | ||||
|      */ | ||||
|     public function only($keys) | ||||
| @@ -842,7 +894,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * Get and remove the last N items from the collection. | ||||
|      * | ||||
|      * @param  int  $count | ||||
|      * @return mixed | ||||
|      * @return static<int, TValue>|TValue|null | ||||
|      */ | ||||
|     public function pop($count = 1) | ||||
|     { | ||||
| @@ -868,8 +920,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Push an item onto the beginning of the collection. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  mixed  $key | ||||
|      * @param  TValue  $value | ||||
|      * @param  TKey  $key | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function prepend($value, $key = null) | ||||
| @@ -882,7 +934,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Push one or more items onto the end of the collection. | ||||
|      * | ||||
|      * @param  mixed  $values | ||||
|      * @param  TValue  ...$values | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function push(...$values) | ||||
| @@ -897,7 +949,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Push all of the given items onto the collection. | ||||
|      * | ||||
|      * @param  iterable  $source | ||||
|      * @param  iterable<array-key, TValue>  $source | ||||
|      * @return static | ||||
|      */ | ||||
|     public function concat($source) | ||||
| @@ -914,9 +966,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get and remove an item from the collection. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TPullDefault | ||||
|      * | ||||
|      * @param  TKey  $key | ||||
|      * @param  TPullDefault|(\Closure(): TPullDefault)  $default | ||||
|      * @return TValue|TPullDefault | ||||
|      */ | ||||
|     public function pull($key, $default = null) | ||||
|     { | ||||
| @@ -926,8 +980,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Put an item in the collection by key. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  mixed  $value | ||||
|      * @param  TKey  $key | ||||
|      * @param  TValue  $value | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function put($key, $value) | ||||
| @@ -940,8 +994,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get one or a specified number of items randomly from the collection. | ||||
|      * | ||||
|      * @param  int|null  $number | ||||
|      * @return static|mixed | ||||
|      * @param  (callable(self<TKey, TValue>): int)|int|null  $number | ||||
|      * @return static<int, TValue>|TValue | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      */ | ||||
| @@ -951,13 +1005,17 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|             return Arr::random($this->items); | ||||
|         } | ||||
|  | ||||
|         if (is_callable($number)) { | ||||
|             return new static(Arr::random($this->items, $number($this))); | ||||
|         } | ||||
|  | ||||
|         return new static(Arr::random($this->items, $number)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Replace the collection items with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function replace($items) | ||||
| @@ -968,7 +1026,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Recursively replace the collection items with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function replaceRecursive($items) | ||||
| @@ -989,9 +1047,9 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Search the collection for a given value and return the corresponding key if successful. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|(callable(TValue,TKey): bool)  $value | ||||
|      * @param  bool  $strict | ||||
|      * @return mixed | ||||
|      * @return TKey|bool | ||||
|      */ | ||||
|     public function search($value, $strict = false) | ||||
|     { | ||||
| @@ -1012,7 +1070,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * Get and remove the first N items from the collection. | ||||
|      * | ||||
|      * @param  int  $count | ||||
|      * @return mixed | ||||
|      * @return static<int, TValue>|TValue|null | ||||
|      */ | ||||
|     public function shift($count = 1) | ||||
|     { | ||||
| @@ -1051,7 +1109,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * | ||||
|      * @param  int  $size | ||||
|      * @param  int  $step | ||||
|      * @return static | ||||
|      * @return static<int, static> | ||||
|      */ | ||||
|     public function sliding($size = 2, $step = 1) | ||||
|     { | ||||
| @@ -1076,7 +1134,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Skip items in the collection until the given condition is met. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|callable(TValue,TKey): bool  $value | ||||
|      * @return static | ||||
|      */ | ||||
|     public function skipUntil($value) | ||||
| @@ -1087,7 +1145,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Skip items in the collection while the given condition is met. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|callable(TValue,TKey): bool  $value | ||||
|      * @return static | ||||
|      */ | ||||
|     public function skipWhile($value) | ||||
| @@ -1111,7 +1169,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * Split a collection into a certain number of groups. | ||||
|      * | ||||
|      * @param  int  $numberOfGroups | ||||
|      * @return static | ||||
|      * @return static<int, static> | ||||
|      */ | ||||
|     public function split($numberOfGroups) | ||||
|     { | ||||
| @@ -1148,7 +1206,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * Split a collection into a certain number of groups, and fill the first groups completely. | ||||
|      * | ||||
|      * @param  int  $numberOfGroups | ||||
|      * @return static | ||||
|      * @return static<int, static> | ||||
|      */ | ||||
|     public function splitIn($numberOfGroups) | ||||
|     { | ||||
| @@ -1158,10 +1216,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  (callable(TValue, TKey): bool)|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return mixed | ||||
|      * @return TValue | ||||
|      * | ||||
|      * @throws \Illuminate\Support\ItemNotFoundException | ||||
|      * @throws \Illuminate\Support\MultipleItemsFoundException | ||||
| @@ -1172,14 +1230,16 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|             ? $this->operatorForWhere(...func_get_args()) | ||||
|             : $key; | ||||
|  | ||||
|         $items = $this->when($filter)->filter($filter); | ||||
|         $items = $this->unless($filter == null)->filter($filter); | ||||
|  | ||||
|         if ($items->isEmpty()) { | ||||
|         $count = $items->count(); | ||||
|  | ||||
|         if ($count === 0) { | ||||
|             throw new ItemNotFoundException; | ||||
|         } | ||||
|  | ||||
|         if ($items->count() > 1) { | ||||
|             throw new MultipleItemsFoundException; | ||||
|         if ($count > 1) { | ||||
|             throw new MultipleItemsFoundException($count); | ||||
|         } | ||||
|  | ||||
|         return $items->first(); | ||||
| @@ -1188,10 +1248,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get the first item in the collection but throw an exception if no matching items exist. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  (callable(TValue, TKey): bool)|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return mixed | ||||
|      * @return TValue | ||||
|      * | ||||
|      * @throws \Illuminate\Support\ItemNotFoundException | ||||
|      */ | ||||
| @@ -1216,7 +1276,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * Chunk the collection into chunks of the given size. | ||||
|      * | ||||
|      * @param  int  $size | ||||
|      * @return static | ||||
|      * @return static<int, static> | ||||
|      */ | ||||
|     public function chunk($size) | ||||
|     { | ||||
| @@ -1236,8 +1296,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Chunk the collection into chunks with a callback. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @param  callable(TValue, TKey, static<int, TValue>): bool  $callback | ||||
|      * @return static<int, static<int, TValue>> | ||||
|      */ | ||||
|     public function chunkWhile(callable $callback) | ||||
|     { | ||||
| @@ -1249,7 +1309,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Sort through each item with a callback. | ||||
|      * | ||||
|      * @param  callable|int|null  $callback | ||||
|      * @param  (callable(TValue, TValue): int)|null|int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function sort($callback = null) | ||||
| @@ -1281,7 +1341,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Sort the collection using the given callback. | ||||
|      * | ||||
|      * @param  callable|array|string  $callback | ||||
|      * @param  array<array-key, (callable(TValue, TValue): mixed)|(callable(TValue, TKey): mixed)|string|array{string, string}>|(callable(TValue, TKey): mixed)|string  $callback | ||||
|      * @param  int  $options | ||||
|      * @param  bool  $descending | ||||
|      * @return static | ||||
| @@ -1319,14 +1379,14 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Sort the collection using multiple comparisons. | ||||
|      * | ||||
|      * @param  array  $comparisons | ||||
|      * @param  array<array-key, (callable(TValue, TValue): mixed)|(callable(TValue, TKey): mixed)|string|array{string, string}>  $comparisons | ||||
|      * @return static | ||||
|      */ | ||||
|     protected function sortByMany(array $comparisons = []) | ||||
|     { | ||||
|         $items = $this->items; | ||||
|  | ||||
|         usort($items, function ($a, $b) use ($comparisons) { | ||||
|         uasort($items, function ($a, $b) use ($comparisons) { | ||||
|             foreach ($comparisons as $comparison) { | ||||
|                 $comparison = Arr::wrap($comparison); | ||||
|  | ||||
| @@ -1335,8 +1395,6 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|                 $ascending = Arr::get($comparison, 1, true) === true || | ||||
|                              Arr::get($comparison, 1, true) === 'asc'; | ||||
|  | ||||
|                 $result = 0; | ||||
|  | ||||
|                 if (! is_string($prop) && is_callable($prop)) { | ||||
|                     $result = $prop($a, $b); | ||||
|                 } else { | ||||
| @@ -1363,7 +1421,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Sort the collection in descending order using the given callback. | ||||
|      * | ||||
|      * @param  callable|string  $callback | ||||
|      * @param  array<array-key, (callable(TValue, TValue): mixed)|(callable(TValue, TKey): mixed)|string|array{string, string}>|(callable(TValue, TKey): mixed)|string  $callback | ||||
|      * @param  int  $options | ||||
|      * @return static | ||||
|      */ | ||||
| @@ -1402,7 +1460,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Sort the collection keys using a callback. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable(TKey, TKey): int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function sortKeysUsing(callable $callback) | ||||
| @@ -1419,7 +1477,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * | ||||
|      * @param  int  $offset | ||||
|      * @param  int|null  $length | ||||
|      * @param  mixed  $replacement | ||||
|      * @param  array<array-key, TValue>  $replacement | ||||
|      * @return static | ||||
|      */ | ||||
|     public function splice($offset, $length = null, $replacement = []) | ||||
| @@ -1449,7 +1507,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Take items in the collection until the given condition is met. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|callable(TValue,TKey): bool  $value | ||||
|      * @return static | ||||
|      */ | ||||
|     public function takeUntil($value) | ||||
| @@ -1460,7 +1518,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Take items in the collection while the given condition is met. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|callable(TValue,TKey): bool  $value | ||||
|      * @return static | ||||
|      */ | ||||
|     public function takeWhile($value) | ||||
| @@ -1471,7 +1529,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Transform each item in the collection using a callback. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable(TValue, TKey): TValue  $callback | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function transform(callable $callback) | ||||
| @@ -1494,7 +1552,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Return only unique items from the collection array. | ||||
|      * | ||||
|      * @param  string|callable|null  $key | ||||
|      * @param  (callable(TValue, TKey): mixed)|string|null  $key | ||||
|      * @param  bool  $strict | ||||
|      * @return static | ||||
|      */ | ||||
| @@ -1520,7 +1578,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Reset the keys on the underlying array. | ||||
|      * | ||||
|      * @return static | ||||
|      * @return static<int, TValue> | ||||
|      */ | ||||
|     public function values() | ||||
|     { | ||||
| @@ -1533,8 +1591,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); | ||||
|      *      => [[1, 4], [2, 5], [3, 6]] | ||||
|      * | ||||
|      * @param  mixed  ...$items | ||||
|      * @return static | ||||
|      * @template TZipValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<array-key, TZipValue>|iterable<array-key, TZipValue>  ...$items | ||||
|      * @return static<int, static<int, TValue|TZipValue>> | ||||
|      */ | ||||
|     public function zip($items) | ||||
|     { | ||||
| @@ -1552,9 +1612,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Pad collection to the specified length with a value. | ||||
|      * | ||||
|      * @template TPadValue | ||||
|      * | ||||
|      * @param  int  $size | ||||
|      * @param  mixed  $value | ||||
|      * @return static | ||||
|      * @param  TPadValue  $value | ||||
|      * @return static<int, TValue|TPadValue> | ||||
|      */ | ||||
|     public function pad($size, $value) | ||||
|     { | ||||
| @@ -1564,10 +1626,9 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get an iterator for the items. | ||||
|      * | ||||
|      * @return \ArrayIterator | ||||
|      * @return \ArrayIterator<TKey, TValue> | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function getIterator() | ||||
|     public function getIterator(): Traversable | ||||
|     { | ||||
|         return new ArrayIterator($this->items); | ||||
|     } | ||||
| @@ -1577,8 +1638,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function count() | ||||
|     public function count(): int | ||||
|     { | ||||
|         return count($this->items); | ||||
|     } | ||||
| @@ -1586,8 +1646,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Count the number of items in the collection by a field or using a callback. | ||||
|      * | ||||
|      * @param  callable|string  $countBy | ||||
|      * @return static | ||||
|      * @param  (callable(TValue, TKey): mixed)|string|null  $countBy | ||||
|      * @return static<array-key, int> | ||||
|      */ | ||||
|     public function countBy($countBy = null) | ||||
|     { | ||||
| @@ -1597,7 +1657,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Add an item to the collection. | ||||
|      * | ||||
|      * @param  mixed  $item | ||||
|      * @param  TValue  $item | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function add($item) | ||||
| @@ -1610,7 +1670,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get a base Support collection instance from this collection. | ||||
|      * | ||||
|      * @return \Illuminate\Support\Collection | ||||
|      * @return \Illuminate\Support\Collection<TKey, TValue> | ||||
|      */ | ||||
|     public function toBase() | ||||
|     { | ||||
| @@ -1620,11 +1680,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Determine if an item exists at an offset. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  TKey  $key | ||||
|      * @return bool | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetExists($key) | ||||
|     public function offsetExists($key): bool | ||||
|     { | ||||
|         return isset($this->items[$key]); | ||||
|     } | ||||
| @@ -1632,11 +1691,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Get an item at a given offset. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @return mixed | ||||
|      * @param  TKey  $key | ||||
|      * @return TValue | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetGet($key) | ||||
|     public function offsetGet($key): mixed | ||||
|     { | ||||
|         return $this->items[$key]; | ||||
|     } | ||||
| @@ -1644,12 +1702,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Set the item at a given offset. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  mixed  $value | ||||
|      * @param  TKey|null  $key | ||||
|      * @param  TValue  $value | ||||
|      * @return void | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetSet($key, $value) | ||||
|     public function offsetSet($key, $value): void | ||||
|     { | ||||
|         if (is_null($key)) { | ||||
|             $this->items[] = $value; | ||||
| @@ -1661,11 +1718,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl | ||||
|     /** | ||||
|      * Unset the item at a given offset. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  TKey  $key | ||||
|      * @return void | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetUnset($key) | ||||
|     public function offsetUnset($key): void | ||||
|     { | ||||
|         unset($this->items[$key]); | ||||
|     } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,63 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Support; | ||||
|  | ||||
| /** | ||||
|  * @mixin \Illuminate\Support\Enumerable | ||||
|  */ | ||||
| class HigherOrderWhenProxy | ||||
| { | ||||
|     /** | ||||
|      * The collection being operated on. | ||||
|      * | ||||
|      * @var \Illuminate\Support\Enumerable | ||||
|      */ | ||||
|     protected $collection; | ||||
|  | ||||
|     /** | ||||
|      * The condition for proxying. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $condition; | ||||
|  | ||||
|     /** | ||||
|      * Create a new proxy instance. | ||||
|      * | ||||
|      * @param  \Illuminate\Support\Enumerable  $collection | ||||
|      * @param  bool  $condition | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(Enumerable $collection, $condition) | ||||
|     { | ||||
|         $this->condition = $condition; | ||||
|         $this->collection = $collection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Proxy accessing an attribute onto the collection. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function __get($key) | ||||
|     { | ||||
|         return $this->condition | ||||
|             ? $this->collection->{$key} | ||||
|             : $this->collection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Proxy a method call onto the collection. | ||||
|      * | ||||
|      * @param  string  $method | ||||
|      * @param  array  $parameters | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function __call($method, $parameters) | ||||
|     { | ||||
|         return $this->condition | ||||
|             ? $this->collection->{$method}(...$parameters) | ||||
|             : $this->collection; | ||||
|     } | ||||
| } | ||||
| @@ -5,27 +5,39 @@ namespace Illuminate\Support; | ||||
| use ArrayIterator; | ||||
| use Closure; | ||||
| use DateTimeInterface; | ||||
| use Generator; | ||||
| use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString; | ||||
| use Illuminate\Support\Traits\EnumeratesValues; | ||||
| use Illuminate\Support\Traits\Macroable; | ||||
| use InvalidArgumentException; | ||||
| use IteratorAggregate; | ||||
| use stdClass; | ||||
| use Traversable; | ||||
|  | ||||
| /** | ||||
|  * @template TKey of array-key | ||||
|  * @template TValue | ||||
|  * | ||||
|  * @implements \Illuminate\Support\Enumerable<TKey, TValue> | ||||
|  */ | ||||
| class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
| { | ||||
|     /** | ||||
|      * @use \Illuminate\Support\Traits\EnumeratesValues<TKey, TValue> | ||||
|      */ | ||||
|     use EnumeratesValues, Macroable; | ||||
|  | ||||
|     /** | ||||
|      * The source from which to generate items. | ||||
|      * | ||||
|      * @var callable|static | ||||
|      * @var (Closure(): \Generator<TKey, TValue, mixed, void>)|static|array<TKey, TValue> | ||||
|      */ | ||||
|     public $source; | ||||
|  | ||||
|     /** | ||||
|      * Create a new lazy collection instance. | ||||
|      * | ||||
|      * @param  mixed  $source | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>|(Closure(): \Generator<TKey, TValue, mixed, void>)|self<TKey, TValue>|array<TKey, TValue>|null  $source | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($source = null) | ||||
| @@ -34,17 +46,35 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|             $this->source = $source; | ||||
|         } elseif (is_null($source)) { | ||||
|             $this->source = static::empty(); | ||||
|         } elseif ($source instanceof Generator) { | ||||
|             throw new InvalidArgumentException( | ||||
|                 'Generators should not be passed directly to LazyCollection. Instead, pass a generator function.' | ||||
|             ); | ||||
|         } else { | ||||
|             $this->source = $this->getArrayableItems($source); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new collection instance if the value isn't one already. | ||||
|      * | ||||
|      * @template TMakeKey of array-key | ||||
|      * @template TMakeValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TMakeKey, TMakeValue>|iterable<TMakeKey, TMakeValue>|(Closure(): \Generator<TMakeKey, TMakeValue, mixed, void>)|self<TMakeKey, TMakeValue>|array<TMakeKey, TMakeValue>|null  $items | ||||
|      * @return static<TMakeKey, TMakeValue> | ||||
|      */ | ||||
|     public static function make($items = []) | ||||
|     { | ||||
|         return new static($items); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a collection with the given range. | ||||
|      * | ||||
|      * @param  int  $from | ||||
|      * @param  int  $to | ||||
|      * @return static | ||||
|      * @return static<int, int> | ||||
|      */ | ||||
|     public static function range($from, $to) | ||||
|     { | ||||
| @@ -64,7 +94,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get all items in the enumerable. | ||||
|      * | ||||
|      * @return array | ||||
|      * @return array<TKey, TValue> | ||||
|      */ | ||||
|     public function all() | ||||
|     { | ||||
| @@ -126,8 +156,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the average value of a given key. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @return mixed | ||||
|      * @param  (callable(TValue): float|int)|string|null  $callback | ||||
|      * @return float|int|null | ||||
|      */ | ||||
|     public function avg($callback = null) | ||||
|     { | ||||
| @@ -137,8 +167,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the median of a given key. | ||||
|      * | ||||
|      * @param  string|array|null  $key | ||||
|      * @return mixed | ||||
|      * @param  string|array<array-key, string>|null  $key | ||||
|      * @return float|int|null | ||||
|      */ | ||||
|     public function median($key = null) | ||||
|     { | ||||
| @@ -148,8 +178,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the mode of a given key. | ||||
|      * | ||||
|      * @param  string|array|null  $key | ||||
|      * @return array|null | ||||
|      * @param  string|array<string>|null  $key | ||||
|      * @return array<int, float|int>|null | ||||
|      */ | ||||
|     public function mode($key = null) | ||||
|     { | ||||
| @@ -159,7 +189,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Collapse the collection of items into a single array. | ||||
|      * | ||||
|      * @return static | ||||
|      * @return static<int, mixed> | ||||
|      */ | ||||
|     public function collapse() | ||||
|     { | ||||
| @@ -177,7 +207,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Determine if an item exists in the enumerable. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  (callable(TValue, TKey): bool)|TValue|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return bool | ||||
| @@ -187,6 +217,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|         if (func_num_args() === 1 && $this->useAsCallable($key)) { | ||||
|             $placeholder = new stdClass; | ||||
|  | ||||
|             /** @var callable $key */ | ||||
|             return $this->first($key, $placeholder) !== $placeholder; | ||||
|         } | ||||
|  | ||||
| @@ -205,6 +236,32 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|         return $this->contains($this->operatorForWhere(...func_get_args())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if an item exists, using strict comparison. | ||||
|      * | ||||
|      * @param  (callable(TValue): bool)|TValue|array-key  $key | ||||
|      * @param  TValue|null  $value | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function containsStrict($key, $value = null) | ||||
|     { | ||||
|         if (func_num_args() === 2) { | ||||
|             return $this->contains(fn ($item) => data_get($item, $key) === $value); | ||||
|         } | ||||
|  | ||||
|         if ($this->useAsCallable($key)) { | ||||
|             return ! is_null($this->first($key)); | ||||
|         } | ||||
|  | ||||
|         foreach ($this as $item) { | ||||
|             if ($item === $key) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if an item is not contained in the enumerable. | ||||
|      * | ||||
| @@ -221,8 +278,11 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Cross join the given iterables, returning all possible permutations. | ||||
|      * | ||||
|      * @param  array  ...$arrays | ||||
|      * @return static | ||||
|      * @template TCrossJoinKey | ||||
|      * @template TCrossJoinValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TCrossJoinKey, TCrossJoinValue>|iterable<TCrossJoinKey, TCrossJoinValue>  ...$arrays | ||||
|      * @return static<int, array<int, TValue|TCrossJoinValue>> | ||||
|      */ | ||||
|     public function crossJoin(...$arrays) | ||||
|     { | ||||
| @@ -232,8 +292,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Count the number of items in the collection by a field or using a callback. | ||||
|      * | ||||
|      * @param  callable|string  $countBy | ||||
|      * @return static | ||||
|      * @param  (callable(TValue, TKey): mixed)|string|null  $countBy | ||||
|      * @return static<array-key, int> | ||||
|      */ | ||||
|     public function countBy($countBy = null) | ||||
|     { | ||||
| @@ -261,7 +321,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the items that are not present in the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<array-key, TValue>|iterable<array-key, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diff($items) | ||||
| @@ -272,8 +332,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the items that are not present in the given items, using the callback. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  callable  $callback | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<array-key, TValue>|iterable<array-key, TValue>  $items | ||||
|      * @param  callable(TValue, TValue): int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffUsing($items, callable $callback) | ||||
| @@ -284,7 +344,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the items whose keys and values are not present in the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffAssoc($items) | ||||
| @@ -295,8 +355,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the items whose keys and values are not present in the given items, using the callback. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  callable  $callback | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @param  callable(TKey, TKey): int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffAssocUsing($items, callable $callback) | ||||
| @@ -307,7 +367,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the items whose keys are not present in the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffKeys($items) | ||||
| @@ -318,8 +378,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the items whose keys are not present in the given items, using the callback. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  callable  $callback | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @param  callable(TKey, TKey): int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function diffKeysUsing($items, callable $callback) | ||||
| @@ -330,7 +390,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Retrieve duplicate items. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @param  (callable(TValue): bool)|string|null  $callback | ||||
|      * @param  bool  $strict | ||||
|      * @return static | ||||
|      */ | ||||
| @@ -342,7 +402,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Retrieve duplicate items using strict comparison. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @param  (callable(TValue): bool)|string|null  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function duplicatesStrict($callback = null) | ||||
| @@ -353,7 +413,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get all items except for those with the specified keys. | ||||
|      * | ||||
|      * @param  mixed  $keys | ||||
|      * @param  \Illuminate\Support\Enumerable<array-key, TKey>|array<array-key, TKey>  $keys | ||||
|      * @return static | ||||
|      */ | ||||
|     public function except($keys) | ||||
| @@ -364,7 +424,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Run a filter over each of the items. | ||||
|      * | ||||
|      * @param  callable|null  $callback | ||||
|      * @param  (callable(TValue, TKey): bool)|null  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function filter(callable $callback = null) | ||||
| @@ -387,9 +447,11 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the first item from the enumerable passing the given truth test. | ||||
|      * | ||||
|      * @param  callable|null  $callback | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TFirstDefault | ||||
|      * | ||||
|      * @param  (callable(TValue): bool)|null  $callback | ||||
|      * @param  TFirstDefault|(\Closure(): TFirstDefault)  $default | ||||
|      * @return TValue|TFirstDefault | ||||
|      */ | ||||
|     public function first(callable $callback = null, $default = null) | ||||
|     { | ||||
| @@ -416,7 +478,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * Get a flattened list of the items in the collection. | ||||
|      * | ||||
|      * @param  int  $depth | ||||
|      * @return static | ||||
|      * @return static<int, mixed> | ||||
|      */ | ||||
|     public function flatten($depth = INF) | ||||
|     { | ||||
| @@ -438,7 +500,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Flip the items in the collection. | ||||
|      * | ||||
|      * @return static | ||||
|      * @return static<TValue, TKey> | ||||
|      */ | ||||
|     public function flip() | ||||
|     { | ||||
| @@ -452,9 +514,11 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get an item by key. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TGetDefault | ||||
|      * | ||||
|      * @param  TKey|null  $key | ||||
|      * @param  TGetDefault|(\Closure(): TGetDefault)  $default | ||||
|      * @return TValue|TGetDefault | ||||
|      */ | ||||
|     public function get($key, $default = null) | ||||
|     { | ||||
| @@ -474,9 +538,9 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Group an associative array by a field or using a callback. | ||||
|      * | ||||
|      * @param  array|callable|string  $groupBy | ||||
|      * @param  (callable(TValue, TKey): array-key)|array|string  $groupBy | ||||
|      * @param  bool  $preserveKeys | ||||
|      * @return static | ||||
|      * @return static<array-key, static<array-key, TValue>> | ||||
|      */ | ||||
|     public function groupBy($groupBy, $preserveKeys = false) | ||||
|     { | ||||
| @@ -486,8 +550,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Key an associative array by a field or using a callback. | ||||
|      * | ||||
|      * @param  callable|string  $keyBy | ||||
|      * @return static | ||||
|      * @param  (callable(TValue, TKey): array-key)|array|string  $keyBy | ||||
|      * @return static<array-key, TValue> | ||||
|      */ | ||||
|     public function keyBy($keyBy) | ||||
|     { | ||||
| @@ -548,7 +612,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Concatenate values of a given key as a string. | ||||
|      * | ||||
|      * @param  string  $value | ||||
|      * @param  callable|string  $value | ||||
|      * @param  string|null  $glue | ||||
|      * @return string | ||||
|      */ | ||||
| @@ -560,7 +624,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Intersect the collection with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function intersect($items) | ||||
| @@ -571,7 +635,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Intersect the collection with the given items by key. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function intersectByKeys($items) | ||||
| @@ -614,7 +678,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the keys of the collection items. | ||||
|      * | ||||
|      * @return static | ||||
|      * @return static<int, TKey> | ||||
|      */ | ||||
|     public function keys() | ||||
|     { | ||||
| @@ -628,9 +692,11 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the last item from the collection. | ||||
|      * | ||||
|      * @param  callable|null  $callback | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      * @template TLastDefault | ||||
|      * | ||||
|      * @param  (callable(TValue, TKey): bool)|null  $callback | ||||
|      * @param  TLastDefault|(\Closure(): TLastDefault)  $default | ||||
|      * @return TValue|TLastDefault | ||||
|      */ | ||||
|     public function last(callable $callback = null, $default = null) | ||||
|     { | ||||
| @@ -648,9 +714,9 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the values of a given key. | ||||
|      * | ||||
|      * @param  string|array  $value | ||||
|      * @param  string|array<array-key, string>  $value | ||||
|      * @param  string|null  $key | ||||
|      * @return static | ||||
|      * @return static<int, mixed> | ||||
|      */ | ||||
|     public function pluck($value, $key = null) | ||||
|     { | ||||
| @@ -678,8 +744,10 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Run a map over each of the items. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TMapValue | ||||
|      * | ||||
|      * @param  callable(TValue, TKey): TMapValue  $callback | ||||
|      * @return static<TKey, TMapValue> | ||||
|      */ | ||||
|     public function map(callable $callback) | ||||
|     { | ||||
| @@ -695,8 +763,11 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * | ||||
|      * The callback should return an associative array with a single key/value pair. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TMapToDictionaryKey of array-key | ||||
|      * @template TMapToDictionaryValue | ||||
|      * | ||||
|      * @param  callable(TValue, TKey): array<TMapToDictionaryKey, TMapToDictionaryValue>  $callback | ||||
|      * @return static<TMapToDictionaryKey, array<int, TMapToDictionaryValue>> | ||||
|      */ | ||||
|     public function mapToDictionary(callable $callback) | ||||
|     { | ||||
| @@ -708,8 +779,11 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * | ||||
|      * The callback should return an associative array with a single key/value pair. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TMapWithKeysKey of array-key | ||||
|      * @template TMapWithKeysValue | ||||
|      * | ||||
|      * @param  callable(TValue, TKey): array<TMapWithKeysKey, TMapWithKeysValue>  $callback | ||||
|      * @return static<TMapWithKeysKey, TMapWithKeysValue> | ||||
|      */ | ||||
|     public function mapWithKeys(callable $callback) | ||||
|     { | ||||
| @@ -723,7 +797,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Merge the collection with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function merge($items) | ||||
| @@ -734,8 +808,10 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Recursively merge the collection with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @return static | ||||
|      * @template TMergeRecursiveValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TMergeRecursiveValue>|iterable<TKey, TMergeRecursiveValue>  $items | ||||
|      * @return static<TKey, TValue|TMergeRecursiveValue> | ||||
|      */ | ||||
|     public function mergeRecursive($items) | ||||
|     { | ||||
| @@ -745,8 +821,10 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Create a collection by using this collection for keys and another for its values. | ||||
|      * | ||||
|      * @param  mixed  $values | ||||
|      * @return static | ||||
|      * @template TCombineValue | ||||
|      * | ||||
|      * @param  \IteratorAggregate<array-key, TCombineValue>|array<array-key, TCombineValue>|(callable(): \Generator<array-key, TCombineValue>)  $values | ||||
|      * @return static<TValue, TCombineValue> | ||||
|      */ | ||||
|     public function combine($values) | ||||
|     { | ||||
| @@ -776,7 +854,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Union the collection with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function union($items) | ||||
| @@ -809,7 +887,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the items with the specified keys. | ||||
|      * | ||||
|      * @param  mixed  $keys | ||||
|      * @param  \Illuminate\Support\Enumerable<array-key, TKey>|array<array-key, TKey>|string  $keys | ||||
|      * @return static | ||||
|      */ | ||||
|     public function only($keys) | ||||
| @@ -844,7 +922,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Push all of the given items onto the collection. | ||||
|      * | ||||
|      * @param  iterable  $source | ||||
|      * @param  iterable<array-key, TValue>  $source | ||||
|      * @return static | ||||
|      */ | ||||
|     public function concat($source) | ||||
| @@ -859,7 +937,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * Get one or a specified number of items randomly from the collection. | ||||
|      * | ||||
|      * @param  int|null  $number | ||||
|      * @return static|mixed | ||||
|      * @return static<int, TValue>|TValue | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      */ | ||||
| @@ -873,7 +951,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Replace the collection items with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function replace($items) | ||||
| @@ -900,7 +978,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Recursively replace the collection items with the given items. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>  $items | ||||
|      * @return static | ||||
|      */ | ||||
|     public function replaceRecursive($items) | ||||
| @@ -921,12 +999,13 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Search the collection for a given value and return the corresponding key if successful. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|(callable(TValue,TKey): bool)  $value | ||||
|      * @param  bool  $strict | ||||
|      * @return mixed | ||||
|      * @return TKey|bool | ||||
|      */ | ||||
|     public function search($value, $strict = false) | ||||
|     { | ||||
|         /** @var (callable(TValue,TKey): bool) $predicate */ | ||||
|         $predicate = $this->useAsCallable($value) | ||||
|             ? $value | ||||
|             : function ($item) use ($value, $strict) { | ||||
| @@ -958,7 +1037,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * | ||||
|      * @param  int  $size | ||||
|      * @param  int  $step | ||||
|      * @return static | ||||
|      * @return static<int, static> | ||||
|      */ | ||||
|     public function sliding($size = 2, $step = 1) | ||||
|     { | ||||
| @@ -971,7 +1050,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|                 $chunk[$iterator->key()] = $iterator->current(); | ||||
|  | ||||
|                 if (count($chunk) == $size) { | ||||
|                     yield tap(new static($chunk), function () use (&$chunk, $step) { | ||||
|                     yield (new static($chunk))->tap(function () use (&$chunk, $step) { | ||||
|                         $chunk = array_slice($chunk, $step, null, true); | ||||
|                     }); | ||||
|  | ||||
| @@ -1018,7 +1097,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Skip items in the collection until the given condition is met. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|callable(TValue,TKey): bool  $value | ||||
|      * @return static | ||||
|      */ | ||||
|     public function skipUntil($value) | ||||
| @@ -1031,7 +1110,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Skip items in the collection while the given condition is met. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|callable(TValue,TKey): bool  $value | ||||
|      * @return static | ||||
|      */ | ||||
|     public function skipWhile($value) | ||||
| @@ -1075,7 +1154,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * Split a collection into a certain number of groups. | ||||
|      * | ||||
|      * @param  int  $numberOfGroups | ||||
|      * @return static | ||||
|      * @return static<int, static> | ||||
|      */ | ||||
|     public function split($numberOfGroups) | ||||
|     { | ||||
| @@ -1085,10 +1164,10 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  (callable(TValue, TKey): bool)|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return mixed | ||||
|      * @return TValue | ||||
|      * | ||||
|      * @throws \Illuminate\Support\ItemNotFoundException | ||||
|      * @throws \Illuminate\Support\MultipleItemsFoundException | ||||
| @@ -1100,7 +1179,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|             : $key; | ||||
|  | ||||
|         return $this | ||||
|             ->when($filter) | ||||
|             ->unless($filter == null) | ||||
|             ->filter($filter) | ||||
|             ->take(2) | ||||
|             ->collect() | ||||
| @@ -1110,10 +1189,10 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the first item in the collection but throw an exception if no matching items exist. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  (callable(TValue, TKey): bool)|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return mixed | ||||
|      * @return TValue | ||||
|      * | ||||
|      * @throws \Illuminate\Support\ItemNotFoundException | ||||
|      */ | ||||
| @@ -1124,7 +1203,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|             : $key; | ||||
|  | ||||
|         return $this | ||||
|             ->when($filter) | ||||
|             ->unless($filter == null) | ||||
|             ->filter($filter) | ||||
|             ->take(1) | ||||
|             ->collect() | ||||
| @@ -1135,7 +1214,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * Chunk the collection into chunks of the given size. | ||||
|      * | ||||
|      * @param  int  $size | ||||
|      * @return static | ||||
|      * @return static<int, static> | ||||
|      */ | ||||
|     public function chunk($size) | ||||
|     { | ||||
| @@ -1174,7 +1253,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * Split a collection into a certain number of groups, and fill the first groups completely. | ||||
|      * | ||||
|      * @param  int  $numberOfGroups | ||||
|      * @return static | ||||
|      * @return static<int, static> | ||||
|      */ | ||||
|     public function splitIn($numberOfGroups) | ||||
|     { | ||||
| @@ -1184,8 +1263,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Chunk the collection into chunks with a callback. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @param  callable(TValue, TKey, Collection<TKey, TValue>): bool  $callback | ||||
|      * @return static<int, static<int, TValue>> | ||||
|      */ | ||||
|     public function chunkWhile(callable $callback) | ||||
|     { | ||||
| @@ -1221,7 +1300,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Sort through each item with a callback. | ||||
|      * | ||||
|      * @param  callable|null|int  $callback | ||||
|      * @param  (callable(TValue, TValue): int)|null|int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function sort($callback = null) | ||||
| @@ -1243,7 +1322,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Sort the collection using the given callback. | ||||
|      * | ||||
|      * @param  callable|string  $callback | ||||
|      * @param  array<array-key, (callable(TValue, TValue): mixed)|(callable(TValue, TKey): mixed)|string|array{string, string}>|(callable(TValue, TKey): mixed)|string  $callback | ||||
|      * @param  int  $options | ||||
|      * @param  bool  $descending | ||||
|      * @return static | ||||
| @@ -1256,7 +1335,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Sort the collection in descending order using the given callback. | ||||
|      * | ||||
|      * @param  callable|string  $callback | ||||
|      * @param  array<array-key, (callable(TValue, TValue): mixed)|(callable(TValue, TKey): mixed)|string|array{string, string}>|(callable(TValue, TKey): mixed)|string  $callback | ||||
|      * @param  int  $options | ||||
|      * @return static | ||||
|      */ | ||||
| @@ -1291,7 +1370,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Sort the collection keys using a callback. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable(TKey, TKey): int  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function sortKeysUsing(callable $callback) | ||||
| @@ -1331,11 +1410,12 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Take items in the collection until the given condition is met. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|callable(TValue,TKey): bool  $value | ||||
|      * @return static | ||||
|      */ | ||||
|     public function takeUntil($value) | ||||
|     { | ||||
|         /** @var callable(TValue, TKey): bool $callback */ | ||||
|         $callback = $this->useAsCallable($value) ? $value : $this->equality($value); | ||||
|  | ||||
|         return new static(function () use ($callback) { | ||||
| @@ -1359,19 +1439,30 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     { | ||||
|         $timeout = $timeout->getTimestamp(); | ||||
|  | ||||
|         return $this->takeWhile(function () use ($timeout) { | ||||
|             return $this->now() < $timeout; | ||||
|         return new static(function () use ($timeout) { | ||||
|             if ($this->now() >= $timeout) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             foreach ($this as $key => $value) { | ||||
|                 yield $key => $value; | ||||
|  | ||||
|                 if ($this->now() >= $timeout) { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Take items in the collection while the given condition is met. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  TValue|callable(TValue,TKey): bool  $value | ||||
|      * @return static | ||||
|      */ | ||||
|     public function takeWhile($value) | ||||
|     { | ||||
|         /** @var callable(TValue, TKey): bool $callback */ | ||||
|         $callback = $this->useAsCallable($value) ? $value : $this->equality($value); | ||||
|  | ||||
|         return $this->takeUntil(function ($item, $key) use ($callback) { | ||||
| @@ -1382,7 +1473,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Pass each item in the collection to the given callback, lazily. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable(TValue, TKey): mixed  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function tapEach(callable $callback) | ||||
| @@ -1409,7 +1500,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Return only unique items from the collection array. | ||||
|      * | ||||
|      * @param  string|callable|null  $key | ||||
|      * @param  (callable(TValue, TKey): mixed)|string|null  $key | ||||
|      * @param  bool  $strict | ||||
|      * @return static | ||||
|      */ | ||||
| @@ -1433,7 +1524,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Reset the keys on the underlying array. | ||||
|      * | ||||
|      * @return static | ||||
|      * @return static<int, TValue> | ||||
|      */ | ||||
|     public function values() | ||||
|     { | ||||
| @@ -1450,8 +1541,10 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * e.g. new LazyCollection([1, 2, 3])->zip([4, 5, 6]); | ||||
|      *      => [[1, 4], [2, 5], [3, 6]] | ||||
|      * | ||||
|      * @param  mixed  ...$items | ||||
|      * @return static | ||||
|      * @template TZipValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<array-key, TZipValue>|iterable<array-key, TZipValue>  ...$items | ||||
|      * @return static<int, static<int, TValue|TZipValue>> | ||||
|      */ | ||||
|     public function zip($items) | ||||
|     { | ||||
| @@ -1473,9 +1566,11 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Pad collection to the specified length with a value. | ||||
|      * | ||||
|      * @template TPadValue | ||||
|      * | ||||
|      * @param  int  $size | ||||
|      * @param  mixed  $value | ||||
|      * @return static | ||||
|      * @param  TPadValue  $value | ||||
|      * @return static<int, TValue|TPadValue> | ||||
|      */ | ||||
|     public function pad($size, $value) | ||||
|     { | ||||
| @@ -1501,10 +1596,9 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Get the values iterator. | ||||
|      * | ||||
|      * @return \Traversable | ||||
|      * @return \Traversable<TKey, TValue> | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function getIterator() | ||||
|     public function getIterator(): Traversable | ||||
|     { | ||||
|         return $this->makeIterator($this->source); | ||||
|     } | ||||
| @@ -1514,8 +1608,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function count() | ||||
|     public function count(): int | ||||
|     { | ||||
|         if (is_array($this->source)) { | ||||
|             return count($this->source); | ||||
| @@ -1527,8 +1620,11 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Make an iterator from the given source. | ||||
|      * | ||||
|      * @param  mixed  $source | ||||
|      * @return \Traversable | ||||
|      * @template TIteratorKey of array-key | ||||
|      * @template TIteratorValue | ||||
|      * | ||||
|      * @param  \IteratorAggregate<TIteratorKey, TIteratorValue>|array<TIteratorKey, TIteratorValue>|(callable(): \Generator<TIteratorKey, TIteratorValue>)  $source | ||||
|      * @return \Traversable<TIteratorKey, TIteratorValue> | ||||
|      */ | ||||
|     protected function makeIterator($source) | ||||
|     { | ||||
| @@ -1546,9 +1642,9 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|     /** | ||||
|      * Explode the "value" and "key" arguments passed to "pluck". | ||||
|      * | ||||
|      * @param  string|array  $value | ||||
|      * @param  string|array|null  $key | ||||
|      * @return array | ||||
|      * @param  string|string[]  $value | ||||
|      * @param  string|string[]|null  $key | ||||
|      * @return array{string[],string[]|null} | ||||
|      */ | ||||
|     protected function explodePluckParameters($value, $key) | ||||
|     { | ||||
| @@ -1563,7 +1659,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable | ||||
|      * Pass this lazy collection through a method on the collection class. | ||||
|      * | ||||
|      * @param  string  $method | ||||
|      * @param  array  $params | ||||
|      * @param  array<mixed>  $params | ||||
|      * @return static | ||||
|      */ | ||||
|     protected function passthru($method, array $params) | ||||
|   | ||||
| @@ -6,4 +6,35 @@ use RuntimeException; | ||||
|  | ||||
| class MultipleItemsFoundException extends RuntimeException | ||||
| { | ||||
|     /** | ||||
|      * The number of items found. | ||||
|      * | ||||
|      * @var int | ||||
|      */ | ||||
|     public $count; | ||||
|  | ||||
|     /** | ||||
|      * Create a new exception instance. | ||||
|      * | ||||
|      * @param  int  $count | ||||
|      * @param  int  $code | ||||
|      * @param  \Throwable|null  $previous | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($count, $code = 0, $previous = null) | ||||
|     { | ||||
|         $this->count = $count; | ||||
|  | ||||
|         parent::__construct("$count items were found.", $code, $previous); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the number of items found. | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getCount() | ||||
|     { | ||||
|         return $this->count; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,6 @@ use Illuminate\Support\Arr; | ||||
| use Illuminate\Support\Collection; | ||||
| use Illuminate\Support\Enumerable; | ||||
| use Illuminate\Support\HigherOrderCollectionProxy; | ||||
| use Illuminate\Support\HigherOrderWhenProxy; | ||||
| use JsonSerializable; | ||||
| use Symfony\Component\VarDumper\VarDumper; | ||||
| use Traversable; | ||||
| @@ -19,6 +18,9 @@ use UnexpectedValueException; | ||||
| use UnitEnum; | ||||
|  | ||||
| /** | ||||
|  * @template TKey of array-key | ||||
|  * @template TValue | ||||
|  * | ||||
|  * @property-read HigherOrderCollectionProxy $average | ||||
|  * @property-read HigherOrderCollectionProxy $avg | ||||
|  * @property-read HigherOrderCollectionProxy $contains | ||||
| @@ -35,19 +37,23 @@ use UnitEnum; | ||||
|  * @property-read HigherOrderCollectionProxy $min | ||||
|  * @property-read HigherOrderCollectionProxy $partition | ||||
|  * @property-read HigherOrderCollectionProxy $reject | ||||
|  * @property-read HigherOrderCollectionProxy $skipUntil | ||||
|  * @property-read HigherOrderCollectionProxy $skipWhile | ||||
|  * @property-read HigherOrderCollectionProxy $some | ||||
|  * @property-read HigherOrderCollectionProxy $sortBy | ||||
|  * @property-read HigherOrderCollectionProxy $sortByDesc | ||||
|  * @property-read HigherOrderCollectionProxy $skipUntil | ||||
|  * @property-read HigherOrderCollectionProxy $skipWhile | ||||
|  * @property-read HigherOrderCollectionProxy $sum | ||||
|  * @property-read HigherOrderCollectionProxy $takeUntil | ||||
|  * @property-read HigherOrderCollectionProxy $takeWhile | ||||
|  * @property-read HigherOrderCollectionProxy $unique | ||||
|  * @property-read HigherOrderCollectionProxy $unless | ||||
|  * @property-read HigherOrderCollectionProxy $until | ||||
|  * @property-read HigherOrderCollectionProxy $when | ||||
|  */ | ||||
| trait EnumeratesValues | ||||
| { | ||||
|     use Conditionable; | ||||
|  | ||||
|     /** | ||||
|      * Indicates that the object's string representation should be escaped when __toString is invoked. | ||||
|      * | ||||
| @@ -58,7 +64,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * The methods that can be proxied. | ||||
|      * | ||||
|      * @var string[] | ||||
|      * @var array<int, string> | ||||
|      */ | ||||
|     protected static $proxies = [ | ||||
|         'average', | ||||
| @@ -86,14 +92,19 @@ trait EnumeratesValues | ||||
|         'takeUntil', | ||||
|         'takeWhile', | ||||
|         'unique', | ||||
|         'unless', | ||||
|         'until', | ||||
|         'when', | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * Create a new collection instance if the value isn't one already. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @return static | ||||
|      * @template TMakeKey of array-key | ||||
|      * @template TMakeValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TMakeKey, TMakeValue>|iterable<TMakeKey, TMakeValue>|null  $items | ||||
|      * @return static<TMakeKey, TMakeValue> | ||||
|      */ | ||||
|     public static function make($items = []) | ||||
|     { | ||||
| @@ -103,8 +114,11 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Wrap the given value in a collection if applicable. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @return static | ||||
|      * @template TWrapKey of array-key | ||||
|      * @template TWrapValue | ||||
|      * | ||||
|      * @param  iterable<TWrapKey, TWrapValue>  $value | ||||
|      * @return static<TWrapKey, TWrapValue> | ||||
|      */ | ||||
|     public static function wrap($value) | ||||
|     { | ||||
| @@ -116,8 +130,11 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Get the underlying items from the given collection if applicable. | ||||
|      * | ||||
|      * @param  array|static  $value | ||||
|      * @return array | ||||
|      * @template TUnwrapKey of array-key | ||||
|      * @template TUnwrapValue | ||||
|      * | ||||
|      * @param  array<TUnwrapKey, TUnwrapValue>|static<TUnwrapKey, TUnwrapValue>  $value | ||||
|      * @return array<TUnwrapKey, TUnwrapValue> | ||||
|      */ | ||||
|     public static function unwrap($value) | ||||
|     { | ||||
| @@ -137,9 +154,11 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Create a new collection by invoking the callback a given amount of times. | ||||
|      * | ||||
|      * @template TTimesValue | ||||
|      * | ||||
|      * @param  int  $number | ||||
|      * @param  callable|null  $callback | ||||
|      * @return static | ||||
|      * @param  (callable(int): TTimesValue)|null  $callback | ||||
|      * @return static<int, TTimesValue> | ||||
|      */ | ||||
|     public static function times($number, callable $callback = null) | ||||
|     { | ||||
| @@ -148,15 +167,15 @@ trait EnumeratesValues | ||||
|         } | ||||
|  | ||||
|         return static::range(1, $number) | ||||
|             ->when($callback) | ||||
|             ->unless($callback == null) | ||||
|             ->map($callback); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Alias for the "avg" method. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @return mixed | ||||
|      * @param  (callable(TValue): float|int)|string|null  $callback | ||||
|      * @return float|int|null | ||||
|      */ | ||||
|     public function average($callback = null) | ||||
|     { | ||||
| @@ -166,7 +185,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Alias for the "contains" method. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  (callable(TValue, TKey): bool)|TValue|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return bool | ||||
| @@ -176,39 +195,11 @@ trait EnumeratesValues | ||||
|         return $this->contains(...func_get_args()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if an item exists, using strict comparison. | ||||
|      * | ||||
|      * @param  mixed  $key | ||||
|      * @param  mixed  $value | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function containsStrict($key, $value = null) | ||||
|     { | ||||
|         if (func_num_args() === 2) { | ||||
|             return $this->contains(function ($item) use ($key, $value) { | ||||
|                 return data_get($item, $key) === $value; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         if ($this->useAsCallable($key)) { | ||||
|             return ! is_null($this->first($key)); | ||||
|         } | ||||
|  | ||||
|         foreach ($this as $item) { | ||||
|             if ($item === $key) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Dump the items and end the script. | ||||
|      * | ||||
|      * @param  mixed  ...$args | ||||
|      * @return void | ||||
|      * @return never | ||||
|      */ | ||||
|     public function dd(...$args) | ||||
|     { | ||||
| @@ -236,7 +227,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Execute a callback over each item. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable(TValue, TKey): mixed  $callback | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function each(callable $callback) | ||||
| @@ -253,7 +244,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Execute a callback over each nested chunk of items. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable(...mixed): mixed  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function eachSpread(callable $callback) | ||||
| @@ -268,7 +259,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Determine if all items pass the given truth test. | ||||
|      * | ||||
|      * @param  string|callable  $key | ||||
|      * @param  (callable(TValue, TKey): bool)|TValue|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return bool | ||||
| @@ -293,16 +284,32 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Get the first item by the given key value pair. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  callable|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return mixed | ||||
|      * @return TValue|null | ||||
|      */ | ||||
|     public function firstWhere($key, $operator = null, $value = null) | ||||
|     { | ||||
|         return $this->first($this->operatorForWhere(...func_get_args())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a single key's value from the first matching item in the collection. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function value($key, $default = null) | ||||
|     { | ||||
|         if ($value = $this->firstWhere($key)) { | ||||
|             return data_get($value, $key, $default); | ||||
|         } | ||||
|  | ||||
|         return value($default); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the collection is not empty. | ||||
|      * | ||||
| @@ -316,8 +323,10 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Run a map over each nested chunk of items. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TMapSpreadValue | ||||
|      * | ||||
|      * @param  callable(mixed): TMapSpreadValue  $callback | ||||
|      * @return static<TKey, TMapSpreadValue> | ||||
|      */ | ||||
|     public function mapSpread(callable $callback) | ||||
|     { | ||||
| @@ -333,8 +342,11 @@ trait EnumeratesValues | ||||
|      * | ||||
|      * The callback should return an associative array with a single key/value pair. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TMapToGroupsKey of array-key | ||||
|      * @template TMapToGroupsValue | ||||
|      * | ||||
|      * @param  callable(TValue, TKey): array<TMapToGroupsKey, TMapToGroupsValue>  $callback | ||||
|      * @return static<TMapToGroupsKey, static<int, TMapToGroupsValue>> | ||||
|      */ | ||||
|     public function mapToGroups(callable $callback) | ||||
|     { | ||||
| @@ -346,8 +358,11 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Map a collection and flatten the result by a single level. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return static | ||||
|      * @template TFlatMapKey of array-key | ||||
|      * @template TFlatMapValue | ||||
|      * | ||||
|      * @param  callable(TValue, TKey): (\Illuminate\Support\Collection<TFlatMapKey, TFlatMapValue>|array<TFlatMapKey, TFlatMapValue>)  $callback | ||||
|      * @return static<TFlatMapKey, TFlatMapValue> | ||||
|      */ | ||||
|     public function flatMap(callable $callback) | ||||
|     { | ||||
| @@ -357,48 +372,42 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Map the values into a new class. | ||||
|      * | ||||
|      * @param  string  $class | ||||
|      * @return static | ||||
|      * @template TMapIntoValue | ||||
|      * | ||||
|      * @param  class-string<TMapIntoValue>  $class | ||||
|      * @return static<TKey, TMapIntoValue> | ||||
|      */ | ||||
|     public function mapInto($class) | ||||
|     { | ||||
|         return $this->map(function ($value, $key) use ($class) { | ||||
|             return new $class($value, $key); | ||||
|         }); | ||||
|         return $this->map(fn ($value, $key) => new $class($value, $key)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the min value of a given key. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @param  (callable(TValue):mixed)|string|null  $callback | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function min($callback = null) | ||||
|     { | ||||
|         $callback = $this->valueRetriever($callback); | ||||
|  | ||||
|         return $this->map(function ($value) use ($callback) { | ||||
|             return $callback($value); | ||||
|         })->filter(function ($value) { | ||||
|             return ! is_null($value); | ||||
|         })->reduce(function ($result, $value) { | ||||
|             return is_null($result) || $value < $result ? $value : $result; | ||||
|         }); | ||||
|         return $this->map(fn ($value) => $callback($value)) | ||||
|             ->filter(fn ($value) => ! is_null($value)) | ||||
|             ->reduce(fn ($result, $value) => is_null($result) || $value < $result ? $value : $result); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the max value of a given key. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @param  (callable(TValue):mixed)|string|null  $callback | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function max($callback = null) | ||||
|     { | ||||
|         $callback = $this->valueRetriever($callback); | ||||
|  | ||||
|         return $this->filter(function ($value) { | ||||
|             return ! is_null($value); | ||||
|         })->reduce(function ($result, $item) use ($callback) { | ||||
|         return $this->filter(fn ($value) => ! is_null($value))->reduce(function ($result, $item) use ($callback) { | ||||
|             $value = $callback($item); | ||||
|  | ||||
|             return is_null($result) || $value > $result ? $value : $result; | ||||
| @@ -422,10 +431,10 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Partition the collection into two arrays using the given callback or key. | ||||
|      * | ||||
|      * @param  callable|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return static | ||||
|      * @param  (callable(TValue, TKey): bool)|TValue|string  $key | ||||
|      * @param  TValue|string|null  $operator | ||||
|      * @param  TValue|null  $value | ||||
|      * @return static<int<0, 1>, static<TKey, TValue>> | ||||
|      */ | ||||
|     public function partition($key, $operator = null, $value = null) | ||||
|     { | ||||
| @@ -450,7 +459,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Get the sum of the given values. | ||||
|      * | ||||
|      * @param  callable|string|null  $callback | ||||
|      * @param  (callable(TValue): mixed)|string|null  $callback | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function sum($callback = null) | ||||
| @@ -459,40 +468,17 @@ trait EnumeratesValues | ||||
|             ? $this->identity() | ||||
|             : $this->valueRetriever($callback); | ||||
|  | ||||
|         return $this->reduce(function ($result, $item) use ($callback) { | ||||
|             return $result + $callback($item); | ||||
|         }, 0); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Apply the callback if the value is truthy. | ||||
|      * | ||||
|      * @param  bool|mixed  $value | ||||
|      * @param  callable|null  $callback | ||||
|      * @param  callable|null  $default | ||||
|      * @return static|mixed | ||||
|      */ | ||||
|     public function when($value, callable $callback = null, callable $default = null) | ||||
|     { | ||||
|         if (! $callback) { | ||||
|             return new HigherOrderWhenProxy($this, $value); | ||||
|         } | ||||
|  | ||||
|         if ($value) { | ||||
|             return $callback($this, $value); | ||||
|         } elseif ($default) { | ||||
|             return $default($this, $value); | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|         return $this->reduce(fn ($result, $item) => $result + $callback($item), 0); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Apply the callback if the collection is empty. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable|null  $default | ||||
|      * @return static|mixed | ||||
|      * @template TWhenEmptyReturnType | ||||
|      * | ||||
|      * @param  (callable($this): TWhenEmptyReturnType)  $callback | ||||
|      * @param  (callable($this): TWhenEmptyReturnType)|null  $default | ||||
|      * @return $this|TWhenEmptyReturnType | ||||
|      */ | ||||
|     public function whenEmpty(callable $callback, callable $default = null) | ||||
|     { | ||||
| @@ -502,34 +488,25 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Apply the callback if the collection is not empty. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable|null  $default | ||||
|      * @return static|mixed | ||||
|      * @template TWhenNotEmptyReturnType | ||||
|      * | ||||
|      * @param  callable($this): TWhenNotEmptyReturnType  $callback | ||||
|      * @param  (callable($this): TWhenNotEmptyReturnType)|null  $default | ||||
|      * @return $this|TWhenNotEmptyReturnType | ||||
|      */ | ||||
|     public function whenNotEmpty(callable $callback, callable $default = null) | ||||
|     { | ||||
|         return $this->when($this->isNotEmpty(), $callback, $default); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Apply the callback 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) | ||||
|     { | ||||
|         return $this->when(! $value, $callback, $default); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Apply the callback unless the collection is empty. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable|null  $default | ||||
|      * @return static|mixed | ||||
|      * @template TUnlessEmptyReturnType | ||||
|      * | ||||
|      * @param  callable($this): TUnlessEmptyReturnType  $callback | ||||
|      * @param  (callable($this): TUnlessEmptyReturnType)|null  $default | ||||
|      * @return $this|TUnlessEmptyReturnType | ||||
|      */ | ||||
|     public function unlessEmpty(callable $callback, callable $default = null) | ||||
|     { | ||||
| @@ -539,9 +516,11 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Apply the callback unless the collection is not empty. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  callable|null  $default | ||||
|      * @return static|mixed | ||||
|      * @template TUnlessNotEmptyReturnType | ||||
|      * | ||||
|      * @param  callable($this): TUnlessNotEmptyReturnType  $callback | ||||
|      * @param  (callable($this): TUnlessNotEmptyReturnType)|null  $default | ||||
|      * @return $this|TUnlessNotEmptyReturnType | ||||
|      */ | ||||
|     public function unlessNotEmpty(callable $callback, callable $default = null) | ||||
|     { | ||||
| @@ -551,7 +530,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Filter items by the given key value pair. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  callable|string  $key | ||||
|      * @param  mixed  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return static | ||||
| @@ -599,7 +578,7 @@ trait EnumeratesValues | ||||
|      * Filter items by the given key value pair. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  mixed  $values | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values | ||||
|      * @param  bool  $strict | ||||
|      * @return static | ||||
|      */ | ||||
| @@ -607,16 +586,14 @@ trait EnumeratesValues | ||||
|     { | ||||
|         $values = $this->getArrayableItems($values); | ||||
|  | ||||
|         return $this->filter(function ($item) use ($key, $values, $strict) { | ||||
|             return in_array(data_get($item, $key), $values, $strict); | ||||
|         }); | ||||
|         return $this->filter(fn ($item) => in_array(data_get($item, $key), $values, $strict)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Filter items by the given key value pair using strict comparison. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  mixed  $values | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values | ||||
|      * @return static | ||||
|      */ | ||||
|     public function whereInStrict($key, $values) | ||||
| @@ -628,7 +605,7 @@ trait EnumeratesValues | ||||
|      * Filter items such that the value of the given key is between the given values. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  array  $values | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values | ||||
|      * @return static | ||||
|      */ | ||||
|     public function whereBetween($key, $values) | ||||
| @@ -640,21 +617,21 @@ trait EnumeratesValues | ||||
|      * Filter items such that the value of the given key is not between the given values. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  array  $values | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values | ||||
|      * @return static | ||||
|      */ | ||||
|     public function whereNotBetween($key, $values) | ||||
|     { | ||||
|         return $this->filter(function ($item) use ($key, $values) { | ||||
|             return data_get($item, $key) < reset($values) || data_get($item, $key) > end($values); | ||||
|         }); | ||||
|         return $this->filter( | ||||
|             fn ($item) => data_get($item, $key) < reset($values) || data_get($item, $key) > end($values) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Filter items by the given key value pair. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  mixed  $values | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values | ||||
|      * @param  bool  $strict | ||||
|      * @return static | ||||
|      */ | ||||
| @@ -662,16 +639,14 @@ trait EnumeratesValues | ||||
|     { | ||||
|         $values = $this->getArrayableItems($values); | ||||
|  | ||||
|         return $this->reject(function ($item) use ($key, $values, $strict) { | ||||
|             return in_array(data_get($item, $key), $values, $strict); | ||||
|         }); | ||||
|         return $this->reject(fn ($item) => in_array(data_get($item, $key), $values, $strict)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Filter items by the given key value pair using strict comparison. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  mixed  $values | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values | ||||
|      * @return static | ||||
|      */ | ||||
|     public function whereNotInStrict($key, $values) | ||||
| @@ -682,8 +657,10 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Filter the items, removing any items that don't match the given type(s). | ||||
|      * | ||||
|      * @param  string|string[]  $type | ||||
|      * @return static | ||||
|      * @template TWhereInstanceOf | ||||
|      * | ||||
|      * @param  class-string<TWhereInstanceOf>|array<array-key, class-string<TWhereInstanceOf>>  $type | ||||
|      * @return static<TKey, TWhereInstanceOf> | ||||
|      */ | ||||
|     public function whereInstanceOf($type) | ||||
|     { | ||||
| @@ -705,8 +682,10 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Pass the collection to the given callback and return the result. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return mixed | ||||
|      * @template TPipeReturnType | ||||
|      * | ||||
|      * @param  callable($this): TPipeReturnType  $callback | ||||
|      * @return TPipeReturnType | ||||
|      */ | ||||
|     public function pipe(callable $callback) | ||||
|     { | ||||
| @@ -716,7 +695,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Pass the collection into a new class. | ||||
|      * | ||||
|      * @param  string  $class | ||||
|      * @param  class-string  $class | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function pipeInto($class) | ||||
| @@ -727,38 +706,26 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Pass the collection through a series of callable pipes and return the result. | ||||
|      * | ||||
|      * @param  array<callable>  $pipes | ||||
|      * @param  array<callable>  $callbacks | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function pipeThrough($pipes) | ||||
|     public function pipeThrough($callbacks) | ||||
|     { | ||||
|         return static::make($pipes)->reduce( | ||||
|             function ($carry, $pipe) { | ||||
|                 return $pipe($carry); | ||||
|             }, | ||||
|         return Collection::make($callbacks)->reduce( | ||||
|             fn ($carry, $callback) => $callback($carry), | ||||
|             $this, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Pass the collection to the given callback and then return it. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function tap(callable $callback) | ||||
|     { | ||||
|         $callback(clone $this); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reduce the collection to a single value. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  mixed  $initial | ||||
|      * @return mixed | ||||
|      * @template TReduceInitial | ||||
|      * @template TReduceReturnType | ||||
|      * | ||||
|      * @param  callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType  $callback | ||||
|      * @param  TReduceInitial  $initial | ||||
|      * @return TReduceReturnType | ||||
|      */ | ||||
|     public function reduce(callable $callback, $initial = null) | ||||
|     { | ||||
| @@ -771,22 +738,6 @@ trait EnumeratesValues | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reduce the collection to multiple aggregate values. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  mixed  ...$initial | ||||
|      * @return array | ||||
|      * | ||||
|      * @deprecated Use "reduceSpread" instead | ||||
|      * | ||||
|      * @throws \UnexpectedValueException | ||||
|      */ | ||||
|     public function reduceMany(callable $callback, ...$initial) | ||||
|     { | ||||
|         return $this->reduceSpread($callback, ...$initial); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reduce the collection to multiple aggregate values. | ||||
|      * | ||||
| @@ -805,7 +756,7 @@ trait EnumeratesValues | ||||
|  | ||||
|             if (! is_array($result)) { | ||||
|                 throw new UnexpectedValueException(sprintf( | ||||
|                     "%s::reduceMany expects reducer to return an array, but got a '%s' instead.", | ||||
|                     "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.", | ||||
|                     class_basename(static::class), gettype($result) | ||||
|                 )); | ||||
|             } | ||||
| @@ -814,22 +765,10 @@ trait EnumeratesValues | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reduce an associative collection to a single value. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @param  mixed  $initial | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function reduceWithKeys(callable $callback, $initial = null) | ||||
|     { | ||||
|         return $this->reduce($callback, $initial); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a collection of all elements that do not pass a given truth test. | ||||
|      * | ||||
|      * @param  callable|mixed  $callback | ||||
|      * @param  (callable(TValue, TKey): bool)|bool|TValue  $callback | ||||
|      * @return static | ||||
|      */ | ||||
|     public function reject($callback = true) | ||||
| @@ -843,10 +782,45 @@ trait EnumeratesValues | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Pass the collection to the given callback and then return it. | ||||
|      * | ||||
|      * @param  callable($this): mixed  $callback | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function tap(callable $callback) | ||||
|     { | ||||
|         $callback($this); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return only unique items from the collection array. | ||||
|      * | ||||
|      * @param  (callable(TValue, TKey): mixed)|string|null  $key | ||||
|      * @param  bool  $strict | ||||
|      * @return static | ||||
|      */ | ||||
|     public function unique($key = null, $strict = false) | ||||
|     { | ||||
|         $callback = $this->valueRetriever($key); | ||||
|  | ||||
|         $exists = []; | ||||
|  | ||||
|         return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { | ||||
|             if (in_array($id = $callback($item, $key), $exists, $strict)) { | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             $exists[] = $id; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return only unique items from the collection array using strict comparison. | ||||
|      * | ||||
|      * @param  string|callable|null  $key | ||||
|      * @param  (callable(TValue, TKey): mixed)|string|null  $key | ||||
|      * @return static | ||||
|      */ | ||||
|     public function uniqueStrict($key = null) | ||||
| @@ -857,7 +831,7 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Collect the values into a collection. | ||||
|      * | ||||
|      * @return \Illuminate\Support\Collection | ||||
|      * @return \Illuminate\Support\Collection<TKey, TValue> | ||||
|      */ | ||||
|     public function collect() | ||||
|     { | ||||
| @@ -867,22 +841,19 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Get the collection of items as a plain array. | ||||
|      * | ||||
|      * @return array | ||||
|      * @return array<TKey, mixed> | ||||
|      */ | ||||
|     public function toArray() | ||||
|     { | ||||
|         return $this->map(function ($value) { | ||||
|             return $value instanceof Arrayable ? $value->toArray() : $value; | ||||
|         })->all(); | ||||
|         return $this->map(fn ($value) => $value instanceof Arrayable ? $value->toArray() : $value)->all(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert the object into something JSON serializable. | ||||
|      * | ||||
|      * @return array | ||||
|      * @return array<TKey, mixed> | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function jsonSerialize() | ||||
|     public function jsonSerialize(): array | ||||
|     { | ||||
|         return array_map(function ($value) { | ||||
|             if ($value instanceof JsonSerializable) { | ||||
| @@ -976,7 +947,7 @@ trait EnumeratesValues | ||||
|      * Results array of items from Collection or Arrayable. | ||||
|      * | ||||
|      * @param  mixed  $items | ||||
|      * @return array | ||||
|      * @return array<TKey, TValue> | ||||
|      */ | ||||
|     protected function getArrayableItems($items) | ||||
|     { | ||||
| @@ -986,12 +957,12 @@ trait EnumeratesValues | ||||
|             return $items->all(); | ||||
|         } elseif ($items instanceof Arrayable) { | ||||
|             return $items->toArray(); | ||||
|         } elseif ($items instanceof Traversable) { | ||||
|             return iterator_to_array($items); | ||||
|         } elseif ($items instanceof Jsonable) { | ||||
|             return json_decode($items->toJson(), true); | ||||
|         } elseif ($items instanceof JsonSerializable) { | ||||
|             return (array) $items->jsonSerialize(); | ||||
|         } elseif ($items instanceof Traversable) { | ||||
|             return iterator_to_array($items); | ||||
|         } elseif ($items instanceof UnitEnum) { | ||||
|             return [$items]; | ||||
|         } | ||||
| @@ -1002,13 +973,17 @@ trait EnumeratesValues | ||||
|     /** | ||||
|      * Get an operator checker callback. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @param  callable|string  $key | ||||
|      * @param  string|null  $operator | ||||
|      * @param  mixed  $value | ||||
|      * @return \Closure | ||||
|      */ | ||||
|     protected function operatorForWhere($key, $operator = null, $value = null) | ||||
|     { | ||||
|         if ($this->useAsCallable($key)) { | ||||
|             return $key; | ||||
|         } | ||||
|  | ||||
|         if (func_num_args() === 1) { | ||||
|             $value = true; | ||||
|  | ||||
| @@ -1044,6 +1019,7 @@ trait EnumeratesValues | ||||
|                 case '>=':  return $retrieved >= $value; | ||||
|                 case '===': return $retrieved === $value; | ||||
|                 case '!==': return $retrieved !== $value; | ||||
|                 case '<=>': return $retrieved <=> $value; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| @@ -1071,22 +1047,18 @@ trait EnumeratesValues | ||||
|             return $value; | ||||
|         } | ||||
|  | ||||
|         return function ($item) use ($value) { | ||||
|             return data_get($item, $value); | ||||
|         }; | ||||
|         return fn ($item) => data_get($item, $value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Make a function to check an item's equality. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @return \Closure | ||||
|      * @return \Closure(mixed): bool | ||||
|      */ | ||||
|     protected function equality($value) | ||||
|     { | ||||
|         return function ($item) use ($value) { | ||||
|             return $item === $value; | ||||
|         }; | ||||
|         return fn ($item) => $item === $value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -1097,20 +1069,16 @@ trait EnumeratesValues | ||||
|      */ | ||||
|     protected function negate(Closure $callback) | ||||
|     { | ||||
|         return function (...$params) use ($callback) { | ||||
|             return ! $callback(...$params); | ||||
|         }; | ||||
|         return fn (...$params) => ! $callback(...$params); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Make a function that returns what's passed to it. | ||||
|      * | ||||
|      * @return \Closure | ||||
|      * @return \Closure(TValue): TValue | ||||
|      */ | ||||
|     protected function identity() | ||||
|     { | ||||
|         return function ($value) { | ||||
|             return $value; | ||||
|         }; | ||||
|         return fn ($value) => $value; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,9 +14,10 @@ | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": "^7.3|^8.0", | ||||
|         "illuminate/contracts": "^8.0", | ||||
|         "illuminate/macroable": "^8.0" | ||||
|         "php": "^8.0.2", | ||||
|         "illuminate/conditionable": "^9.0", | ||||
|         "illuminate/contracts": "^9.0", | ||||
|         "illuminate/macroable": "^9.0" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
| @@ -28,11 +29,11 @@ | ||||
|     }, | ||||
|     "extra": { | ||||
|         "branch-alias": { | ||||
|             "dev-master": "8.x-dev" | ||||
|             "dev-master": "9.x-dev" | ||||
|         } | ||||
|     }, | ||||
|     "suggest": { | ||||
|         "symfony/var-dumper": "Required to use the dump method (^5.4)." | ||||
|         "symfony/var-dumper": "Required to use the dump method (^6.0)." | ||||
|     }, | ||||
|     "config": { | ||||
|         "sort-packages": true | ||||
|   | ||||
| @@ -7,10 +7,13 @@ if (! function_exists('collect')) { | ||||
|     /** | ||||
|      * Create a collection from the given value. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @return \Illuminate\Support\Collection | ||||
|      * @template TKey of array-key | ||||
|      * @template TValue | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>|null  $value | ||||
|      * @return \Illuminate\Support\Collection<TKey, TValue> | ||||
|      */ | ||||
|     function collect($value = null) | ||||
|     function collect($value = []) | ||||
|     { | ||||
|         return new Collection($value); | ||||
|     } | ||||
| @@ -58,7 +61,7 @@ if (! function_exists('data_get')) { | ||||
|             if ($segment === '*') { | ||||
|                 if ($target instanceof Collection) { | ||||
|                     $target = $target->all(); | ||||
|                 } elseif (! is_array($target)) { | ||||
|                 } elseif (! is_iterable($target)) { | ||||
|                     return value($default); | ||||
|                 } | ||||
|  | ||||
| @@ -177,6 +180,7 @@ if (! function_exists('value')) { | ||||
|      * Return the default value of the given value. | ||||
|      * | ||||
|      * @param  mixed  $value | ||||
|      * @param  mixed  $args | ||||
|      * @return mixed | ||||
|      */ | ||||
|     function value($value, ...$args) | ||||
|   | ||||
							
								
								
									
										109
									
								
								vendor/laravel/framework/src/Illuminate/Conditionable/HigherOrderWhenProxy.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								vendor/laravel/framework/src/Illuminate/Conditionable/HigherOrderWhenProxy.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Support; | ||||
|  | ||||
| class HigherOrderWhenProxy | ||||
| { | ||||
|     /** | ||||
|      * The target being conditionally operated on. | ||||
|      * | ||||
|      * @var mixed | ||||
|      */ | ||||
|     protected $target; | ||||
|  | ||||
|     /** | ||||
|      * The condition for proxying. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $condition; | ||||
|  | ||||
|     /** | ||||
|      * Indicates whether the proxy has a condition. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $hasCondition = false; | ||||
|  | ||||
|     /** | ||||
|      * Determine whether the condition should be negated. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $negateConditionOnCapture; | ||||
|  | ||||
|     /** | ||||
|      * Create a new proxy instance. | ||||
|      * | ||||
|      * @param  mixed  $target | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($target) | ||||
|     { | ||||
|         $this->target = $target; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the condition on the proxy. | ||||
|      * | ||||
|      * @param  bool  $condition | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function condition($condition) | ||||
|     { | ||||
|         [$this->condition, $this->hasCondition] = [$condition, true]; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Indicate that the condition should be negated. | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function negateConditionOnCapture() | ||||
|     { | ||||
|         $this->negateConditionOnCapture = true; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Proxy accessing an attribute onto the target. | ||||
|      * | ||||
|      * @param  string  $key | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function __get($key) | ||||
|     { | ||||
|         if (! $this->hasCondition) { | ||||
|             $condition = $this->target->{$key}; | ||||
|  | ||||
|             return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition); | ||||
|         } | ||||
|  | ||||
|         return $this->condition | ||||
|             ? $this->target->{$key} | ||||
|             : $this->target; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Proxy a method call on the target. | ||||
|      * | ||||
|      * @param  string  $method | ||||
|      * @param  array  $parameters | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function __call($method, $parameters) | ||||
|     { | ||||
|         if (! $this->hasCondition) { | ||||
|             $condition = $this->target->{$method}(...$parameters); | ||||
|  | ||||
|             return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition); | ||||
|         } | ||||
|  | ||||
|         return $this->condition | ||||
|             ? $this->target->{$method}(...$parameters) | ||||
|             : $this->target; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/laravel/framework/src/Illuminate/Conditionable/LICENSE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/laravel/framework/src/Illuminate/Conditionable/LICENSE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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. | ||||
							
								
								
									
										73
									
								
								vendor/laravel/framework/src/Illuminate/Conditionable/Traits/Conditionable.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								vendor/laravel/framework/src/Illuminate/Conditionable/Traits/Conditionable.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Support\Traits; | ||||
|  | ||||
| use Closure; | ||||
| use Illuminate\Support\HigherOrderWhenProxy; | ||||
|  | ||||
| trait Conditionable | ||||
| { | ||||
|     /** | ||||
|      * Apply the callback if the given "value" is (or resolves to) truthy. | ||||
|      * | ||||
|      * @template TWhenParameter | ||||
|      * @template TWhenReturnType | ||||
|      * | ||||
|      * @param  (\Closure($this): TWhenParameter)|TWhenParameter|null $value | ||||
|      * @param  (callable($this, TWhenParameter): TWhenReturnType)|null  $callback | ||||
|      * @param  (callable($this, TWhenParameter): TWhenReturnType)|null  $default | ||||
|      * @return $this|TWhenReturnType | ||||
|      */ | ||||
|     public function when($value = null, callable $callback = null, callable $default = null) | ||||
|     { | ||||
|         $value = $value instanceof Closure ? $value($this) : $value; | ||||
|  | ||||
|         if (func_num_args() === 0) { | ||||
|             return new HigherOrderWhenProxy($this); | ||||
|         } | ||||
|  | ||||
|         if (func_num_args() === 1) { | ||||
|             return (new HigherOrderWhenProxy($this))->condition($value); | ||||
|         } | ||||
|  | ||||
|         if ($value) { | ||||
|             return $callback($this, $value) ?? $this; | ||||
|         } elseif ($default) { | ||||
|             return $default($this, $value) ?? $this; | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Apply the callback if the given "value" is (or resolves to) falsy. | ||||
|      * | ||||
|      * @template TUnlessParameter | ||||
|      * @template TUnlessReturnType | ||||
|      * | ||||
|      * @param  (\Closure($this): TUnlessParameter)|TUnlessParameter|null  $value | ||||
|      * @param  (callable($this, TUnlessParameter): TUnlessReturnType)|null  $callback | ||||
|      * @param  (callable($this, TUnlessParameter): TUnlessReturnType)|null  $default | ||||
|      * @return $this|TUnlessReturnType | ||||
|      */ | ||||
|     public function unless($value = null, callable $callback = null, callable $default = null) | ||||
|     { | ||||
|         $value = $value instanceof Closure ? $value($this) : $value; | ||||
|  | ||||
|         if (func_num_args() === 0) { | ||||
|             return (new HigherOrderWhenProxy($this))->negateConditionOnCapture(); | ||||
|         } | ||||
|  | ||||
|         if (func_num_args() === 1) { | ||||
|             return (new HigherOrderWhenProxy($this))->condition(! $value); | ||||
|         } | ||||
|  | ||||
|         if (! $value) { | ||||
|             return $callback($this, $value) ?? $this; | ||||
|         } elseif ($default) { | ||||
|             return $default($this, $value) ?? $this; | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										33
									
								
								vendor/laravel/framework/src/Illuminate/Conditionable/composer.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/laravel/framework/src/Illuminate/Conditionable/composer.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| { | ||||
|     "name": "illuminate/conditionable", | ||||
|     "description": "The Illuminate Conditionable 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": "^8.0.2" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
|             "Illuminate\\Support\\": "" | ||||
|         } | ||||
|     }, | ||||
|     "extra": { | ||||
|         "branch-alias": { | ||||
|             "dev-master": "9.x-dev" | ||||
|         } | ||||
|     }, | ||||
|     "config": { | ||||
|         "sort-packages": true | ||||
|     }, | ||||
|     "minimum-stability": "dev" | ||||
| } | ||||
| @@ -5,9 +5,12 @@ namespace Illuminate\Config; | ||||
| use ArrayAccess; | ||||
| use Illuminate\Contracts\Config\Repository as ConfigContract; | ||||
| use Illuminate\Support\Arr; | ||||
| use Illuminate\Support\Traits\Macroable; | ||||
|  | ||||
| class Repository implements ArrayAccess, ConfigContract | ||||
| { | ||||
|     use Macroable; | ||||
|  | ||||
|     /** | ||||
|      * All of the configuration items. | ||||
|      * | ||||
| @@ -138,8 +141,7 @@ class Repository implements ArrayAccess, ConfigContract | ||||
|      * @param  string  $key | ||||
|      * @return bool | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetExists($key) | ||||
|     public function offsetExists($key): bool | ||||
|     { | ||||
|         return $this->has($key); | ||||
|     } | ||||
| @@ -150,8 +152,7 @@ class Repository implements ArrayAccess, ConfigContract | ||||
|      * @param  string  $key | ||||
|      * @return mixed | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetGet($key) | ||||
|     public function offsetGet($key): mixed | ||||
|     { | ||||
|         return $this->get($key); | ||||
|     } | ||||
| @@ -163,8 +164,7 @@ class Repository implements ArrayAccess, ConfigContract | ||||
|      * @param  mixed  $value | ||||
|      * @return void | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetSet($key, $value) | ||||
|     public function offsetSet($key, $value): void | ||||
|     { | ||||
|         $this->set($key, $value); | ||||
|     } | ||||
| @@ -175,8 +175,7 @@ class Repository implements ArrayAccess, ConfigContract | ||||
|      * @param  string  $key | ||||
|      * @return void | ||||
|      */ | ||||
|     #[\ReturnTypeWillChange] | ||||
|     public function offsetUnset($key) | ||||
|     public function offsetUnset($key): void | ||||
|     { | ||||
|         $this->set($key, null); | ||||
|     } | ||||
|   | ||||
| @@ -14,9 +14,9 @@ | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": "^7.3|^8.0", | ||||
|         "illuminate/collections": "^8.0", | ||||
|         "illuminate/contracts": "^8.0" | ||||
|         "php": "^8.0.2", | ||||
|         "illuminate/collections": "^9.0", | ||||
|         "illuminate/contracts": "^9.0" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
| @@ -25,7 +25,7 @@ | ||||
|     }, | ||||
|     "extra": { | ||||
|         "branch-alias": { | ||||
|             "dev-master": "8.x-dev" | ||||
|             "dev-master": "9.x-dev" | ||||
|         } | ||||
|     }, | ||||
|     "config": { | ||||
|   | ||||
| @@ -15,6 +15,7 @@ use Symfony\Component\Console\Command\Command as SymfonyCommand; | ||||
| use Symfony\Component\Console\Exception\CommandNotFoundException; | ||||
| use Symfony\Component\Console\Input\ArgvInput; | ||||
| use Symfony\Component\Console\Input\ArrayInput; | ||||
| use Symfony\Component\Console\Input\InputDefinition; | ||||
| use Symfony\Component\Console\Input\InputInterface; | ||||
| use Symfony\Component\Console\Input\InputOption; | ||||
| use Symfony\Component\Console\Input\StringInput; | ||||
| @@ -31,6 +32,13 @@ class Application extends SymfonyApplication implements ApplicationContract | ||||
|      */ | ||||
|     protected $laravel; | ||||
|  | ||||
|     /** | ||||
|      * The event dispatcher instance. | ||||
|      * | ||||
|      * @var \Illuminate\Contracts\Events\Dispatcher | ||||
|      */ | ||||
|     protected $events; | ||||
|  | ||||
|     /** | ||||
|      * The output from the previous command. | ||||
|      * | ||||
| @@ -46,11 +54,11 @@ class Application extends SymfonyApplication implements ApplicationContract | ||||
|     protected static $bootstrappers = []; | ||||
|  | ||||
|     /** | ||||
|      * The Event Dispatcher. | ||||
|      * A map of command names to classes. | ||||
|      * | ||||
|      * @var \Illuminate\Contracts\Events\Dispatcher | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $events; | ||||
|     protected $commandMap = []; | ||||
|  | ||||
|     /** | ||||
|      * Create a new Artisan console application. | ||||
| @@ -79,7 +87,7 @@ class Application extends SymfonyApplication implements ApplicationContract | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function run(InputInterface $input = null, OutputInterface $output = null) | ||||
|     public function run(InputInterface $input = null, OutputInterface $output = null): int | ||||
|     { | ||||
|         $commandName = $this->getCommandName( | ||||
|             $input = $input ?: new ArgvInput | ||||
| @@ -254,11 +262,21 @@ class Application extends SymfonyApplication implements ApplicationContract | ||||
|     /** | ||||
|      * Add a command, resolving through the application. | ||||
|      * | ||||
|      * @param  string  $command | ||||
|      * @return \Symfony\Component\Console\Command\Command | ||||
|      * @param  \Illuminate\Console\Command|string  $command | ||||
|      * @return \Symfony\Component\Console\Command\Command|null | ||||
|      */ | ||||
|     public function resolve($command) | ||||
|     { | ||||
|         if (is_subclass_of($command, SymfonyCommand::class) && ($commandName = $command::getDefaultName())) { | ||||
|             $this->commandMap[$commandName] = $command; | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         if ($command instanceof Command) { | ||||
|             return $this->add($command); | ||||
|         } | ||||
|  | ||||
|         return $this->add($this->laravel->make($command)); | ||||
|     } | ||||
|  | ||||
| @@ -279,6 +297,18 @@ class Application extends SymfonyApplication implements ApplicationContract | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the container command loader for lazy resolution. | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function setContainerCommandLoader() | ||||
|     { | ||||
|         $this->setCommandLoader(new ContainerCommandLoader($this->laravel, $this->commandMap)); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the default input definition for the application. | ||||
|      * | ||||
| @@ -286,7 +316,7 @@ class Application extends SymfonyApplication implements ApplicationContract | ||||
|      * | ||||
|      * @return \Symfony\Component\Console\Input\InputDefinition | ||||
|      */ | ||||
|     protected function getDefaultInputDefinition() | ||||
|     protected function getDefaultInputDefinition(): InputDefinition | ||||
|     { | ||||
|         return tap(parent::getDefaultInputDefinition(), function ($definition) { | ||||
|             $definition->addOption($this->getEnvironmentOption()); | ||||
|   | ||||
							
								
								
									
										98
									
								
								vendor/laravel/framework/src/Illuminate/Console/CacheCommandMutex.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								vendor/laravel/framework/src/Illuminate/Console/CacheCommandMutex.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console; | ||||
|  | ||||
| use Carbon\CarbonInterval; | ||||
| use Illuminate\Contracts\Cache\Factory as Cache; | ||||
|  | ||||
| class CacheCommandMutex implements CommandMutex | ||||
| { | ||||
|     /** | ||||
|      * The cache factory implementation. | ||||
|      * | ||||
|      * @var \Illuminate\Contracts\Cache\Factory | ||||
|      */ | ||||
|     public $cache; | ||||
|  | ||||
|     /** | ||||
|      * The cache store that should be used. | ||||
|      * | ||||
|      * @var string|null | ||||
|      */ | ||||
|     public $store = null; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command mutex. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Cache\Factory  $cache | ||||
|      */ | ||||
|     public function __construct(Cache $cache) | ||||
|     { | ||||
|         $this->cache = $cache; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Attempt to obtain a command mutex for the given command. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Command  $command | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function create($command) | ||||
|     { | ||||
|         return $this->cache->store($this->store)->add( | ||||
|             $this->commandMutexName($command), | ||||
|             true, | ||||
|             method_exists($command, 'isolationLockExpiresAt') | ||||
|                     ? $command->isolationLockExpiresAt() | ||||
|                     : CarbonInterval::hour(), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if a command mutex exists for the given command. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Command  $command | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function exists($command) | ||||
|     { | ||||
|         return $this->cache->store($this->store)->has( | ||||
|             $this->commandMutexName($command) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Release the mutex for the given command. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Command  $command | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function forget($command) | ||||
|     { | ||||
|         return $this->cache->store($this->store)->forget( | ||||
|             $this->commandMutexName($command) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param  \Illuminate\Console\Command  $command | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function commandMutexName($command) | ||||
|     { | ||||
|         return 'framework'.DIRECTORY_SEPARATOR.'command-'.$command->getName(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Specify the cache store that should be used. | ||||
|      * | ||||
|      * @param  string|null  $store | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function useStore($store) | ||||
|     { | ||||
|         $this->store = $store; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| @@ -2,9 +2,12 @@ | ||||
|  | ||||
| namespace Illuminate\Console; | ||||
|  | ||||
| use Illuminate\Console\View\Components\Factory; | ||||
| use Illuminate\Contracts\Console\Isolatable; | ||||
| use Illuminate\Support\Traits\Macroable; | ||||
| use Symfony\Component\Console\Command\Command as SymfonyCommand; | ||||
| use Symfony\Component\Console\Input\InputInterface; | ||||
| use Symfony\Component\Console\Input\InputOption; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class Command extends SymfonyCommand | ||||
| @@ -12,6 +15,7 @@ class Command extends SymfonyCommand | ||||
|     use Concerns\CallsCommands, | ||||
|         Concerns\HasParameters, | ||||
|         Concerns\InteractsWithIO, | ||||
|         Concerns\InteractsWithSignals, | ||||
|         Macroable; | ||||
|  | ||||
|     /** | ||||
| @@ -38,7 +42,7 @@ class Command extends SymfonyCommand | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      * @var string|null | ||||
|      */ | ||||
|     protected $description; | ||||
|  | ||||
| @@ -75,7 +79,11 @@ class Command extends SymfonyCommand | ||||
|         // Once we have constructed the command, we'll set the description and other | ||||
|         // related properties of the command. If a signature wasn't used to build | ||||
|         // the command we'll set the arguments and the options on this command. | ||||
|         $this->setDescription((string) $this->description); | ||||
|         if (! isset($this->description)) { | ||||
|             $this->setDescription((string) static::getDefaultDescription()); | ||||
|         } else { | ||||
|             $this->setDescription((string) $this->description); | ||||
|         } | ||||
|  | ||||
|         $this->setHelp((string) $this->help); | ||||
|  | ||||
| @@ -84,6 +92,10 @@ class Command extends SymfonyCommand | ||||
|         if (! isset($this->signature)) { | ||||
|             $this->specifyParameters(); | ||||
|         } | ||||
|  | ||||
|         if ($this instanceof Isolatable) { | ||||
|             $this->configureIsolation(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -104,6 +116,22 @@ class Command extends SymfonyCommand | ||||
|         $this->getDefinition()->addOptions($options); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Configure the console command for isolation. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function configureIsolation() | ||||
|     { | ||||
|         $this->getDefinition()->addOption(new InputOption( | ||||
|             'isolated', | ||||
|             null, | ||||
|             InputOption::VALUE_OPTIONAL, | ||||
|             'Do not run the command if another instance of the command is already running', | ||||
|             false | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the console command. | ||||
|      * | ||||
| @@ -111,15 +139,21 @@ class Command extends SymfonyCommand | ||||
|      * @param  \Symfony\Component\Console\Output\OutputInterface  $output | ||||
|      * @return int | ||||
|      */ | ||||
|     public function run(InputInterface $input, OutputInterface $output) | ||||
|     public function run(InputInterface $input, OutputInterface $output): int | ||||
|     { | ||||
|         $this->output = $this->laravel->make( | ||||
|             OutputStyle::class, ['input' => $input, 'output' => $output] | ||||
|         ); | ||||
|  | ||||
|         return parent::run( | ||||
|             $this->input = $input, $this->output | ||||
|         ); | ||||
|         $this->components = $this->laravel->make(Factory::class, ['output' => $this->output]); | ||||
|  | ||||
|         try { | ||||
|             return parent::run( | ||||
|                 $this->input = $input, $this->output | ||||
|             ); | ||||
|         } finally { | ||||
|             $this->untrap(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -131,9 +165,38 @@ class Command extends SymfonyCommand | ||||
|      */ | ||||
|     protected function execute(InputInterface $input, OutputInterface $output) | ||||
|     { | ||||
|         if ($this instanceof Isolatable && $this->option('isolated') !== false && | ||||
|             ! $this->commandIsolationMutex()->create($this)) { | ||||
|             $this->comment(sprintf( | ||||
|                 'The [%s] command is already running.', $this->getName() | ||||
|             )); | ||||
|  | ||||
|             return (int) (is_numeric($this->option('isolated')) | ||||
|                         ? $this->option('isolated') | ||||
|                         : self::SUCCESS); | ||||
|         } | ||||
|  | ||||
|         $method = method_exists($this, 'handle') ? 'handle' : '__invoke'; | ||||
|  | ||||
|         return (int) $this->laravel->call([$this, $method]); | ||||
|         try { | ||||
|             return (int) $this->laravel->call([$this, $method]); | ||||
|         } finally { | ||||
|             if ($this instanceof Isolatable && $this->option('isolated') !== false) { | ||||
|                 $this->commandIsolationMutex()->forget($this); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a command isolation mutex instance for the command. | ||||
|      * | ||||
|      * @return \Illuminate\Console\CommandMutex | ||||
|      */ | ||||
|     protected function commandIsolationMutex() | ||||
|     { | ||||
|         return $this->laravel->bound(CommandMutex::class) | ||||
|             ? $this->laravel->make(CommandMutex::class) | ||||
|             : $this->laravel->make(CacheCommandMutex::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -166,17 +229,15 @@ class Command extends SymfonyCommand | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isHidden() | ||||
|     public function isHidden(): bool | ||||
|     { | ||||
|         return $this->hidden; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|      * | ||||
|      * @return static | ||||
|      */ | ||||
|     public function setHidden(bool $hidden) | ||||
|     public function setHidden(bool $hidden = true): static | ||||
|     { | ||||
|         parent::setHidden($this->hidden = $hidden); | ||||
|  | ||||
|   | ||||
							
								
								
									
										30
									
								
								vendor/laravel/framework/src/Illuminate/Console/CommandMutex.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/laravel/framework/src/Illuminate/Console/CommandMutex.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console; | ||||
|  | ||||
| interface CommandMutex | ||||
| { | ||||
|     /** | ||||
|      * Attempt to obtain a command mutex for the given command. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Command  $command | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function create($command); | ||||
|  | ||||
|     /** | ||||
|      * Determine if a command mutex exists for the given command. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Command  $command | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function exists($command); | ||||
|  | ||||
|     /** | ||||
|      * Release the mutex for the given command. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Command  $command | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function forget($command); | ||||
| } | ||||
| @@ -28,17 +28,17 @@ trait CreatesMatchingTest | ||||
|      * Create the matching test case if requested. | ||||
|      * | ||||
|      * @param  string  $path | ||||
|      * @return void | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function handleTestCreation($path) | ||||
|     { | ||||
|         if (! $this->option('test') && ! $this->option('pest')) { | ||||
|             return; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $this->call('make:test', [ | ||||
|         return $this->callSilent('make:test', [ | ||||
|             'name' => Str::of($path)->after($this->laravel['path'])->beforeLast('.php')->append('Test')->replace('\\', '/'), | ||||
|             '--pest' => $this->option('pest'), | ||||
|         ]); | ||||
|         ]) == 0; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ trait HasParameters | ||||
|             if ($arguments instanceof InputArgument) { | ||||
|                 $this->getDefinition()->addArgument($arguments); | ||||
|             } else { | ||||
|                 $this->addArgument(...array_values($arguments)); | ||||
|                 $this->addArgument(...$arguments); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -29,7 +29,7 @@ trait HasParameters | ||||
|             if ($options instanceof InputOption) { | ||||
|                 $this->getDefinition()->addOption($options); | ||||
|             } else { | ||||
|                 $this->addOption(...array_values($options)); | ||||
|                 $this->addOption(...$options); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -15,6 +15,15 @@ use Symfony\Component\Console\Question\Question; | ||||
|  | ||||
| trait InteractsWithIO | ||||
| { | ||||
|     /** | ||||
|      * The console components factory. | ||||
|      * | ||||
|      * @var \Illuminate\Console\View\Components\Factory | ||||
|      * | ||||
|      * @internal This property is not meant to be used or overwritten outside the framework. | ||||
|      */ | ||||
|     protected $components; | ||||
|  | ||||
|     /** | ||||
|      * The input interface implementation. | ||||
|      * | ||||
| @@ -64,7 +73,7 @@ trait InteractsWithIO | ||||
|      * Get the value of a command argument. | ||||
|      * | ||||
|      * @param  string|null  $key | ||||
|      * @return string|array|null | ||||
|      * @return array|string|bool|null | ||||
|      */ | ||||
|     public function argument($key = null) | ||||
|     { | ||||
| @@ -198,7 +207,7 @@ trait InteractsWithIO | ||||
|      * | ||||
|      * @param  string  $question | ||||
|      * @param  array  $choices | ||||
|      * @param  string|null  $default | ||||
|      * @param  string|int|null  $default | ||||
|      * @param  mixed|null  $attempts | ||||
|      * @param  bool  $multiple | ||||
|      * @return string|array | ||||
| @@ -217,7 +226,7 @@ trait InteractsWithIO | ||||
|      * | ||||
|      * @param  array  $headers | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable|array  $rows | ||||
|      * @param  string  $tableStyle | ||||
|      * @param  \Symfony\Component\Console\Helper\TableStyle|string  $tableStyle | ||||
|      * @param  array  $columnStyles | ||||
|      * @return void | ||||
|      */ | ||||
| @@ -355,28 +364,31 @@ trait InteractsWithIO | ||||
|      * Write a string in an alert box. | ||||
|      * | ||||
|      * @param  string  $string | ||||
|      * @param  int|string|null  $verbosity | ||||
|      * @return void | ||||
|      */ | ||||
|     public function alert($string) | ||||
|     public function alert($string, $verbosity = null) | ||||
|     { | ||||
|         $length = Str::length(strip_tags($string)) + 12; | ||||
|  | ||||
|         $this->comment(str_repeat('*', $length)); | ||||
|         $this->comment('*     '.$string.'     *'); | ||||
|         $this->comment(str_repeat('*', $length)); | ||||
|         $this->comment(str_repeat('*', $length), $verbosity); | ||||
|         $this->comment('*     '.$string.'     *', $verbosity); | ||||
|         $this->comment(str_repeat('*', $length), $verbosity); | ||||
|  | ||||
|         $this->newLine(); | ||||
|         $this->comment('', $verbosity); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write a blank line. | ||||
|      * | ||||
|      * @param  int  $count | ||||
|      * @return void | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function newLine($count = 1) | ||||
|     { | ||||
|         $this->output->newLine($count); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										51
									
								
								vendor/laravel/framework/src/Illuminate/Console/Concerns/InteractsWithSignals.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								vendor/laravel/framework/src/Illuminate/Console/Concerns/InteractsWithSignals.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\Concerns; | ||||
|  | ||||
| use Illuminate\Console\Signals; | ||||
| use Illuminate\Support\Arr; | ||||
|  | ||||
| trait InteractsWithSignals | ||||
| { | ||||
|     /** | ||||
|      * The signal registrar instance. | ||||
|      * | ||||
|      * @var \Illuminate\Console\Signals|null | ||||
|      */ | ||||
|     protected $signals; | ||||
|  | ||||
|     /** | ||||
|      * Define a callback to be run when the given signal(s) occurs. | ||||
|      * | ||||
|      * @param  iterable<array-key, int>|int  $signals | ||||
|      * @param  callable(int $signal): void  $callback | ||||
|      * @return void | ||||
|      */ | ||||
|     public function trap($signals, $callback) | ||||
|     { | ||||
|         Signals::whenAvailable(function () use ($signals, $callback) { | ||||
|             $this->signals ??= new Signals( | ||||
|                 $this->getApplication()->getSignalRegistry(), | ||||
|             ); | ||||
|  | ||||
|             collect(Arr::wrap($signals)) | ||||
|                 ->each(fn ($signal) => $this->signals->register($signal, $callback)); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Untrap signal handlers set within the command's handler. | ||||
|      * | ||||
|      * @return void | ||||
|      * | ||||
|      * @internal | ||||
|      */ | ||||
|     public function untrap() | ||||
|     { | ||||
|         if (! is_null($this->signals)) { | ||||
|             $this->signals->unregister(); | ||||
|  | ||||
|             $this->signals = null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -13,7 +13,7 @@ trait ConfirmableTrait | ||||
|      * @param  \Closure|bool|null  $callback | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function confirmToProceed($warning = 'Application In Production!', $callback = null) | ||||
|     public function confirmToProceed($warning = 'Application In Production', $callback = null) | ||||
|     { | ||||
|         $callback = is_null($callback) ? $this->getDefaultConfirmCallback() : $callback; | ||||
|  | ||||
| @@ -24,12 +24,14 @@ trait ConfirmableTrait | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             $this->alert($warning); | ||||
|             $this->components->alert($warning); | ||||
|  | ||||
|             $confirmed = $this->confirm('Do you really wish to run this command?'); | ||||
|             $confirmed = $this->components->confirm('Do you really wish to run this command?'); | ||||
|  | ||||
|             if (! $confirmed) { | ||||
|                 $this->comment('Command Canceled!'); | ||||
|                 $this->newLine(); | ||||
|  | ||||
|                 $this->components->warn('Command canceled.'); | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|   | ||||
							
								
								
									
										76
									
								
								vendor/laravel/framework/src/Illuminate/Console/ContainerCommandLoader.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/laravel/framework/src/Illuminate/Console/ContainerCommandLoader.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console; | ||||
|  | ||||
| use Psr\Container\ContainerInterface; | ||||
| use Symfony\Component\Console\Command\Command; | ||||
| use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; | ||||
| use Symfony\Component\Console\Exception\CommandNotFoundException; | ||||
|  | ||||
| class ContainerCommandLoader implements CommandLoaderInterface | ||||
| { | ||||
|     /** | ||||
|      * The container instance. | ||||
|      * | ||||
|      * @var \Psr\Container\ContainerInterface | ||||
|      */ | ||||
|     protected $container; | ||||
|  | ||||
|     /** | ||||
|      * A map of command names to classes. | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $commandMap; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command loader instance. | ||||
|      * | ||||
|      * @param  \Psr\Container\ContainerInterface  $container | ||||
|      * @param  array  $commandMap | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(ContainerInterface $container, array $commandMap) | ||||
|     { | ||||
|         $this->container = $container; | ||||
|         $this->commandMap = $commandMap; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Resolve a command from the container. | ||||
|      * | ||||
|      * @param  string  $name | ||||
|      * @return \Symfony\Component\Console\Command\Command | ||||
|      * | ||||
|      * @throws \Symfony\Component\Console\Exception\CommandNotFoundException | ||||
|      */ | ||||
|     public function get(string $name): Command | ||||
|     { | ||||
|         if (! $this->has($name)) { | ||||
|             throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); | ||||
|         } | ||||
|  | ||||
|         return $this->container->get($this->commandMap[$name]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determines if a command exists. | ||||
|      * | ||||
|      * @param  string  $name | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function has(string $name): bool | ||||
|     { | ||||
|         return $name && isset($this->commandMap[$name]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the command names. | ||||
|      * | ||||
|      * @return string[] | ||||
|      */ | ||||
|     public function getNames(): array | ||||
|     { | ||||
|         return array_keys($this->commandMap); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								vendor/laravel/framework/src/Illuminate/Console/Contracts/NewLineAware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/laravel/framework/src/Illuminate/Console/Contracts/NewLineAware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\Contracts; | ||||
|  | ||||
| interface NewLineAware | ||||
| { | ||||
|     /** | ||||
|      * Whether a newline has already been written. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function newLineWritten(); | ||||
| } | ||||
| @@ -56,9 +56,11 @@ abstract class GeneratorCommand extends Command | ||||
|         'endif', | ||||
|         'endswitch', | ||||
|         'endwhile', | ||||
|         'enum', | ||||
|         'eval', | ||||
|         'exit', | ||||
|         'extends', | ||||
|         'false', | ||||
|         'final', | ||||
|         'finally', | ||||
|         'fn', | ||||
| @@ -76,6 +78,7 @@ abstract class GeneratorCommand extends Command | ||||
|         'interface', | ||||
|         'isset', | ||||
|         'list', | ||||
|         'match', | ||||
|         'namespace', | ||||
|         'new', | ||||
|         'or', | ||||
| @@ -83,6 +86,7 @@ abstract class GeneratorCommand extends Command | ||||
|         'private', | ||||
|         'protected', | ||||
|         'public', | ||||
|         'readonly', | ||||
|         'require', | ||||
|         'require_once', | ||||
|         'return', | ||||
| @@ -90,6 +94,7 @@ abstract class GeneratorCommand extends Command | ||||
|         'switch', | ||||
|         'throw', | ||||
|         'trait', | ||||
|         'true', | ||||
|         'try', | ||||
|         'unset', | ||||
|         'use', | ||||
| @@ -97,6 +102,14 @@ abstract class GeneratorCommand extends Command | ||||
|         'while', | ||||
|         'xor', | ||||
|         'yield', | ||||
|         '__CLASS__', | ||||
|         '__DIR__', | ||||
|         '__FILE__', | ||||
|         '__FUNCTION__', | ||||
|         '__LINE__', | ||||
|         '__METHOD__', | ||||
|         '__NAMESPACE__', | ||||
|         '__TRAIT__', | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
| @@ -136,7 +149,7 @@ abstract class GeneratorCommand extends Command | ||||
|         // language and that the class name will actually be valid. If it is not valid we | ||||
|         // can error now and prevent from polluting the filesystem using invalid files. | ||||
|         if ($this->isReservedName($this->getNameInput())) { | ||||
|             $this->error('The name "'.$this->getNameInput().'" is reserved by PHP.'); | ||||
|             $this->components->error('The name "'.$this->getNameInput().'" is reserved by PHP.'); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
| @@ -151,7 +164,7 @@ abstract class GeneratorCommand extends Command | ||||
|         if ((! $this->hasOption('force') || | ||||
|              ! $this->option('force')) && | ||||
|              $this->alreadyExists($this->getNameInput())) { | ||||
|             $this->error($this->type.' already exists!'); | ||||
|             $this->components->error($this->type.' already exists.'); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
| @@ -163,11 +176,15 @@ abstract class GeneratorCommand extends Command | ||||
|  | ||||
|         $this->files->put($path, $this->sortImports($this->buildClass($name))); | ||||
|  | ||||
|         $this->info($this->type.' created successfully.'); | ||||
|         $info = $this->type; | ||||
|  | ||||
|         if (in_array(CreatesMatchingTest::class, class_uses_recursive($this))) { | ||||
|             $this->handleTestCreation($path); | ||||
|             if ($this->handleTestCreation($path)) { | ||||
|                 $info .= ' and test'; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $this->components->info(sprintf('%s [%s] created successfully.', $info, $path)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -340,7 +357,7 @@ abstract class GeneratorCommand extends Command | ||||
|      */ | ||||
|     protected function sortImports($stub) | ||||
|     { | ||||
|         if (preg_match('/(?P<imports>(?:use [^;]+;$\n?)+)/m', $stub, $match)) { | ||||
|         if (preg_match('/(?P<imports>(?:^use [^;{]+;$\n?)+)/m', $stub, $match)) { | ||||
|             $imports = explode("\n", trim($match['imports'])); | ||||
|  | ||||
|             sort($imports); | ||||
|   | ||||
| @@ -2,11 +2,12 @@ | ||||
|  | ||||
| namespace Illuminate\Console; | ||||
|  | ||||
| use Illuminate\Console\Contracts\NewLineAware; | ||||
| use Symfony\Component\Console\Input\InputInterface; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
| use Symfony\Component\Console\Style\SymfonyStyle; | ||||
|  | ||||
| class OutputStyle extends SymfonyStyle | ||||
| class OutputStyle extends SymfonyStyle implements NewLineAware | ||||
| { | ||||
|     /** | ||||
|      * The output instance. | ||||
| @@ -15,6 +16,13 @@ class OutputStyle extends SymfonyStyle | ||||
|      */ | ||||
|     private $output; | ||||
|  | ||||
|     /** | ||||
|      * If the last output written wrote a new line. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $newLineWritten = false; | ||||
|  | ||||
|     /** | ||||
|      * Create a new Console OutputStyle instance. | ||||
|      * | ||||
| @@ -29,12 +37,54 @@ class OutputStyle extends SymfonyStyle | ||||
|         parent::__construct($input, $output); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|      */ | ||||
|     public function write(string|iterable $messages, bool $newline = false, int $options = 0) | ||||
|     { | ||||
|         $this->newLineWritten = $newline; | ||||
|  | ||||
|         parent::write($messages, $newline, $options); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|      */ | ||||
|     public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL) | ||||
|     { | ||||
|         $this->newLineWritten = true; | ||||
|  | ||||
|         parent::writeln($messages, $type); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|      */ | ||||
|     public function newLine(int $count = 1) | ||||
|     { | ||||
|         $this->newLineWritten = $count > 0; | ||||
|  | ||||
|         parent::newLine($count); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|      */ | ||||
|     public function newLineWritten() | ||||
|     { | ||||
|         if ($this->output instanceof static && $this->output->newLineWritten()) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return $this->newLineWritten; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns whether verbosity is quiet (-q). | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isQuiet() | ||||
|     public function isQuiet(): bool | ||||
|     { | ||||
|         return $this->output->isQuiet(); | ||||
|     } | ||||
| @@ -44,7 +94,7 @@ class OutputStyle extends SymfonyStyle | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isVerbose() | ||||
|     public function isVerbose(): bool | ||||
|     { | ||||
|         return $this->output->isVerbose(); | ||||
|     } | ||||
| @@ -54,7 +104,7 @@ class OutputStyle extends SymfonyStyle | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isVeryVerbose() | ||||
|     public function isVeryVerbose(): bool | ||||
|     { | ||||
|         return $this->output->isVeryVerbose(); | ||||
|     } | ||||
| @@ -64,7 +114,7 @@ class OutputStyle extends SymfonyStyle | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isDebug() | ||||
|     public function isDebug(): bool | ||||
|     { | ||||
|         return $this->output->isDebug(); | ||||
|     } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
|  | ||||
| namespace Illuminate\Console; | ||||
|  | ||||
| use Illuminate\Support\Str; | ||||
| use InvalidArgumentException; | ||||
| use Symfony\Component\Console\Input\InputArgument; | ||||
| use Symfony\Component\Console\Input\InputOption; | ||||
| @@ -21,10 +20,8 @@ class Parser | ||||
|     { | ||||
|         $name = static::name($expression); | ||||
|  | ||||
|         if (preg_match_all('/\{\s*(.*?)\s*\}/', $expression, $matches)) { | ||||
|             if (count($matches[1])) { | ||||
|                 return array_merge([$name], static::parameters($matches[1])); | ||||
|             } | ||||
|         if (preg_match_all('/\{\s*(.*?)\s*\}/', $expression, $matches) && count($matches[1])) { | ||||
|             return array_merge([$name], static::parameters($matches[1])); | ||||
|         } | ||||
|  | ||||
|         return [$name, [], []]; | ||||
| @@ -81,11 +78,11 @@ class Parser | ||||
|         [$token, $description] = static::extractDescription($token); | ||||
|  | ||||
|         switch (true) { | ||||
|             case Str::endsWith($token, '?*'): | ||||
|             case str_ends_with($token, '?*'): | ||||
|                 return new InputArgument(trim($token, '?*'), InputArgument::IS_ARRAY, $description); | ||||
|             case Str::endsWith($token, '*'): | ||||
|             case str_ends_with($token, '*'): | ||||
|                 return new InputArgument(trim($token, '*'), InputArgument::IS_ARRAY | InputArgument::REQUIRED, $description); | ||||
|             case Str::endsWith($token, '?'): | ||||
|             case str_ends_with($token, '?'): | ||||
|                 return new InputArgument(trim($token, '?'), InputArgument::OPTIONAL, $description); | ||||
|             case preg_match('/(.+)\=\*(.+)/', $token, $matches): | ||||
|                 return new InputArgument($matches[1], InputArgument::IS_ARRAY, $description, preg_split('/,\s?/', $matches[2])); | ||||
| @@ -116,9 +113,9 @@ class Parser | ||||
|         } | ||||
|  | ||||
|         switch (true) { | ||||
|             case Str::endsWith($token, '='): | ||||
|             case str_ends_with($token, '='): | ||||
|                 return new InputOption(trim($token, '='), $shortcut, InputOption::VALUE_OPTIONAL, $description); | ||||
|             case Str::endsWith($token, '=*'): | ||||
|             case str_ends_with($token, '=*'): | ||||
|                 return new InputOption(trim($token, '=*'), $shortcut, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, $description); | ||||
|             case preg_match('/(.+)\=\*(.+)/', $token, $matches): | ||||
|                 return new InputOption($matches[1], $shortcut, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, $description, preg_split('/,\s?/', $matches[2])); | ||||
|   | ||||
							
								
								
									
										77
									
								
								vendor/laravel/framework/src/Illuminate/Console/QuestionHelper.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								vendor/laravel/framework/src/Illuminate/Console/QuestionHelper.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console; | ||||
|  | ||||
| use Illuminate\Console\View\Components\TwoColumnDetail; | ||||
| use Symfony\Component\Console\Formatter\OutputFormatter; | ||||
| use Symfony\Component\Console\Helper\SymfonyQuestionHelper; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
| use Symfony\Component\Console\Question\ChoiceQuestion; | ||||
| use Symfony\Component\Console\Question\ConfirmationQuestion; | ||||
| use Symfony\Component\Console\Question\Question; | ||||
|  | ||||
| class QuestionHelper extends SymfonyQuestionHelper | ||||
| { | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|      */ | ||||
|     protected function writePrompt(OutputInterface $output, Question $question) | ||||
|     { | ||||
|         $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion()); | ||||
|  | ||||
|         $text = $this->ensureEndsWithPunctuation($text); | ||||
|  | ||||
|         $text = "  <fg=default;options=bold>$text</></>"; | ||||
|  | ||||
|         $default = $question->getDefault(); | ||||
|  | ||||
|         if ($question->isMultiline()) { | ||||
|             $text .= sprintf(' (press %s to continue)', 'Windows' == PHP_OS_FAMILY | ||||
|                 ? '<comment>Ctrl+Z</comment> then <comment>Enter</comment>' | ||||
|                 : '<comment>Ctrl+D</comment>'); | ||||
|         } | ||||
|  | ||||
|         switch (true) { | ||||
|             case null === $default: | ||||
|                 $text = sprintf('<info>%s</info>', $text); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             case $question instanceof ConfirmationQuestion: | ||||
|                 $text = sprintf('<info>%s (yes/no)</info> [<comment>%s</comment>]', $text, $default ? 'yes' : 'no'); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             case $question instanceof ChoiceQuestion: | ||||
|                 $choices = $question->getChoices(); | ||||
|                 $text = sprintf('<info>%s</info> [<comment>%s</comment>]', $text, OutputFormatter::escape($choices[$default] ?? $default)); | ||||
|  | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         $output->writeln($text); | ||||
|  | ||||
|         if ($question instanceof ChoiceQuestion) { | ||||
|             foreach ($question->getChoices() as $key => $value) { | ||||
|                 with(new TwoColumnDetail($output))->render($value, $key); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $output->write('<options=bold>❯ </>'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Ensures the given string ends with punctuation. | ||||
|      * | ||||
|      * @param  string  $string | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function ensureEndsWithPunctuation($string) | ||||
|     { | ||||
|         if (! str($string)->endsWith(['?', ':', '!', '.'])) { | ||||
|             return "$string:"; | ||||
|         } | ||||
|  | ||||
|         return $string; | ||||
|     } | ||||
| } | ||||
| @@ -6,6 +6,7 @@ use Illuminate\Contracts\Container\Container; | ||||
| use Illuminate\Support\Reflector; | ||||
| use InvalidArgumentException; | ||||
| use LogicException; | ||||
| use RuntimeException; | ||||
| use Throwable; | ||||
|  | ||||
| class CallbackEvent extends Event | ||||
| @@ -24,11 +25,25 @@ class CallbackEvent extends Event | ||||
|      */ | ||||
|     protected $parameters; | ||||
|  | ||||
|     /** | ||||
|      * The result of the callback's execution. | ||||
|      * | ||||
|      * @var mixed | ||||
|      */ | ||||
|     protected $result; | ||||
|  | ||||
|     /** | ||||
|      * The exception that was thrown when calling the callback, if any. | ||||
|      * | ||||
|      * @var \Throwable|null | ||||
|      */ | ||||
|     protected $exception; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Scheduling\EventMutex  $mutex | ||||
|      * @param  string  $callback | ||||
|      * @param  string|callable  $callback | ||||
|      * @param  array  $parameters | ||||
|      * @param  \DateTimeZone|string|null  $timezone | ||||
|      * @return void | ||||
| @@ -50,58 +65,64 @@ class CallbackEvent extends Event | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the given event. | ||||
|      * Run the callback event. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Container\Container  $container | ||||
|      * @return mixed | ||||
|      * | ||||
|      * @throws \Exception | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     public function run(Container $container) | ||||
|     { | ||||
|         if ($this->description && $this->withoutOverlapping && | ||||
|             ! $this->mutex->create($this)) { | ||||
|             return; | ||||
|         parent::run($container); | ||||
|  | ||||
|         if ($this->exception) { | ||||
|             throw $this->exception; | ||||
|         } | ||||
|  | ||||
|         $pid = getmypid(); | ||||
|  | ||||
|         register_shutdown_function(function () use ($pid) { | ||||
|             if ($pid === getmypid()) { | ||||
|                 $this->removeMutex(); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         parent::callBeforeCallbacks($container); | ||||
|  | ||||
|         try { | ||||
|             $response = is_object($this->callback) | ||||
|                         ? $container->call([$this->callback, '__invoke'], $this->parameters) | ||||
|                         : $container->call($this->callback, $this->parameters); | ||||
|  | ||||
|             $this->exitCode = $response === false ? 1 : 0; | ||||
|         } catch (Throwable $e) { | ||||
|             $this->exitCode = 1; | ||||
|  | ||||
|             throw $e; | ||||
|         } finally { | ||||
|             $this->removeMutex(); | ||||
|  | ||||
|             parent::callAfterCallbacks($container); | ||||
|         } | ||||
|  | ||||
|         return $response; | ||||
|         return $this->result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clear the mutex for the event. | ||||
|      * Determine if the event should skip because another process is overlapping. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function shouldSkipDueToOverlapping() | ||||
|     { | ||||
|         return $this->description && parent::shouldSkipDueToOverlapping(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Indicate that the callback should run in the background. | ||||
|      * | ||||
|      * @return void | ||||
|      * | ||||
|      * @throws \RuntimeException | ||||
|      */ | ||||
|     protected function removeMutex() | ||||
|     public function runInBackground() | ||||
|     { | ||||
|         if ($this->description && $this->withoutOverlapping) { | ||||
|             $this->mutex->forget($this); | ||||
|         throw new RuntimeException('Scheduled closures can not be run in the background.'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the callback. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Container\Container  $container | ||||
|      * @return int | ||||
|      */ | ||||
|     protected function execute($container) | ||||
|     { | ||||
|         try { | ||||
|             $this->result = is_object($this->callback) | ||||
|                 ? $container->call([$this->callback, '__invoke'], $this->parameters) | ||||
|                 : $container->call($this->callback, $this->parameters); | ||||
|  | ||||
|             return $this->result === false ? 1 : 0; | ||||
|         } catch (Throwable $e) { | ||||
|             $this->exception = $e; | ||||
|  | ||||
|             return 1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -121,13 +142,7 @@ class CallbackEvent extends Event | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $this->withoutOverlapping = true; | ||||
|  | ||||
|         $this->expiresAt = $expiresAt; | ||||
|  | ||||
|         return $this->skip(function () { | ||||
|             return $this->mutex->exists($this); | ||||
|         }); | ||||
|         return parent::withoutOverlapping($expiresAt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -145,19 +160,7 @@ class CallbackEvent extends Event | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $this->onOneServer = true; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the mutex name for the scheduled command. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function mutexName() | ||||
|     { | ||||
|         return 'framework/schedule-'.sha1($this->description); | ||||
|         return parent::onOneServer(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -173,4 +176,26 @@ class CallbackEvent extends Event | ||||
|  | ||||
|         return is_string($this->callback) ? $this->callback : 'Callback'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the mutex name for the scheduled command. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function mutexName() | ||||
|     { | ||||
|         return 'framework/schedule-'.sha1($this->description ?? ''); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clear the mutex for the event. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function removeMutex() | ||||
|     { | ||||
|         if ($this->description) { | ||||
|             parent::removeMutex(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class Event | ||||
|     /** | ||||
|      * The command string. | ||||
|      * | ||||
|      * @var string | ||||
|      * @var string|null | ||||
|      */ | ||||
|     public $command; | ||||
|  | ||||
| @@ -47,7 +47,7 @@ class Event | ||||
|     /** | ||||
|      * The user the command should run as. | ||||
|      * | ||||
|      * @var string | ||||
|      * @var string|null | ||||
|      */ | ||||
|     public $user; | ||||
|  | ||||
| @@ -138,7 +138,7 @@ class Event | ||||
|     /** | ||||
|      * The human readable description of the event. | ||||
|      * | ||||
|      * @var string | ||||
|      * @var string|null | ||||
|      */ | ||||
|     public $description; | ||||
|  | ||||
| @@ -149,6 +149,13 @@ class Event | ||||
|      */ | ||||
|     public $mutex; | ||||
|  | ||||
|     /** | ||||
|      * The mutex name resolver callback. | ||||
|      * | ||||
|      * @var \Closure|null | ||||
|      */ | ||||
|     public $mutexNameResolver; | ||||
|  | ||||
|     /** | ||||
|      * The exit status code of the command. | ||||
|      * | ||||
| @@ -188,62 +195,46 @@ class Event | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Container\Container  $container | ||||
|      * @return void | ||||
|      * | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     public function run(Container $container) | ||||
|     { | ||||
|         if ($this->withoutOverlapping && | ||||
|             ! $this->mutex->create($this)) { | ||||
|         if ($this->shouldSkipDueToOverlapping()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->runInBackground | ||||
|                     ? $this->runCommandInBackground($container) | ||||
|                     : $this->runCommandInForeground($container); | ||||
|     } | ||||
|         $exitCode = $this->start($container); | ||||
|  | ||||
|     /** | ||||
|      * Get the mutex name for the scheduled command. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function mutexName() | ||||
|     { | ||||
|         return 'framework'.DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the command in the foreground. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Container\Container  $container | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function runCommandInForeground(Container $container) | ||||
|     { | ||||
|         try { | ||||
|             $this->callBeforeCallbacks($container); | ||||
|  | ||||
|             $this->exitCode = Process::fromShellCommandline( | ||||
|                 $this->buildCommand(), base_path(), null, null, null | ||||
|             )->run(); | ||||
|  | ||||
|             $this->callAfterCallbacks($container); | ||||
|         } finally { | ||||
|             $this->removeMutex(); | ||||
|         if (! $this->runInBackground) { | ||||
|             $this->finish($container, $exitCode); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the command in the background. | ||||
|      * Determine if the event should skip because another process is overlapping. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function shouldSkipDueToOverlapping() | ||||
|     { | ||||
|         return $this->withoutOverlapping && ! $this->mutex->create($this); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the command process. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Container\Container  $container | ||||
|      * @return void | ||||
|      * @return int | ||||
|      * | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     protected function runCommandInBackground(Container $container) | ||||
|     protected function start($container) | ||||
|     { | ||||
|         try { | ||||
|             $this->callBeforeCallbacks($container); | ||||
|  | ||||
|             Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run(); | ||||
|             return $this->execute($container); | ||||
|         } catch (Throwable $exception) { | ||||
|             $this->removeMutex(); | ||||
|  | ||||
| @@ -251,6 +242,37 @@ class Event | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the command process. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Container\Container  $container | ||||
|      * @return int | ||||
|      */ | ||||
|     protected function execute($container) | ||||
|     { | ||||
|         return Process::fromShellCommandline( | ||||
|             $this->buildCommand(), base_path(), null, null, null | ||||
|         )->run(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Mark the command process as finished and run callbacks/cleanup. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Container\Container  $container | ||||
|      * @param  int  $exitCode | ||||
|      * @return void | ||||
|      */ | ||||
|     public function finish(Container $container, $exitCode) | ||||
|     { | ||||
|         $this->exitCode = (int) $exitCode; | ||||
|  | ||||
|         try { | ||||
|             $this->callAfterCallbacks($container); | ||||
|         } finally { | ||||
|             $this->removeMutex(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Call all of the "before" callbacks for the event. | ||||
|      * | ||||
| @@ -277,24 +299,6 @@ class Event | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Call all of the "after" callbacks for the event. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Container\Container  $container | ||||
|      * @param  int  $exitCode | ||||
|      * @return void | ||||
|      */ | ||||
|     public function callAfterCallbacksWithExitCode(Container $container, $exitCode) | ||||
|     { | ||||
|         $this->exitCode = (int) $exitCode; | ||||
|  | ||||
|         try { | ||||
|             $this->callAfterCallbacks($container); | ||||
|         } finally { | ||||
|             $this->removeMutex(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Build the command string. | ||||
|      * | ||||
| @@ -783,7 +787,7 @@ class Event | ||||
|         } | ||||
|  | ||||
|         return $this->then(function (Container $container) use ($callback) { | ||||
|             if (0 === $this->exitCode) { | ||||
|             if ($this->exitCode === 0) { | ||||
|                 $container->call($callback); | ||||
|             } | ||||
|         }); | ||||
| @@ -818,7 +822,7 @@ class Event | ||||
|         } | ||||
|  | ||||
|         return $this->then(function (Container $container) use ($callback) { | ||||
|             if (0 !== $this->exitCode) { | ||||
|             if ($this->exitCode !== 0) { | ||||
|                 $container->call($callback); | ||||
|             } | ||||
|         }); | ||||
| @@ -931,6 +935,35 @@ class Event | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the mutex name for the scheduled command. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function mutexName() | ||||
|     { | ||||
|         $mutexNameResolver = $this->mutexNameResolver; | ||||
|  | ||||
|         if (! is_null($mutexNameResolver) && is_callable($mutexNameResolver)) { | ||||
|             return $mutexNameResolver($this); | ||||
|         } | ||||
|  | ||||
|         return 'framework'.DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the mutex name or name resolver callback. | ||||
|      * | ||||
|      * @param  \Closure|string  $mutexName | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function createMutexNameUsing(Closure|string $mutexName) | ||||
|     { | ||||
|         $this->mutexNameResolver = is_string($mutexName) ? fn () => $mutexName : $mutexName; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete the mutex for the event. | ||||
|      * | ||||
|   | ||||
| @@ -60,9 +60,9 @@ trait ManagesFrequencies | ||||
|  | ||||
|         if ($endTime->lessThan($startTime)) { | ||||
|             if ($startTime->greaterThan($now)) { | ||||
|                 $startTime->subDay(1); | ||||
|                 $startTime = $startTime->subDay(1); | ||||
|             } else { | ||||
|                 $endTime->addDay(1); | ||||
|                 $endTime = $endTime->addDay(1); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -174,6 +174,16 @@ trait ManagesFrequencies | ||||
|         return $this->spliceIntoPosition(1, $offset); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Schedule the event to run every odd hour. | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function everyOddHour() | ||||
|     { | ||||
|         return $this->spliceIntoPosition(1, 0)->spliceIntoPosition(2, '1-23/2'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Schedule the event to run every two hours. | ||||
|      * | ||||
| @@ -467,6 +477,21 @@ trait ManagesFrequencies | ||||
|                     ->spliceIntoPosition(4, '1-12/3'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Schedule the event to run quarterly on a given day and time. | ||||
|      * | ||||
|      * @param  int  $dayOfQuarter | ||||
|      * @param  int  $time | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function quarterlyOn($dayOfQuarter = 1, $time = '0:0') | ||||
|     { | ||||
|         $this->dailyAt($time); | ||||
|  | ||||
|         return $this->spliceIntoPosition(3, $dayOfQuarter) | ||||
|                     ->spliceIntoPosition(4, '1-12/3'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Schedule the event to run yearly. | ||||
|      * | ||||
| @@ -531,7 +556,7 @@ trait ManagesFrequencies | ||||
|      */ | ||||
|     protected function spliceIntoPosition($position, $value) | ||||
|     { | ||||
|         $segments = explode(' ', $this->expression); | ||||
|         $segments = preg_split("/\s+/", $this->expression); | ||||
|  | ||||
|         $segments[$position - 1] = $value; | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,6 @@ use Illuminate\Contracts\Queue\ShouldBeUnique; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Queue\CallQueuedClosure; | ||||
| use Illuminate\Support\ProcessUtils; | ||||
| use Illuminate\Support\Str; | ||||
| use Illuminate\Support\Traits\Macroable; | ||||
| use RuntimeException; | ||||
|  | ||||
| @@ -23,11 +22,17 @@ class Schedule | ||||
|     use Macroable; | ||||
|  | ||||
|     const SUNDAY = 0; | ||||
|  | ||||
|     const MONDAY = 1; | ||||
|  | ||||
|     const TUESDAY = 2; | ||||
|  | ||||
|     const WEDNESDAY = 3; | ||||
|  | ||||
|     const THURSDAY = 4; | ||||
|  | ||||
|     const FRIDAY = 5; | ||||
|  | ||||
|     const SATURDAY = 6; | ||||
|  | ||||
|     /** | ||||
| @@ -272,11 +277,11 @@ class Schedule | ||||
|             return ProcessUtils::escapeArgument($value); | ||||
|         }); | ||||
|  | ||||
|         if (Str::startsWith($key, '--')) { | ||||
|         if (str_starts_with($key, '--')) { | ||||
|             $value = $value->map(function ($value) use ($key) { | ||||
|                 return "{$key}={$value}"; | ||||
|             }); | ||||
|         } elseif (Str::startsWith($key, '-')) { | ||||
|         } elseif (str_starts_with($key, '-')) { | ||||
|             $value = $value->map(function ($value) use ($key) { | ||||
|                 return "{$key} {$value}"; | ||||
|             }); | ||||
| @@ -352,7 +357,7 @@ class Schedule | ||||
|             } catch (BindingResolutionException $e) { | ||||
|                 throw new RuntimeException( | ||||
|                     'Unable to resolve the dispatcher from the service container. Please bind it or install the illuminate/bus package.', | ||||
|                     $e->getCode(), $e | ||||
|                     is_int($e->getCode()) ? $e->getCode() : 0, $e | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -32,7 +32,7 @@ class ScheduleClearCacheCommand extends Command | ||||
|  | ||||
|         foreach ($schedule->events($this->laravel) as $event) { | ||||
|             if ($event->mutex->exists($event)) { | ||||
|                 $this->line('<info>Deleting mutex for:</info> '.$event->command); | ||||
|                 $this->components->info(sprintf('Deleting mutex for [%s]', $event->command)); | ||||
|  | ||||
|                 $event->mutex->forget($event); | ||||
|  | ||||
| @@ -41,7 +41,7 @@ class ScheduleClearCacheCommand extends Command | ||||
|         } | ||||
|  | ||||
|         if (! $mutexCleared) { | ||||
|             $this->info('No mutex files were found.'); | ||||
|             $this->components->info('No mutex files were found.'); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,9 @@ namespace Illuminate\Console\Scheduling; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Console\Events\ScheduledBackgroundTaskFinished; | ||||
| use Illuminate\Contracts\Events\Dispatcher; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
|  | ||||
| #[AsCommand(name: 'schedule:finish')] | ||||
| class ScheduleFinishCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -15,6 +17,17 @@ class ScheduleFinishCommand extends Command | ||||
|      */ | ||||
|     protected $signature = 'schedule:finish {id} {code=0}'; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'schedule:finish'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
| @@ -40,7 +53,7 @@ class ScheduleFinishCommand extends Command | ||||
|         collect($schedule->events())->filter(function ($value) { | ||||
|             return $value->mutexName() == $this->argument('id'); | ||||
|         })->each(function ($event) { | ||||
|             $event->callafterCallbacksWithExitCode($this->laravel, $this->argument('code')); | ||||
|             $event->finish($this->laravel, $this->argument('code')); | ||||
|  | ||||
|             $this->laravel->make(Dispatcher::class)->dispatch(new ScheduledBackgroundTaskFinished($event)); | ||||
|         }); | ||||
|   | ||||
| @@ -2,11 +2,18 @@ | ||||
|  | ||||
| namespace Illuminate\Console\Scheduling; | ||||
|  | ||||
| use Closure; | ||||
| use Cron\CronExpression; | ||||
| use DateTimeZone; | ||||
| use Illuminate\Console\Application; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Support\Carbon; | ||||
| use ReflectionClass; | ||||
| use ReflectionFunction; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
| use Symfony\Component\Console\Terminal; | ||||
|  | ||||
| #[AsCommand(name: 'schedule:list')] | ||||
| class ScheduleListCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -14,14 +21,35 @@ class ScheduleListCommand extends Command | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'schedule:list {--timezone= : The timezone that times should be displayed in}'; | ||||
|     protected $signature = 'schedule:list | ||||
|         {--timezone= : The timezone that times should be displayed in} | ||||
|         {--next : Sort the listed tasks by their next due date} | ||||
|     '; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'schedule:list'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'List the scheduled commands'; | ||||
|     protected $description = 'List all scheduled tasks'; | ||||
|  | ||||
|     /** | ||||
|      * The terminal width resolver callback. | ||||
|      * | ||||
|      * @var \Closure|null | ||||
|      */ | ||||
|     protected static $terminalWidthResolver; | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
| @@ -33,23 +61,199 @@ class ScheduleListCommand extends Command | ||||
|      */ | ||||
|     public function handle(Schedule $schedule) | ||||
|     { | ||||
|         foreach ($schedule->events() as $event) { | ||||
|             $rows[] = [ | ||||
|                 $event->command, | ||||
|                 $event->expression, | ||||
|                 $event->description, | ||||
|                 (new CronExpression($event->expression)) | ||||
|                             ->getNextRunDate(Carbon::now()->setTimezone($event->timezone)) | ||||
|                             ->setTimezone(new DateTimeZone($this->option('timezone') ?? config('app.timezone'))) | ||||
|                             ->format('Y-m-d H:i:s P'), | ||||
|             ]; | ||||
|         $events = collect($schedule->events()); | ||||
|  | ||||
|         if ($events->isEmpty()) { | ||||
|             $this->components->info('No scheduled tasks have been defined.'); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->table([ | ||||
|             'Command', | ||||
|             'Interval', | ||||
|             'Description', | ||||
|             'Next Due', | ||||
|         ], $rows ?? []); | ||||
|         $terminalWidth = self::getTerminalWidth(); | ||||
|  | ||||
|         $expressionSpacing = $this->getCronExpressionSpacing($events); | ||||
|  | ||||
|         $timezone = new DateTimeZone($this->option('timezone') ?? config('app.timezone')); | ||||
|  | ||||
|         $events = $this->sortEvents($events, $timezone); | ||||
|  | ||||
|         $events = $events->map(function ($event) use ($terminalWidth, $expressionSpacing, $timezone) { | ||||
|             $expression = $this->formatCronExpression($event->expression, $expressionSpacing); | ||||
|  | ||||
|             $command = $event->command ?? ''; | ||||
|  | ||||
|             $description = $event->description ?? ''; | ||||
|  | ||||
|             if (! $this->output->isVerbose()) { | ||||
|                 $command = str_replace([Application::phpBinary(), Application::artisanBinary()], [ | ||||
|                     'php', | ||||
|                     preg_replace("#['\"]#", '', Application::artisanBinary()), | ||||
|                 ], $command); | ||||
|             } | ||||
|  | ||||
|             if ($event instanceof CallbackEvent) { | ||||
|                 if (class_exists($description)) { | ||||
|                     $command = $description; | ||||
|                     $description = ''; | ||||
|                 } else { | ||||
|                     $command = 'Closure at: '.$this->getClosureLocation($event); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             $command = mb_strlen($command) > 1 ? "{$command} " : ''; | ||||
|  | ||||
|             $nextDueDateLabel = 'Next Due:'; | ||||
|  | ||||
|             $nextDueDate = $this->getNextDueDateForEvent($event, $timezone); | ||||
|  | ||||
|             $nextDueDate = $this->output->isVerbose() | ||||
|                 ? $nextDueDate->format('Y-m-d H:i:s P') | ||||
|                 : $nextDueDate->diffForHumans(); | ||||
|  | ||||
|             $hasMutex = $event->mutex->exists($event) ? 'Has Mutex › ' : ''; | ||||
|  | ||||
|             $dots = str_repeat('.', max( | ||||
|                 $terminalWidth - mb_strlen($expression.$command.$nextDueDateLabel.$nextDueDate.$hasMutex) - 8, 0 | ||||
|             )); | ||||
|  | ||||
|             // Highlight the parameters... | ||||
|             $command = preg_replace("#(php artisan [\w\-:]+) (.+)#", '$1 <fg=yellow;options=bold>$2</>', $command); | ||||
|  | ||||
|             return [sprintf( | ||||
|                 '  <fg=yellow>%s</>  %s<fg=#6C7280>%s %s%s %s</>', | ||||
|                 $expression, | ||||
|                 $command, | ||||
|                 $dots, | ||||
|                 $hasMutex, | ||||
|                 $nextDueDateLabel, | ||||
|                 $nextDueDate | ||||
|             ), $this->output->isVerbose() && mb_strlen($description) > 1 ? sprintf( | ||||
|                 '  <fg=#6C7280>%s%s %s</>', | ||||
|                 str_repeat(' ', mb_strlen($expression) + 2), | ||||
|                 '⇁', | ||||
|                 $description | ||||
|             ) : '']; | ||||
|         }); | ||||
|  | ||||
|         $this->line( | ||||
|             $events->flatten()->filter()->prepend('')->push('')->toArray() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the spacing to be used on each event row. | ||||
|      * | ||||
|      * @param  \Illuminate\Support\Collection  $events | ||||
|      * @return array<int, int> | ||||
|      */ | ||||
|     private function getCronExpressionSpacing($events) | ||||
|     { | ||||
|         $rows = $events->map(fn ($event) => array_map('mb_strlen', preg_split("/\s+/", $event->expression))); | ||||
|  | ||||
|         return collect($rows[0] ?? [])->keys()->map(fn ($key) => $rows->max($key))->all(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sorts the events by due date if option set. | ||||
|      * | ||||
|      * @param  \Illuminate\Support\Collection  $events | ||||
|      * @param  \DateTimeZone  $timezone | ||||
|      * @return \Illuminate\Support\Collection | ||||
|      */ | ||||
|     private function sortEvents(\Illuminate\Support\Collection $events, DateTimeZone $timezone) | ||||
|     { | ||||
|         return $this->option('next') | ||||
|                     ? $events->sortBy(fn ($event) => $this->getNextDueDateForEvent($event, $timezone)) | ||||
|                     : $events; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the next due date for an event. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Scheduling\Event  $event | ||||
|      * @param  \DateTimeZone  $timezone | ||||
|      * @return \Illuminate\Support\Carbon | ||||
|      */ | ||||
|     private function getNextDueDateForEvent($event, DateTimeZone $timezone) | ||||
|     { | ||||
|         return Carbon::instance( | ||||
|             (new CronExpression($event->expression)) | ||||
|                 ->getNextRunDate(Carbon::now()->setTimezone($event->timezone)) | ||||
|                 ->setTimezone($timezone) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Formats the cron expression based on the spacing provided. | ||||
|      * | ||||
|      * @param  string  $expression | ||||
|      * @param  array<int, int>  $spacing | ||||
|      * @return string | ||||
|      */ | ||||
|     private function formatCronExpression($expression, $spacing) | ||||
|     { | ||||
|         $expressions = preg_split("/\s+/", $expression); | ||||
|  | ||||
|         return collect($spacing) | ||||
|             ->map(fn ($length, $index) => str_pad($expressions[$index], $length)) | ||||
|             ->implode(' '); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the file and line number for the event closure. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Scheduling\CallbackEvent  $event | ||||
|      * @return string | ||||
|      */ | ||||
|     private function getClosureLocation(CallbackEvent $event) | ||||
|     { | ||||
|         $callback = tap((new ReflectionClass($event))->getProperty('callback')) | ||||
|                         ->setAccessible(true) | ||||
|                         ->getValue($event); | ||||
|  | ||||
|         if ($callback instanceof Closure) { | ||||
|             $function = new ReflectionFunction($callback); | ||||
|  | ||||
|             return sprintf( | ||||
|                 '%s:%s', | ||||
|                 str_replace($this->laravel->basePath().DIRECTORY_SEPARATOR, '', $function->getFileName() ?: ''), | ||||
|                 $function->getStartLine() | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         if (is_string($callback)) { | ||||
|             return $callback; | ||||
|         } | ||||
|  | ||||
|         if (is_array($callback)) { | ||||
|             $className = is_string($callback[0]) ? $callback[0] : $callback[0]::class; | ||||
|  | ||||
|             return sprintf('%s::%s', $className, $callback[1]); | ||||
|         } | ||||
|  | ||||
|         return sprintf('%s::__invoke', $callback::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the terminal width. | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public static function getTerminalWidth() | ||||
|     { | ||||
|         return is_null(static::$terminalWidthResolver) | ||||
|             ? (new Terminal)->getWidth() | ||||
|             : call_user_func(static::$terminalWidthResolver); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set a callback that should be used when resolving the terminal width. | ||||
|      * | ||||
|      * @param  \Closure|null  $resolver | ||||
|      * @return void | ||||
|      */ | ||||
|     public static function resolveTerminalWidthUsing($resolver) | ||||
|     { | ||||
|         static::$terminalWidthResolver = $resolver; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace Illuminate\Console\Scheduling; | ||||
|  | ||||
| use Illuminate\Console\Application; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Console\Events\ScheduledTaskFailed; | ||||
| use Illuminate\Console\Events\ScheduledTaskFinished; | ||||
| @@ -9,9 +10,12 @@ use Illuminate\Console\Events\ScheduledTaskSkipped; | ||||
| use Illuminate\Console\Events\ScheduledTaskStarting; | ||||
| use Illuminate\Contracts\Debug\ExceptionHandler; | ||||
| use Illuminate\Contracts\Events\Dispatcher; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Illuminate\Support\Facades\Date; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
| use Throwable; | ||||
|  | ||||
| #[AsCommand(name: 'schedule:run')] | ||||
| class ScheduleRunCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -21,6 +25,17 @@ class ScheduleRunCommand extends Command | ||||
|      */ | ||||
|     protected $name = 'schedule:run'; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'schedule:run'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
| @@ -63,6 +78,13 @@ class ScheduleRunCommand extends Command | ||||
|      */ | ||||
|     protected $handler; | ||||
|  | ||||
|     /** | ||||
|      * The PHP binary used by the command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $phpBinary; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
| @@ -88,6 +110,9 @@ class ScheduleRunCommand extends Command | ||||
|         $this->schedule = $schedule; | ||||
|         $this->dispatcher = $dispatcher; | ||||
|         $this->handler = $handler; | ||||
|         $this->phpBinary = Application::phpBinary(); | ||||
|  | ||||
|         $this->newLine(); | ||||
|  | ||||
|         foreach ($this->schedule->dueEvents($this->laravel) as $event) { | ||||
|             if (! $event->filtersPass($this->laravel)) { | ||||
| @@ -106,7 +131,9 @@ class ScheduleRunCommand extends Command | ||||
|         } | ||||
|  | ||||
|         if (! $this->eventsRan) { | ||||
|             $this->info('No scheduled commands are ready to run.'); | ||||
|             $this->components->info('No scheduled commands are ready to run.'); | ||||
|         } else { | ||||
|             $this->newLine(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -121,7 +148,9 @@ class ScheduleRunCommand extends Command | ||||
|         if ($this->schedule->serverShouldRun($event, $this->startedAt)) { | ||||
|             $this->runEvent($event); | ||||
|         } else { | ||||
|             $this->line('<info>Skipping command (has already run on another server):</info> '.$event->getSummaryForDisplay()); | ||||
|             $this->components->info(sprintf( | ||||
|                 'Skipping [%s], as command already run on another server.', $event->getSummaryForDisplay() | ||||
|             )); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -133,25 +162,46 @@ class ScheduleRunCommand extends Command | ||||
|      */ | ||||
|     protected function runEvent($event) | ||||
|     { | ||||
|         $this->line('<info>['.date('c').'] Running scheduled command:</info> '.$event->getSummaryForDisplay()); | ||||
|         $summary = $event->getSummaryForDisplay(); | ||||
|  | ||||
|         $this->dispatcher->dispatch(new ScheduledTaskStarting($event)); | ||||
|         $command = $event instanceof CallbackEvent | ||||
|             ? $summary | ||||
|             : trim(str_replace($this->phpBinary, '', $event->command)); | ||||
|  | ||||
|         $start = microtime(true); | ||||
|         $description = sprintf( | ||||
|             '<fg=gray>%s</> Running [%s]%s', | ||||
|             Carbon::now()->format('Y-m-d H:i:s'), | ||||
|             $command, | ||||
|             $event->runInBackground ? ' in background' : '', | ||||
|         ); | ||||
|  | ||||
|         try { | ||||
|             $event->run($this->laravel); | ||||
|         $this->components->task($description, function () use ($event) { | ||||
|             $this->dispatcher->dispatch(new ScheduledTaskStarting($event)); | ||||
|  | ||||
|             $this->dispatcher->dispatch(new ScheduledTaskFinished( | ||||
|                 $event, | ||||
|                 round(microtime(true) - $start, 2) | ||||
|             )); | ||||
|             $start = microtime(true); | ||||
|  | ||||
|             $this->eventsRan = true; | ||||
|         } catch (Throwable $e) { | ||||
|             $this->dispatcher->dispatch(new ScheduledTaskFailed($event, $e)); | ||||
|             try { | ||||
|                 $event->run($this->laravel); | ||||
|  | ||||
|             $this->handler->report($e); | ||||
|                 $this->dispatcher->dispatch(new ScheduledTaskFinished( | ||||
|                     $event, | ||||
|                     round(microtime(true) - $start, 2) | ||||
|                 )); | ||||
|  | ||||
|                 $this->eventsRan = true; | ||||
|             } catch (Throwable $e) { | ||||
|                 $this->dispatcher->dispatch(new ScheduledTaskFailed($event, $e)); | ||||
|  | ||||
|                 $this->handler->report($e); | ||||
|             } | ||||
|  | ||||
|             return $event->exitCode == 0; | ||||
|         }); | ||||
|  | ||||
|         if (! $event instanceof CallbackEvent) { | ||||
|             $this->components->bulletList([ | ||||
|                 $event->getSummaryForDisplay(), | ||||
|             ]); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,8 +2,11 @@ | ||||
|  | ||||
| namespace Illuminate\Console\Scheduling; | ||||
|  | ||||
| use Illuminate\Console\Application; | ||||
| use Illuminate\Console\Command; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
|  | ||||
| #[AsCommand(name: 'schedule:test')] | ||||
| class ScheduleTestCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -11,7 +14,18 @@ class ScheduleTestCommand extends Command | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $name = 'schedule:test'; | ||||
|     protected $signature = 'schedule:test {--name= : The name of the scheduled command to run}'; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'schedule:test'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
| @@ -28,6 +42,8 @@ class ScheduleTestCommand extends Command | ||||
|      */ | ||||
|     public function handle(Schedule $schedule) | ||||
|     { | ||||
|         $phpBinary = Application::phpBinary(); | ||||
|  | ||||
|         $commands = $schedule->events(); | ||||
|  | ||||
|         $commandNames = []; | ||||
| @@ -36,12 +52,48 @@ class ScheduleTestCommand extends Command | ||||
|             $commandNames[] = $command->command ?? $command->getSummaryForDisplay(); | ||||
|         } | ||||
|  | ||||
|         $index = array_search($this->choice('Which command would you like to run?', $commandNames), $commandNames); | ||||
|         if (empty($commandNames)) { | ||||
|             return $this->components->info('No scheduled commands have been defined.'); | ||||
|         } | ||||
|  | ||||
|         if (! empty($name = $this->option('name'))) { | ||||
|             $commandBinary = $phpBinary.' '.Application::artisanBinary(); | ||||
|  | ||||
|             $matches = array_filter($commandNames, function ($commandName) use ($commandBinary, $name) { | ||||
|                 return trim(str_replace($commandBinary, '', $commandName)) === $name; | ||||
|             }); | ||||
|  | ||||
|             if (count($matches) !== 1) { | ||||
|                 $this->components->info('No matching scheduled command found.'); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             $index = key($matches); | ||||
|         } else { | ||||
|             $index = array_search($this->components->choice('Which command would you like to run?', $commandNames), $commandNames); | ||||
|         } | ||||
|  | ||||
|         $event = $commands[$index]; | ||||
|  | ||||
|         $this->line('<info>['.date('c').'] Running scheduled command:</info> '.$event->getSummaryForDisplay()); | ||||
|         $summary = $event->getSummaryForDisplay(); | ||||
|  | ||||
|         $event->run($this->laravel); | ||||
|         $command = $event instanceof CallbackEvent | ||||
|             ? $summary | ||||
|             : trim(str_replace($phpBinary, '', $event->command)); | ||||
|  | ||||
|         $description = sprintf( | ||||
|             'Running [%s]%s', | ||||
|             $command, | ||||
|             $event->runInBackground ? ' in background' : '', | ||||
|         ); | ||||
|  | ||||
|         $this->components->task($description, fn () => $event->run($this->laravel)); | ||||
|  | ||||
|         if (! $event instanceof CallbackEvent) { | ||||
|             $this->components->bulletList([$event->getSummaryForDisplay()]); | ||||
|         } | ||||
|  | ||||
|         $this->newLine(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,8 +4,10 @@ namespace Illuminate\Console\Scheduling; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Symfony\Component\Console\Attribute\AsCommand; | ||||
| use Symfony\Component\Process\Process; | ||||
|  | ||||
| #[AsCommand(name: 'schedule:work')] | ||||
| class ScheduleWorkCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -15,6 +17,17 @@ class ScheduleWorkCommand extends Command | ||||
|      */ | ||||
|     protected $name = 'schedule:work'; | ||||
|  | ||||
|     /** | ||||
|      * The name of the console command. | ||||
|      * | ||||
|      * This name is used to identify the command during lazy loading. | ||||
|      * | ||||
|      * @var string|null | ||||
|      * | ||||
|      * @deprecated | ||||
|      */ | ||||
|     protected static $defaultName = 'schedule:work'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
| @@ -29,9 +42,9 @@ class ScheduleWorkCommand extends Command | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         $this->info('Schedule worker started successfully.'); | ||||
|         $this->components->info('Running schedule tasks every minute.'); | ||||
|  | ||||
|         [$lastExecutionStartedAt, $keyOfLastExecutionWithOutput, $executions] = [null, null, []]; | ||||
|         [$lastExecutionStartedAt, $executions] = [null, []]; | ||||
|  | ||||
|         while (true) { | ||||
|             usleep(100 * 1000); | ||||
| @@ -50,18 +63,10 @@ class ScheduleWorkCommand extends Command | ||||
|             } | ||||
|  | ||||
|             foreach ($executions as $key => $execution) { | ||||
|                 $output = trim($execution->getIncrementalOutput()). | ||||
|                           trim($execution->getIncrementalErrorOutput()); | ||||
|                 $output = $execution->getIncrementalOutput(). | ||||
|                           $execution->getIncrementalErrorOutput(); | ||||
|  | ||||
|                 if (! empty($output)) { | ||||
|                     if ($key !== $keyOfLastExecutionWithOutput) { | ||||
|                         $this->info(PHP_EOL.'['.date('c').'] Execution #'.($key + 1).' output:'); | ||||
|  | ||||
|                         $keyOfLastExecutionWithOutput = $key; | ||||
|                     } | ||||
|  | ||||
|                     $this->output->writeln($output); | ||||
|                 } | ||||
|                 $this->output->write(ltrim($output, "\n")); | ||||
|  | ||||
|                 if (! $execution->isRunning()) { | ||||
|                     unset($executions[$key]); | ||||
|   | ||||
							
								
								
									
										152
									
								
								vendor/laravel/framework/src/Illuminate/Console/Signals.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								vendor/laravel/framework/src/Illuminate/Console/Signals.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console; | ||||
|  | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| class Signals | ||||
| { | ||||
|     /** | ||||
|      * The signal registry instance. | ||||
|      * | ||||
|      * @var \Symfony\Component\Console\SignalRegistry\SignalRegistry | ||||
|      */ | ||||
|     protected $registry; | ||||
|  | ||||
|     /** | ||||
|      * The signal registry's previous list of handlers. | ||||
|      * | ||||
|      * @var array<int, array<int, callable>>|null | ||||
|      */ | ||||
|     protected $previousHandlers; | ||||
|  | ||||
|     /** | ||||
|      * The current availability resolver, if any. | ||||
|      * | ||||
|      * @var (callable(): bool)|null | ||||
|      */ | ||||
|     protected static $availabilityResolver; | ||||
|  | ||||
|     /** | ||||
|      * Create a new signal registrar instance. | ||||
|      * | ||||
|      * @param  \Symfony\Component\Console\SignalRegistry\SignalRegistry  $registry | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($registry) | ||||
|     { | ||||
|         $this->registry = $registry; | ||||
|  | ||||
|         $this->previousHandlers = $this->getHandlers(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register a new signal handler. | ||||
|      * | ||||
|      * @param  int  $signal | ||||
|      * @param  callable(int $signal): void  $callback | ||||
|      * @return void | ||||
|      */ | ||||
|     public function register($signal, $callback) | ||||
|     { | ||||
|         $this->previousHandlers[$signal] ??= $this->initializeSignal($signal); | ||||
|  | ||||
|         with($this->getHandlers(), function ($handlers) use ($signal) { | ||||
|             $handlers[$signal] ??= $this->initializeSignal($signal); | ||||
|  | ||||
|             $this->setHandlers($handlers); | ||||
|         }); | ||||
|  | ||||
|         $this->registry->register($signal, $callback); | ||||
|  | ||||
|         with($this->getHandlers(), function ($handlers) use ($signal) { | ||||
|             $lastHandlerInserted = array_pop($handlers[$signal]); | ||||
|  | ||||
|             array_unshift($handlers[$signal], $lastHandlerInserted); | ||||
|  | ||||
|             $this->setHandlers($handlers); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the signal's existing handler in array format. | ||||
|      * | ||||
|      * @return array<int, callable(int $signal): void> | ||||
|      */ | ||||
|     protected function initializeSignal($signal) | ||||
|     { | ||||
|         return is_callable($existingHandler = pcntl_signal_get_handler($signal)) | ||||
|             ? [$existingHandler] | ||||
|             : null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Unregister the current signal handlers. | ||||
|      * | ||||
|      * @return array<int, array<int, callable(int $signal): void>> | ||||
|      */ | ||||
|     public function unregister() | ||||
|     { | ||||
|         $previousHandlers = $this->previousHandlers; | ||||
|  | ||||
|         foreach ($previousHandlers as $signal => $handler) { | ||||
|             if (is_null($handler)) { | ||||
|                 pcntl_signal($signal, SIG_DFL); | ||||
|  | ||||
|                 unset($previousHandlers[$signal]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $this->setHandlers($previousHandlers); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the given callback if "signals" should be used and are available. | ||||
|      * | ||||
|      * @param  callable  $callback | ||||
|      * @return void | ||||
|      */ | ||||
|     public static function whenAvailable($callback) | ||||
|     { | ||||
|         $resolver = static::$availabilityResolver; | ||||
|  | ||||
|         if ($resolver()) { | ||||
|             $callback(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the registry's handlers. | ||||
|      * | ||||
|      * @return array<int, array<int, callable>> | ||||
|      */ | ||||
|     protected function getHandlers() | ||||
|     { | ||||
|         return (fn () => $this->signalHandlers) | ||||
|             ->call($this->registry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the registry's handlers. | ||||
|      * | ||||
|      * @param  array<int, array<int, callable(int $signal):void>>  $handlers | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function setHandlers($handlers) | ||||
|     { | ||||
|         (fn () => $this->signalHandlers = $handlers) | ||||
|             ->call($this->registry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the availability resolver. | ||||
|      * | ||||
|      * @param  callable(): bool | ||||
|      * @return void | ||||
|      */ | ||||
|     public static function resolveAvailabilityUsing($resolver) | ||||
|     { | ||||
|         static::$availabilityResolver = $resolver; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Alert.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Alert.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class Alert extends Component | ||||
| { | ||||
|     /** | ||||
|      * Renders the component using the given arguments. | ||||
|      * | ||||
|      * @param  string  $string | ||||
|      * @param  int  $verbosity | ||||
|      * @return void | ||||
|      */ | ||||
|     public function render($string, $verbosity = OutputInterface::VERBOSITY_NORMAL) | ||||
|     { | ||||
|         $string = $this->mutate($string, [ | ||||
|             Mutators\EnsureDynamicContentIsHighlighted::class, | ||||
|             Mutators\EnsurePunctuation::class, | ||||
|             Mutators\EnsureRelativePaths::class, | ||||
|         ]); | ||||
|  | ||||
|         $this->renderView('alert', [ | ||||
|             'content' => $string, | ||||
|         ], $verbosity); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/BulletList.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/BulletList.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class BulletList extends Component | ||||
| { | ||||
|     /** | ||||
|      * Renders the component using the given arguments. | ||||
|      * | ||||
|      * @param  array<int, string>  $elements | ||||
|      * @param  int  $verbosity | ||||
|      * @return void | ||||
|      */ | ||||
|     public function render($elements, $verbosity = OutputInterface::VERBOSITY_NORMAL) | ||||
|     { | ||||
|         $elements = $this->mutate($elements, [ | ||||
|             Mutators\EnsureDynamicContentIsHighlighted::class, | ||||
|             Mutators\EnsureNoPunctuation::class, | ||||
|             Mutators\EnsureRelativePaths::class, | ||||
|         ]); | ||||
|  | ||||
|         $this->renderView('bullet-list', [ | ||||
|             'elements' => $elements, | ||||
|         ], $verbosity); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Choice.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Choice.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| use Symfony\Component\Console\Question\ChoiceQuestion; | ||||
|  | ||||
| class Choice extends Component | ||||
| { | ||||
|     /** | ||||
|      * Renders the component using the given arguments. | ||||
|      * | ||||
|      * @param  string  $question | ||||
|      * @param  array<array-key, string>  $choices | ||||
|      * @param  mixed  $default | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function render($question, $choices, $default = null) | ||||
|     { | ||||
|         return $this->usingQuestionHelper( | ||||
|             fn () => $this->output->askQuestion( | ||||
|                 new ChoiceQuestion($question, $choices, $default) | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										124
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Component.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Component.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| use Illuminate\Console\OutputStyle; | ||||
| use Illuminate\Console\QuestionHelper; | ||||
| use ReflectionClass; | ||||
| use Symfony\Component\Console\Helper\SymfonyQuestionHelper; | ||||
| use function Termwind\render; | ||||
| use function Termwind\renderUsing; | ||||
|  | ||||
| abstract class Component | ||||
| { | ||||
|     /** | ||||
|      * The output style implementation. | ||||
|      * | ||||
|      * @var \Illuminate\Console\OutputStyle | ||||
|      */ | ||||
|     protected $output; | ||||
|  | ||||
|     /** | ||||
|      * The list of mutators to apply on the view data. | ||||
|      * | ||||
|      * @var array<int, callable(string): string> | ||||
|      */ | ||||
|     protected $mutators; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new component instance. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\OutputStyle  $output | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($output) | ||||
|     { | ||||
|         $this->output = $output; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Renders the given view. | ||||
|      * | ||||
|      * @param  string  $view | ||||
|      * @param  \Illuminate\Contracts\Support\Arrayable|array  $data | ||||
|      * @param  int  $verbosity | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function renderView($view, $data, $verbosity) | ||||
|     { | ||||
|         renderUsing($this->output); | ||||
|  | ||||
|         render((string) $this->compile($view, $data), $verbosity); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Compile the given view contents. | ||||
|      * | ||||
|      * @param  string  $view | ||||
|      * @param  array  $data | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function compile($view, $data) | ||||
|     { | ||||
|         extract($data); | ||||
|  | ||||
|         ob_start(); | ||||
|  | ||||
|         include __DIR__."/../../resources/views/components/$view.php"; | ||||
|  | ||||
|         return tap(ob_get_contents(), function () { | ||||
|             ob_end_clean(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Mutates the given data with the given set of mutators. | ||||
|      * | ||||
|      * @param  array<int, string>|string  $data | ||||
|      * @param  array<int, callable(string): string>  $mutators | ||||
|      * @return array<int, string>|string | ||||
|      */ | ||||
|     protected function mutate($data, $mutators) | ||||
|     { | ||||
|         foreach ($mutators as $mutator) { | ||||
|             $mutator = new $mutator; | ||||
|  | ||||
|             if (is_iterable($data)) { | ||||
|                 foreach ($data as $key => $value) { | ||||
|                     $data[$key] = $mutator($value); | ||||
|                 } | ||||
|             } else { | ||||
|                 $data = $mutator($data); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Eventually performs a question using the component's question helper. | ||||
|      * | ||||
|      * @param  callable  $callable | ||||
|      * @return mixed | ||||
|      */ | ||||
|     protected function usingQuestionHelper($callable) | ||||
|     { | ||||
|         $property = with(new ReflectionClass(OutputStyle::class)) | ||||
|             ->getParentClass() | ||||
|             ->getProperty('questionHelper'); | ||||
|  | ||||
|         $property->setAccessible(true); | ||||
|  | ||||
|         $currentHelper = $property->isInitialized($this->output) | ||||
|             ? $property->getValue($this->output) | ||||
|             : new SymfonyQuestionHelper(); | ||||
|  | ||||
|         $property->setValue($this->output, new QuestionHelper); | ||||
|  | ||||
|         try { | ||||
|             return $callable(); | ||||
|         } finally { | ||||
|             $property->setValue($this->output, $currentHelper); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Confirm.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Confirm.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| class Confirm extends Component | ||||
| { | ||||
|     /** | ||||
|      * Renders the component using the given arguments. | ||||
|      * | ||||
|      * @param  string  $question | ||||
|      * @param  bool  $default | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function render($question, $default = false) | ||||
|     { | ||||
|         return $this->usingQuestionHelper( | ||||
|             fn () => $this->output->confirm($question, $default), | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Error.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Error.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class Error extends Component | ||||
| { | ||||
|     /** | ||||
|      * Renders the component using the given arguments. | ||||
|      * | ||||
|      * @param  string  $string | ||||
|      * @param  int  $verbosity | ||||
|      * @return void | ||||
|      */ | ||||
|     public function render($string, $verbosity = OutputInterface::VERBOSITY_NORMAL) | ||||
|     { | ||||
|         with(new Line($this->output))->render('error', $string, $verbosity); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										58
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Factory.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Factory.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| /** | ||||
|  * @method void alert(string $string, int $verbosity = \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL) | ||||
|  * @method void bulletList(array $elements, int $verbosity = \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL) | ||||
|  * @method mixed choice(string $question, array $choices, $default = null) | ||||
|  * @method bool confirm(string $question, bool $default = false) | ||||
|  * @method void error(string $string, int $verbosity = \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL) | ||||
|  * @method void info(string $string, int $verbosity = \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL) | ||||
|  * @method void line(string $style, string $string, int $verbosity = \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL) | ||||
|  * @method void task(string $description, ?callable $task = null, int $verbosity = \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL) | ||||
|  * @method void twoColumnDetail(string $first, ?string $second = null, int $verbosity = \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL) | ||||
|  * @method void warn(string $string, int $verbosity = \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL) | ||||
|  */ | ||||
| class Factory | ||||
| { | ||||
|     /** | ||||
|      * The output interface implementation. | ||||
|      * | ||||
|      * @var \Illuminate\Console\OutputStyle | ||||
|      */ | ||||
|     protected $output; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new factory instance. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\OutputStyle  $output | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($output) | ||||
|     { | ||||
|         $this->output = $output; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Dynamically handle calls into the component instance. | ||||
|      * | ||||
|      * @param  string  $method | ||||
|      * @param  array  $parameters | ||||
|      * @return mixed | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      */ | ||||
|     public function __call($method, $parameters) | ||||
|     { | ||||
|         $component = '\Illuminate\Console\View\Components\\'.ucfirst($method); | ||||
|  | ||||
|         throw_unless(class_exists($component), new InvalidArgumentException(sprintf( | ||||
|             'Console component [%s] not found.', $method | ||||
|         ))); | ||||
|  | ||||
|         return with(new $component($this->output))->render(...$parameters); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Info.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Info.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class Info extends Component | ||||
| { | ||||
|     /** | ||||
|      * Renders the component using the given arguments. | ||||
|      * | ||||
|      * @param  string  $string | ||||
|      * @param  int  $verbosity | ||||
|      * @return void | ||||
|      */ | ||||
|     public function render($string, $verbosity = OutputInterface::VERBOSITY_NORMAL) | ||||
|     { | ||||
|         with(new Line($this->output))->render('info', $string, $verbosity); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Line.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/laravel/framework/src/Illuminate/Console/View/Components/Line.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components; | ||||
|  | ||||
| use Illuminate\Console\Contracts\NewLineAware; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class Line extends Component | ||||
| { | ||||
|     /** | ||||
|      * The possible line styles. | ||||
|      * | ||||
|      * @var array<string, array<string, string>> | ||||
|      */ | ||||
|     protected static $styles = [ | ||||
|         'info' => [ | ||||
|             'bgColor' => 'blue', | ||||
|             'fgColor' => 'white', | ||||
|             'title' => 'info', | ||||
|         ], | ||||
|         'warn' => [ | ||||
|             'bgColor' => 'yellow', | ||||
|             'fgColor' => 'black', | ||||
|             'title' => 'warn', | ||||
|         ], | ||||
|         'error' => [ | ||||
|             'bgColor' => 'red', | ||||
|             'fgColor' => 'white', | ||||
|             'title' => 'error', | ||||
|         ], | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * Renders the component using the given arguments. | ||||
|      * | ||||
|      * @param  string  $style | ||||
|      * @param  string  $string | ||||
|      * @param  int  $verbosity | ||||
|      * @return void | ||||
|      */ | ||||
|     public function render($style, $string, $verbosity = OutputInterface::VERBOSITY_NORMAL) | ||||
|     { | ||||
|         $string = $this->mutate($string, [ | ||||
|             Mutators\EnsureDynamicContentIsHighlighted::class, | ||||
|             Mutators\EnsurePunctuation::class, | ||||
|             Mutators\EnsureRelativePaths::class, | ||||
|         ]); | ||||
|  | ||||
|         $this->renderView('line', array_merge(static::$styles[$style], [ | ||||
|             'marginTop' => ($this->output instanceof NewLineAware && $this->output->newLineWritten()) ? 0 : 1, | ||||
|             'content' => $string, | ||||
|         ]), $verbosity); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Illuminate\Console\View\Components\Mutators; | ||||
|  | ||||
| class EnsureDynamicContentIsHighlighted | ||||
| { | ||||
|     /** | ||||
|      * Highlight dynamic content within the given string. | ||||
|      * | ||||
|      * @param  string  $string | ||||
|      * @return string | ||||
|      */ | ||||
|     public function __invoke($string) | ||||
|     { | ||||
|         return preg_replace('/\[([^\]]+)\]/', '<options=bold>[$1]</>', (string) $string); | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 RafficMohammed
					RafficMohammed