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