dependencies-upgrade
This commit is contained in:
6
vendor/laravel/framework/README.md
vendored
6
vendor/laravel/framework/README.md
vendored
@@ -2,9 +2,9 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/d/total.svg" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/v/stable.svg" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/license.svg" alt="License"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## About Laravel
|
||||
|
97
vendor/laravel/framework/composer.json
vendored
97
vendor/laravel/framework/composer.json
vendored
@@ -15,41 +15,43 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5|^8.0",
|
||||
"php": "^7.3|^8.0",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"doctrine/inflector": "^1.4|^2.0",
|
||||
"dragonmantank/cron-expression": "^2.3.1",
|
||||
"dragonmantank/cron-expression": "^3.0.2",
|
||||
"egulias/email-validator": "^2.1.10",
|
||||
"league/commonmark": "^1.3",
|
||||
"laravel/serializable-closure": "^1.0",
|
||||
"league/commonmark": "^1.3|^2.0.2",
|
||||
"league/flysystem": "^1.1",
|
||||
"monolog/monolog": "^2.0",
|
||||
"nesbot/carbon": "^2.31",
|
||||
"nesbot/carbon": "^2.53.1",
|
||||
"opis/closure": "^3.6",
|
||||
"psr/container": "^1.0",
|
||||
"psr/log": "^1.0|^2.0",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"ramsey/uuid": "^3.7|^4.0",
|
||||
"swiftmailer/swiftmailer": "^6.0",
|
||||
"symfony/console": "^5.0",
|
||||
"symfony/error-handler": "^5.0",
|
||||
"symfony/finder": "^5.0",
|
||||
"symfony/http-foundation": "^5.0",
|
||||
"symfony/http-kernel": "^5.0",
|
||||
"symfony/mime": "^5.0",
|
||||
"symfony/polyfill-php73": "^1.17",
|
||||
"symfony/process": "^5.0",
|
||||
"symfony/routing": "^5.0",
|
||||
"symfony/var-dumper": "^5.0",
|
||||
"ramsey/uuid": "^4.2.2",
|
||||
"swiftmailer/swiftmailer": "^6.3",
|
||||
"symfony/console": "^5.4",
|
||||
"symfony/error-handler": "^5.4",
|
||||
"symfony/finder": "^5.4",
|
||||
"symfony/http-foundation": "^5.4",
|
||||
"symfony/http-kernel": "^5.4",
|
||||
"symfony/mime": "^5.4",
|
||||
"symfony/process": "^5.4",
|
||||
"symfony/routing": "^5.4",
|
||||
"symfony/var-dumper": "^5.4",
|
||||
"tijsverkoyen/css-to-inline-styles": "^2.2.2",
|
||||
"vlucas/phpdotenv": "^4.0",
|
||||
"voku/portable-ascii": "^1.4.8"
|
||||
"vlucas/phpdotenv": "^5.4.1",
|
||||
"voku/portable-ascii": "^1.6.1"
|
||||
},
|
||||
"replace": {
|
||||
"illuminate/auth": "self.version",
|
||||
"illuminate/broadcasting": "self.version",
|
||||
"illuminate/bus": "self.version",
|
||||
"illuminate/cache": "self.version",
|
||||
"illuminate/collections": "self.version",
|
||||
"illuminate/config": "self.version",
|
||||
"illuminate/console": "self.version",
|
||||
"illuminate/container": "self.version",
|
||||
@@ -62,6 +64,7 @@
|
||||
"illuminate/hashing": "self.version",
|
||||
"illuminate/http": "self.version",
|
||||
"illuminate/log": "self.version",
|
||||
"illuminate/macroable": "self.version",
|
||||
"illuminate/mail": "self.version",
|
||||
"illuminate/notifications": "self.version",
|
||||
"illuminate/pagination": "self.version",
|
||||
@@ -77,32 +80,35 @@
|
||||
"illuminate/view": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"aws/aws-sdk-php": "^3.155",
|
||||
"doctrine/dbal": "^2.6",
|
||||
"filp/whoops": "^2.8",
|
||||
"guzzlehttp/guzzle": "^6.3.1|^7.0.1",
|
||||
"aws/aws-sdk-php": "^3.198.1",
|
||||
"doctrine/dbal": "^2.13.3|^3.1.4",
|
||||
"filp/whoops": "^2.14.3",
|
||||
"guzzlehttp/guzzle": "^6.5.5|^7.0.1",
|
||||
"league/flysystem-cached-adapter": "^1.0",
|
||||
"mockery/mockery": "~1.3.3|^1.4.2",
|
||||
"moontoast/math": "^1.1",
|
||||
"orchestra/testbench-core": "^5.8",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"orchestra/testbench-core": "^6.27",
|
||||
"pda/pheanstalk": "^4.0",
|
||||
"phpunit/phpunit": "^8.4|^9.3.3",
|
||||
"predis/predis": "^1.1.1",
|
||||
"symfony/cache": "^5.0"
|
||||
"phpunit/phpunit": "^8.5.19|^9.5.8",
|
||||
"predis/predis": "^1.1.9",
|
||||
"symfony/cache": "^5.4"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "1.0"
|
||||
"psr/container-implementation": "1.0",
|
||||
"psr/simple-cache-implementation": "1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"tightenco/collect": "<5.5.33"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/Illuminate/Collections/helpers.php",
|
||||
"src/Illuminate/Events/functions.php",
|
||||
"src/Illuminate/Foundation/helpers.php",
|
||||
"src/Illuminate/Support/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Illuminate\\": "src/Illuminate/"
|
||||
"Illuminate\\": "src/Illuminate/",
|
||||
"Illuminate\\Support\\": ["src/Illuminate/Macroable/", "src/Illuminate/Collections/"]
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
@@ -115,40 +121,45 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.x-dev"
|
||||
"dev-master": "8.x-dev"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "Required to use the multiple_of validation rule.",
|
||||
"ext-ftp": "Required to use the Flysystem FTP driver.",
|
||||
"ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
|
||||
"ext-memcached": "Required to use the memcache cache driver.",
|
||||
"ext-pcntl": "Required to use all features of the queue worker.",
|
||||
"ext-posix": "Required to use all features of the queue worker.",
|
||||
"ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).",
|
||||
"aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.155).",
|
||||
"doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).",
|
||||
"filp/whoops": "Required for friendly error pages in development (^2.8).",
|
||||
"ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
|
||||
"aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.198.1).",
|
||||
"brianium/paratest": "Required to run tests in parallel (^6.0).",
|
||||
"doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).",
|
||||
"filp/whoops": "Required for friendly error pages in development (^2.14.3).",
|
||||
"fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
|
||||
"guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.3.1|^7.0.1).",
|
||||
"guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).",
|
||||
"laravel/tinker": "Required to use the tinker console command (^2.0).",
|
||||
"league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).",
|
||||
"league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).",
|
||||
"league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
|
||||
"mockery/mockery": "Required to use mocking (~1.3.3|^1.4.2).",
|
||||
"moontoast/math": "Required to use ordered UUIDs (^1.1).",
|
||||
"mockery/mockery": "Required to use mocking (^1.4.4).",
|
||||
"nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
|
||||
"pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
|
||||
"phpunit/phpunit": "Required to use assertions and run tests (^8.4|^9.3.3).",
|
||||
"predis/predis": "Required to use the predis connector (^1.1.2).",
|
||||
"phpunit/phpunit": "Required to use assertions and run tests (^8.5.19|^9.5.8).",
|
||||
"predis/predis": "Required to use the predis connector (^1.1.9).",
|
||||
"psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
|
||||
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0).",
|
||||
"symfony/cache": "Required to PSR-6 cache bridge (^5.0).",
|
||||
"symfony/filesystem": "Required to create relative storage directory symbolic links (^5.0).",
|
||||
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0|^7.0).",
|
||||
"symfony/cache": "Required to PSR-6 cache bridge (^5.4).",
|
||||
"symfony/filesystem": "Required to enable support for relative symbolic links (^5.4).",
|
||||
"symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).",
|
||||
"wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)."
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
|
51
vendor/laravel/framework/src/Illuminate/Auth/Access/Events/GateEvaluated.php
vendored
Normal file
51
vendor/laravel/framework/src/Illuminate/Auth/Access/Events/GateEvaluated.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Auth\Access\Events;
|
||||
|
||||
class GateEvaluated
|
||||
{
|
||||
/**
|
||||
* The authenticatable model.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Auth\Authenticatable|null
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* The ability being evaluated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $ability;
|
||||
|
||||
/**
|
||||
* The result of the evaluation.
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
public $result;
|
||||
|
||||
/**
|
||||
* The arguments given during evaluation.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $arguments;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
|
||||
* @param string $ability
|
||||
* @param bool|null $result
|
||||
* @param array $arguments
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($user, $ability, $result, $arguments)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->ability = $ability;
|
||||
$this->result = $result;
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
}
|
@@ -2,10 +2,13 @@
|
||||
|
||||
namespace Illuminate\Auth\Access;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
@@ -115,6 +118,64 @@ class Gate implements GateContract
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an on-demand authorization check. Throw an authorization exception if the condition or callback is false.
|
||||
*
|
||||
* @param \Illuminate\Auth\Access\Response|\Closure|bool $condition
|
||||
* @param string|null $message
|
||||
* @param string|null $code
|
||||
* @return \Illuminate\Auth\Access\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function allowIf($condition, $message = null, $code = null)
|
||||
{
|
||||
return $this->authorizeOnDemand($condition, $message, $code, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an on-demand authorization check. Throw an authorization exception if the condition or callback is true.
|
||||
*
|
||||
* @param \Illuminate\Auth\Access\Response|\Closure|bool $condition
|
||||
* @param string|null $message
|
||||
* @param string|null $code
|
||||
* @return \Illuminate\Auth\Access\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function denyIf($condition, $message = null, $code = null)
|
||||
{
|
||||
return $this->authorizeOnDemand($condition, $message, $code, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize a given condition or callback.
|
||||
*
|
||||
* @param \Illuminate\Auth\Access\Response|\Closure|bool $condition
|
||||
* @param string|null $message
|
||||
* @param string|null $code
|
||||
* @param bool $allowWhenResponseIs
|
||||
* @return \Illuminate\Auth\Access\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
protected function authorizeOnDemand($condition, $message, $code, $allowWhenResponseIs)
|
||||
{
|
||||
$user = $this->resolveUser();
|
||||
|
||||
if ($condition instanceof Closure) {
|
||||
$response = $this->canBeCalledWithUser($user, $condition)
|
||||
? $condition($user)
|
||||
: new Response(false, $message, $code);
|
||||
} else {
|
||||
$response = $condition;
|
||||
}
|
||||
|
||||
return with($response instanceof Response ? $response : new Response(
|
||||
(bool) $response === $allowWhenResponseIs, $message, $code
|
||||
))->authorize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a new ability.
|
||||
*
|
||||
@@ -155,10 +216,10 @@ class Gate implements GateContract
|
||||
{
|
||||
$abilities = $abilities ?: [
|
||||
'viewAny' => 'viewAny',
|
||||
'view' => 'view',
|
||||
'create' => 'create',
|
||||
'update' => 'update',
|
||||
'delete' => 'delete',
|
||||
'view' => 'view',
|
||||
'create' => 'create',
|
||||
'update' => 'update',
|
||||
'delete' => 'delete',
|
||||
];
|
||||
|
||||
foreach ($abilities as $ability => $method) {
|
||||
@@ -373,9 +434,11 @@ class Gate implements GateContract
|
||||
// After calling the authorization callback, we will call the "after" callbacks
|
||||
// that are registered with the Gate, which allows a developer to do logging
|
||||
// if that is required for this application. Then we'll return the result.
|
||||
return $this->callAfterCallbacks(
|
||||
return tap($this->callAfterCallbacks(
|
||||
$user, $ability, $arguments, $result
|
||||
);
|
||||
), function ($result) use ($user, $ability, $arguments) {
|
||||
$this->dispatchGateEvaluatedEvent($user, $ability, $arguments, $result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,6 +581,24 @@ class Gate implements GateContract
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a gate evaluation event.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
|
||||
* @param string $ability
|
||||
* @param array $arguments
|
||||
* @param bool|null $result
|
||||
* @return void
|
||||
*/
|
||||
protected function dispatchGateEvaluatedEvent($user, $ability, array $arguments, $result)
|
||||
{
|
||||
if ($this->container->bound(Dispatcher::class)) {
|
||||
$this->container->make(Dispatcher::class)->dispatch(
|
||||
new Events\GateEvaluated($user, $ability, $result, $arguments)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the callable for the given ability and arguments.
|
||||
*
|
||||
@@ -599,7 +680,15 @@ class Gate implements GateContract
|
||||
|
||||
$classDirname = str_replace('/', '\\', dirname(str_replace('\\', '/', $class)));
|
||||
|
||||
return [$classDirname.'\\Policies\\'.class_basename($class).'Policy'];
|
||||
$classDirnameSegments = explode('\\', $classDirname);
|
||||
|
||||
return Arr::wrap(Collection::times(count($classDirnameSegments), function ($index) use ($class, $classDirnameSegments) {
|
||||
$classDirname = implode('\\', array_slice($classDirnameSegments, 0, $index));
|
||||
|
||||
return $classDirname.'\\Policies\\'.class_basename($class).'Policy';
|
||||
})->reverse()->values()->first(function ($class) {
|
||||
return class_exists($class);
|
||||
}) ?: [$classDirname.'\\Policies\\'.class_basename($class).'Policy']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -770,4 +859,17 @@ class Gate implements GateContract
|
||||
{
|
||||
return $this->policies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the container instance used by the gate.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Container\Container $container
|
||||
* @return $this
|
||||
*/
|
||||
public function setContainer(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -122,7 +122,11 @@ class AuthManager implements FactoryContract
|
||||
{
|
||||
$provider = $this->createUserProvider($config['provider'] ?? null);
|
||||
|
||||
$guard = new SessionGuard($name, $provider, $this->app['session.store']);
|
||||
$guard = new SessionGuard(
|
||||
$name,
|
||||
$provider,
|
||||
$this->app['session.store'],
|
||||
);
|
||||
|
||||
// When using the remember me functionality of the authentication services we
|
||||
// will need to be set the encryption instance of the guard, which allows
|
||||
@@ -139,6 +143,10 @@ class AuthManager implements FactoryContract
|
||||
$guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
|
||||
}
|
||||
|
||||
if (isset($config['remember'])) {
|
||||
$guard->setRememberDuration($config['remember']);
|
||||
}
|
||||
|
||||
return $guard;
|
||||
}
|
||||
|
||||
@@ -295,6 +303,31 @@ class AuthManager implements FactoryContract
|
||||
return count($this->guards) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget all of the resolved guard instances.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function forgetGuards()
|
||||
{
|
||||
$this->guards = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the application instance used by the manager.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return $this
|
||||
*/
|
||||
public function setApplication($app)
|
||||
{
|
||||
$this->app = $app;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically call the default driver instance.
|
||||
*
|
||||
|
@@ -35,11 +35,6 @@ class AuthServiceProvider extends ServiceProvider
|
||||
protected function registerAuthenticator()
|
||||
{
|
||||
$this->app->singleton('auth', function ($app) {
|
||||
// Once the authentication service has actually been requested by the developer
|
||||
// we will set a variable in the application indicating such. This helps us
|
||||
// know that we need to set any queued cookies in the after event later.
|
||||
$app['auth.loaded'] = true;
|
||||
|
||||
return new AuthManager($app);
|
||||
});
|
||||
|
||||
@@ -55,11 +50,9 @@ 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, function ($app) {
|
||||
return call_user_func($app['auth']->userResolver());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,15 +76,13 @@ class AuthServiceProvider extends ServiceProvider
|
||||
*/
|
||||
protected function registerRequirePassword()
|
||||
{
|
||||
$this->app->bind(
|
||||
RequirePassword::class, function ($app) {
|
||||
return new RequirePassword(
|
||||
$app[ResponseFactory::class],
|
||||
$app[UrlGenerator::class],
|
||||
$app['config']->get('auth.password_timeout')
|
||||
);
|
||||
}
|
||||
);
|
||||
$this->app->bind(RequirePassword::class, function ($app) {
|
||||
return new RequirePassword(
|
||||
$app[ResponseFactory::class],
|
||||
$app[UrlGenerator::class],
|
||||
$app['config']->get('auth.password_timeout')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,11 +107,8 @@ class AuthServiceProvider extends ServiceProvider
|
||||
protected function registerEventRebindHandler()
|
||||
{
|
||||
$this->app->rebinding('events', function ($app, $dispatcher) {
|
||||
if (! $app->resolved('auth')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($app['auth']->hasResolvedGuards() === false) {
|
||||
if (! $app->resolved('auth') ||
|
||||
$app['auth']->hasResolvedGuards() === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -31,6 +31,16 @@ trait Authenticatable
|
||||
return $this->{$this->getAuthIdentifierName()};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique broadcast identifier for the user.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAuthIdentifierForBroadcasting()
|
||||
{
|
||||
return $this->getAuthIdentifier();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the password for the user.
|
||||
*
|
||||
|
@@ -16,7 +16,7 @@ class AuthenticationException extends Exception
|
||||
/**
|
||||
* The path the user should be redirected to.
|
||||
*
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
protected $redirectTo;
|
||||
|
||||
@@ -49,7 +49,7 @@ class AuthenticationException extends Exception
|
||||
/**
|
||||
* Get the path the user should be redirected to.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function redirectTo()
|
||||
{
|
||||
|
@@ -13,8 +13,8 @@
|
||||
<script src="{{ asset('js/app.js') }}" defer></script>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="dns-prefetch" href="//fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
|
||||
<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">
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Illuminate\Auth;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
|
||||
use Illuminate\Contracts\Auth\UserProvider;
|
||||
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
|
||||
@@ -117,6 +118,8 @@ class DatabaseUserProvider implements UserProvider
|
||||
|
||||
if (is_array($value) || $value instanceof Arrayable) {
|
||||
$query->whereIn($key, $value);
|
||||
} elseif ($value instanceof Closure) {
|
||||
$value($query);
|
||||
} else {
|
||||
$query->where($key, $value);
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Illuminate\Auth;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
|
||||
use Illuminate\Contracts\Auth\UserProvider;
|
||||
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
|
||||
@@ -123,6 +124,8 @@ class EloquentUserProvider implements UserProvider
|
||||
|
||||
if (is_array($value) || $value instanceof Arrayable) {
|
||||
$query->whereIn($key, $value);
|
||||
} elseif ($value instanceof Closure) {
|
||||
$value($query);
|
||||
} else {
|
||||
$query->where($key, $value);
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ trait GuardHelpers
|
||||
protected $provider;
|
||||
|
||||
/**
|
||||
* Determine if current user is authenticated. If not, throw an exception.
|
||||
* Determine if the current user is authenticated. If not, throw an exception.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Auth\Authenticatable
|
||||
*
|
||||
|
@@ -5,6 +5,7 @@ namespace Illuminate\Auth\Middleware;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class EnsureEmailIsVerified
|
||||
{
|
||||
@@ -14,7 +15,7 @@ class EnsureEmailIsVerified
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $redirectToRoute
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse|null
|
||||
*/
|
||||
public function handle($request, Closure $next, $redirectToRoute = null)
|
||||
{
|
||||
@@ -23,7 +24,7 @@ class EnsureEmailIsVerified
|
||||
! $request->user()->hasVerifiedEmail())) {
|
||||
return $request->expectsJson()
|
||||
? abort(403, 'Your email address is not verified.')
|
||||
: Redirect::route($redirectToRoute ?: 'verification.notice');
|
||||
: Redirect::guest(URL::route($redirectToRoute ?: 'verification.notice'));
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
|
@@ -63,15 +63,17 @@ class ResetPassword extends Notification
|
||||
return call_user_func(static::$toMailCallback, $notifiable, $this->token);
|
||||
}
|
||||
|
||||
if (static::$createUrlCallback) {
|
||||
$url = call_user_func(static::$createUrlCallback, $notifiable, $this->token);
|
||||
} else {
|
||||
$url = url(route('password.reset', [
|
||||
'token' => $this->token,
|
||||
'email' => $notifiable->getEmailForPasswordReset(),
|
||||
], false));
|
||||
}
|
||||
return $this->buildMailMessage($this->resetUrl($notifiable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reset password notification mail message for the given URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
protected function buildMailMessage($url)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject(Lang::get('Reset Password Notification'))
|
||||
->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
|
||||
@@ -80,6 +82,24 @@ class ResetPassword extends Notification
|
||||
->line(Lang::get('If you did not request a password reset, no further action is required.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reset URL for the given notifiable.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return string
|
||||
*/
|
||||
protected function resetUrl($notifiable)
|
||||
{
|
||||
if (static::$createUrlCallback) {
|
||||
return call_user_func(static::$createUrlCallback, $notifiable, $this->token);
|
||||
}
|
||||
|
||||
return url(route('password.reset', [
|
||||
'token' => $this->token,
|
||||
'email' => $notifiable->getEmailForPasswordReset(),
|
||||
], false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when creating the reset password button URL.
|
||||
*
|
||||
|
@@ -11,6 +11,13 @@ use Illuminate\Support\Facades\URL;
|
||||
|
||||
class VerifyEmail extends Notification
|
||||
{
|
||||
/**
|
||||
* The callback that should be used to create the verify email URL.
|
||||
*
|
||||
* @var \Closure|null
|
||||
*/
|
||||
public static $createUrlCallback;
|
||||
|
||||
/**
|
||||
* The callback that should be used to build the mail message.
|
||||
*
|
||||
@@ -43,10 +50,21 @@ class VerifyEmail extends Notification
|
||||
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
|
||||
}
|
||||
|
||||
return $this->buildMailMessage($verificationUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the verify email notification mail message for the given URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
protected function buildMailMessage($url)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject(Lang::get('Verify Email Address'))
|
||||
->line(Lang::get('Please click the button below to verify your email address.'))
|
||||
->action(Lang::get('Verify Email Address'), $verificationUrl)
|
||||
->action(Lang::get('Verify Email Address'), $url)
|
||||
->line(Lang::get('If you did not create an account, no further action is required.'));
|
||||
}
|
||||
|
||||
@@ -58,6 +76,10 @@ class VerifyEmail extends Notification
|
||||
*/
|
||||
protected function verificationUrl($notifiable)
|
||||
{
|
||||
if (static::$createUrlCallback) {
|
||||
return call_user_func(static::$createUrlCallback, $notifiable);
|
||||
}
|
||||
|
||||
return URL::temporarySignedRoute(
|
||||
'verification.verify',
|
||||
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
|
||||
@@ -68,6 +90,17 @@ class VerifyEmail extends Notification
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when creating the email verification URL.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function createUrlUsing($callback)
|
||||
{
|
||||
static::$createUrlCallback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when building the notification mail message.
|
||||
*
|
||||
|
@@ -42,9 +42,10 @@ class PasswordBroker implements PasswordBrokerContract
|
||||
* Send a password reset link to a user.
|
||||
*
|
||||
* @param array $credentials
|
||||
* @param \Closure|null $callback
|
||||
* @return string
|
||||
*/
|
||||
public function sendResetLink(array $credentials)
|
||||
public function sendResetLink(array $credentials, Closure $callback = null)
|
||||
{
|
||||
// First we will check to see if we found a user at the given credentials and
|
||||
// if we did not we will redirect back to this current URI with a piece of
|
||||
@@ -59,12 +60,16 @@ class PasswordBroker implements PasswordBrokerContract
|
||||
return static::RESET_THROTTLED;
|
||||
}
|
||||
|
||||
// Once we have the reset token, we are ready to send the message out to this
|
||||
// user with a link to reset their password. We will then redirect back to
|
||||
// the current URI having nothing set in the session to indicate errors.
|
||||
$user->sendPasswordResetNotification(
|
||||
$this->tokens->create($user)
|
||||
);
|
||||
$token = $this->tokens->create($user);
|
||||
|
||||
if ($callback) {
|
||||
$callback($user, $token);
|
||||
} else {
|
||||
// Once we have the reset token, we are ready to send the message out to this
|
||||
// user with a link to reset their password. We will then redirect back to
|
||||
// the current URI having nothing set in the session to indicate errors.
|
||||
$user->sendPasswordResetNotification($token);
|
||||
}
|
||||
|
||||
return static::RESET_LINK_SENT;
|
||||
}
|
||||
|
@@ -17,9 +17,12 @@ use Illuminate\Contracts\Auth\UserProvider;
|
||||
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Session\Session;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Timebox;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
@@ -29,7 +32,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
use GuardHelpers, Macroable;
|
||||
|
||||
/**
|
||||
* The name of the Guard. Typically "session".
|
||||
* The name of the guard. Typically "web".
|
||||
*
|
||||
* Corresponds to guard name in authentication configuration.
|
||||
*
|
||||
@@ -51,6 +54,13 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
*/
|
||||
protected $viaRemember = false;
|
||||
|
||||
/**
|
||||
* The number of minutes that the "remember me" cookie should be valid for.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $rememberDuration = 2628000;
|
||||
|
||||
/**
|
||||
* The session used by the guard.
|
||||
*
|
||||
@@ -79,6 +89,13 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* The timebox instance.
|
||||
*
|
||||
* @var \Illuminate\Support\Timebox
|
||||
*/
|
||||
protected $timebox;
|
||||
|
||||
/**
|
||||
* Indicates if the logout method has been called.
|
||||
*
|
||||
@@ -100,17 +117,20 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
* @param \Illuminate\Contracts\Auth\UserProvider $provider
|
||||
* @param \Illuminate\Contracts\Session\Session $session
|
||||
* @param \Symfony\Component\HttpFoundation\Request|null $request
|
||||
* @param \Illuminate\Support\Timebox|null $timebox
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($name,
|
||||
UserProvider $provider,
|
||||
Session $session,
|
||||
Request $request = null)
|
||||
Request $request = null,
|
||||
Timebox $timebox = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->session = $session;
|
||||
$this->request = $request;
|
||||
$this->provider = $provider;
|
||||
$this->timebox = $timebox ?: new Timebox;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,7 +340,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the credential array for a HTTP Basic request.
|
||||
* Get the credential array for an HTTP Basic request.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param string $field
|
||||
@@ -373,6 +393,34 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to authenticate a user with credentials and additional callbacks.
|
||||
*
|
||||
* @param array $credentials
|
||||
* @param array|callable $callbacks
|
||||
* @param false $remember
|
||||
* @return bool
|
||||
*/
|
||||
public function attemptWhen(array $credentials = [], $callbacks = null, $remember = false)
|
||||
{
|
||||
$this->fireAttemptEvent($credentials, $remember);
|
||||
|
||||
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
|
||||
|
||||
// This method does the exact same thing as attempt, but also executes callbacks after
|
||||
// the user is retrieved and validated. If one of the callbacks returns falsy we do
|
||||
// not login the user. Instead, we will fail the specific authentication attempt.
|
||||
if ($this->hasValidCredentials($user, $credentials) && $this->shouldLogin($callbacks, $user)) {
|
||||
$this->login($user, $remember);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->fireFailedEvent($user, $credentials);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user matches the credentials.
|
||||
*
|
||||
@@ -382,13 +430,35 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
*/
|
||||
protected function hasValidCredentials($user, $credentials)
|
||||
{
|
||||
$validated = ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
|
||||
return $this->timebox->call(function ($timebox) use ($user, $credentials) {
|
||||
$validated = ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
|
||||
|
||||
if ($validated) {
|
||||
$this->fireValidatedEvent($user);
|
||||
if ($validated) {
|
||||
$timebox->returnEarly();
|
||||
|
||||
$this->fireValidatedEvent($user);
|
||||
}
|
||||
|
||||
return $validated;
|
||||
}, 200 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user should login by executing the given callbacks.
|
||||
*
|
||||
* @param array|callable|null $callbacks
|
||||
* @param \Illuminate\Contracts\Auth\Authenticatable $user
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldLogin($callbacks, AuthenticatableContract $user)
|
||||
{
|
||||
foreach (Arr::wrap($callbacks) as $callback) {
|
||||
if (! $callback($user, $this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $validated;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,7 +554,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
*/
|
||||
protected function createRecaller($value)
|
||||
{
|
||||
return $this->getCookieJar()->forever($this->getRecallerName(), $value);
|
||||
return $this->getCookieJar()->make($this->getRecallerName(), $value, $this->getRememberDuration());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,6 +587,34 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
$this->loggedOut = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the user out of the application on their current device only.
|
||||
*
|
||||
* This method does not cycle the "remember" token.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function logoutCurrentDevice()
|
||||
{
|
||||
$user = $this->user();
|
||||
|
||||
$this->clearUserDataFromStorage();
|
||||
|
||||
// If we have an event dispatcher instance, we can fire off the logout event
|
||||
// so any further processing can be done. This allows the developer to be
|
||||
// listening for anytime a user signs out of this application manually.
|
||||
if (isset($this->events)) {
|
||||
$this->events->dispatch(new CurrentDeviceLogout($this->name, $user));
|
||||
}
|
||||
|
||||
// Once we have fired the logout event we will clear the users out of memory
|
||||
// so they are no longer available as the user is no longer considered as
|
||||
// being signed into this application and should not be available here.
|
||||
$this->user = null;
|
||||
|
||||
$this->loggedOut = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the user data from the session and cookies.
|
||||
*
|
||||
@@ -545,32 +643,6 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
$this->provider->updateRememberToken($user, $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the user out of the application on their current device only.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function logoutCurrentDevice()
|
||||
{
|
||||
$user = $this->user();
|
||||
|
||||
$this->clearUserDataFromStorage();
|
||||
|
||||
// If we have an event dispatcher instance, we can fire off the logout event
|
||||
// so any further processing can be done. This allows the developer to be
|
||||
// listening for anytime a user signs out of this application manually.
|
||||
if (isset($this->events)) {
|
||||
$this->events->dispatch(new CurrentDeviceLogout($this->name, $user));
|
||||
}
|
||||
|
||||
// Once we have fired the logout event we will clear the users out of memory
|
||||
// so they are no longer available as the user is no longer considered as
|
||||
// being signed into this application and should not be available here.
|
||||
$this->user = null;
|
||||
|
||||
$this->loggedOut = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate other sessions for the current user.
|
||||
*
|
||||
@@ -578,7 +650,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $attribute
|
||||
* @return bool|null
|
||||
* @return \Illuminate\Contracts\Auth\Authenticatable|null
|
||||
*
|
||||
* @throws \Illuminate\Auth\AuthenticationException
|
||||
*/
|
||||
public function logoutOtherDevices($password, $attribute = 'password')
|
||||
{
|
||||
@@ -586,9 +660,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
return;
|
||||
}
|
||||
|
||||
$result = tap($this->user()->forceFill([
|
||||
$attribute => Hash::make($password),
|
||||
]))->save();
|
||||
$result = $this->rehashUserPassword($password, $attribute);
|
||||
|
||||
if ($this->recaller() ||
|
||||
$this->getCookieJar()->hasQueued($this->getRecallerName())) {
|
||||
@@ -600,6 +672,26 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rehash the current user's password.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $attribute
|
||||
* @return \Illuminate\Contracts\Auth\Authenticatable|null
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function rehashUserPassword($password, $attribute)
|
||||
{
|
||||
if (! Hash::check($password, $this->user()->{$attribute})) {
|
||||
throw new InvalidArgumentException('The given password does not match the current password.');
|
||||
}
|
||||
|
||||
return tap($this->user()->forceFill([
|
||||
$attribute => Hash::make($password),
|
||||
]))->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an authentication attempt event listener.
|
||||
*
|
||||
@@ -746,6 +838,29 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
return $this->viaRemember;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of minutes the remember me cookie should be valid for.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getRememberDuration()
|
||||
{
|
||||
return $this->rememberDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of minutes the remember me cookie should be valid for.
|
||||
*
|
||||
* @param int $minutes
|
||||
* @return $this
|
||||
*/
|
||||
public function setRememberDuration($minutes)
|
||||
{
|
||||
$this->rememberDuration = $minutes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cookie creator instance used by the guard.
|
||||
*
|
||||
@@ -853,4 +968,14 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timebox instance used by the guard.
|
||||
*
|
||||
* @return \Illuminate\Support\Timebox
|
||||
*/
|
||||
public function getTimebox()
|
||||
{
|
||||
return $this->timebox;
|
||||
}
|
||||
}
|
||||
|
@@ -14,11 +14,13 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5|^8.0",
|
||||
"illuminate/contracts": "^7.0",
|
||||
"illuminate/http": "^7.0",
|
||||
"illuminate/queue": "^7.0",
|
||||
"illuminate/support": "^7.0"
|
||||
"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"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -27,13 +29,13 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.x-dev"
|
||||
"dev-master": "8.x-dev"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"illuminate/console": "Required to use the auth:clear-resets command (^7.0).",
|
||||
"illuminate/queue": "Required to fire login / logout events (^7.0).",
|
||||
"illuminate/session": "Required to use the session based guard (^7.0)."
|
||||
"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)."
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
|
@@ -3,7 +3,7 @@
|
||||
namespace Illuminate\Broadcasting;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Broadcasting\Broadcaster;
|
||||
use Illuminate\Contracts\Broadcasting\Factory as BroadcastingFactory;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Support\Arr;
|
||||
@@ -46,23 +46,37 @@ 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->afterCommit = property_exists($event, 'afterCommit') ? $event->afterCommit : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the queued job.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Broadcasting\Broadcaster $broadcaster
|
||||
* @param \Illuminate\Contracts\Broadcasting\Factory $manager
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Broadcaster $broadcaster)
|
||||
public function handle(BroadcastingFactory $manager)
|
||||
{
|
||||
$name = method_exists($this->event, 'broadcastAs')
|
||||
? $this->event->broadcastAs() : get_class($this->event);
|
||||
|
||||
$broadcaster->broadcast(
|
||||
Arr::wrap($this->event->broadcastOn()), $name,
|
||||
$this->getPayloadFromEvent($this->event)
|
||||
);
|
||||
$channels = Arr::wrap($this->event->broadcastOn());
|
||||
|
||||
if (empty($channels)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$connections = method_exists($this->event, 'broadcastConnections')
|
||||
? $this->event->broadcastConnections()
|
||||
: [null];
|
||||
|
||||
$payload = $this->getPayloadFromEvent($this->event);
|
||||
|
||||
foreach ($connections as $connection) {
|
||||
$manager->connection($connection)->broadcast(
|
||||
$channels, $name, $payload
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,10 +87,9 @@ class BroadcastEvent implements ShouldQueue
|
||||
*/
|
||||
protected function getPayloadFromEvent($event)
|
||||
{
|
||||
if (method_exists($event, 'broadcastWith')) {
|
||||
return array_merge(
|
||||
$event->broadcastWith(), ['socket' => data_get($event, 'socket')]
|
||||
);
|
||||
if (method_exists($event, 'broadcastWith') &&
|
||||
! is_null($payload = $event->broadcastWith())) {
|
||||
return array_merge($payload, ['socket' => data_get($event, 'socket')]);
|
||||
}
|
||||
|
||||
$payload = [];
|
||||
|
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace Illuminate\Broadcasting;
|
||||
|
||||
use Ably\AblyRest;
|
||||
use Closure;
|
||||
use Illuminate\Broadcasting\Broadcasters\AblyBroadcaster;
|
||||
use Illuminate\Broadcasting\Broadcasters\LogBroadcaster;
|
||||
use Illuminate\Broadcasting\Broadcasters\NullBroadcaster;
|
||||
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
|
||||
@@ -70,7 +72,7 @@ class BroadcastManager implements FactoryContract
|
||||
$router->match(
|
||||
['get', 'post'], '/broadcasting/auth',
|
||||
'\\'.BroadcastController::class.'@authenticate'
|
||||
);
|
||||
)->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -110,7 +112,10 @@ class BroadcastManager implements FactoryContract
|
||||
*/
|
||||
public function queue($event)
|
||||
{
|
||||
if ($event instanceof ShouldBroadcastNow) {
|
||||
if ($event instanceof ShouldBroadcastNow ||
|
||||
(is_object($event) &&
|
||||
method_exists($event, 'shouldBroadcastNow') &&
|
||||
$event->shouldBroadcastNow())) {
|
||||
return $this->app->make(BusDispatcherContract::class)->dispatchNow(new BroadcastEvent(clone $event));
|
||||
}
|
||||
|
||||
@@ -220,6 +225,17 @@ class BroadcastManager implements FactoryContract
|
||||
return new PusherBroadcaster($pusher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the driver.
|
||||
*
|
||||
* @param array $config
|
||||
* @return \Illuminate\Contracts\Broadcasting\Broadcaster
|
||||
*/
|
||||
protected function createAblyDriver(array $config)
|
||||
{
|
||||
return new AblyBroadcaster(new AblyRest($config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the driver.
|
||||
*
|
||||
@@ -294,6 +310,19 @@ class BroadcastManager implements FactoryContract
|
||||
$this->app['config']['broadcasting.default'] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect the given disk and remove from local cache.
|
||||
*
|
||||
* @param string|null $name
|
||||
* @return void
|
||||
*/
|
||||
public function purge($name = null)
|
||||
{
|
||||
$name = $name ?? $this->getDefaultDriver();
|
||||
|
||||
unset($this->drivers[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom driver creator Closure.
|
||||
*
|
||||
@@ -308,6 +337,41 @@ class BroadcastManager implements FactoryContract
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application instance used by the manager.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Foundation\Application
|
||||
*/
|
||||
public function getApplication()
|
||||
{
|
||||
return $this->app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the application instance used by the manager.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return $this
|
||||
*/
|
||||
public function setApplication($app)
|
||||
{
|
||||
$this->app = $app;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget all of the resolved driver instances.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function forgetDrivers()
|
||||
{
|
||||
$this->drivers = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically call the default driver instance.
|
||||
*
|
||||
|
225
vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/AblyBroadcaster.php
vendored
Normal file
225
vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/AblyBroadcaster.php
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Broadcasting\Broadcasters;
|
||||
|
||||
use Ably\AblyRest;
|
||||
use Ably\Models\Message as AblyMessage;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
/**
|
||||
* @author Matthew Hall (matthall28@gmail.com)
|
||||
* @author Taylor Otwell (taylor@laravel.com)
|
||||
*/
|
||||
class AblyBroadcaster extends Broadcaster
|
||||
{
|
||||
/**
|
||||
* The AblyRest SDK instance.
|
||||
*
|
||||
* @var \Ably\AblyRest
|
||||
*/
|
||||
protected $ably;
|
||||
|
||||
/**
|
||||
* Create a new broadcaster instance.
|
||||
*
|
||||
* @param \Ably\AblyRest $ably
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AblyRest $ably)
|
||||
{
|
||||
$this->ably = $ably;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the incoming request for a given channel.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
*/
|
||||
public function auth($request)
|
||||
{
|
||||
$channelName = $this->normalizeChannelName($request->channel_name);
|
||||
|
||||
if (empty($request->channel_name) ||
|
||||
($this->isGuardedChannel($request->channel_name) &&
|
||||
! $this->retrieveUser($request, $channelName))) {
|
||||
throw new AccessDeniedHttpException;
|
||||
}
|
||||
|
||||
return parent::verifyUserCanAccessChannel(
|
||||
$request, $channelName
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the valid authentication response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $result
|
||||
* @return mixed
|
||||
*/
|
||||
public function validAuthenticationResponse($request, $result)
|
||||
{
|
||||
if (Str::startsWith($request->channel_name, 'private')) {
|
||||
$signature = $this->generateAblySignature(
|
||||
$request->channel_name, $request->socket_id
|
||||
);
|
||||
|
||||
return ['auth' => $this->getPublicToken().':'.$signature];
|
||||
}
|
||||
|
||||
$channelName = $this->normalizeChannelName($request->channel_name);
|
||||
|
||||
$user = $this->retrieveUser($request, $channelName);
|
||||
|
||||
$broadcastIdentifier = method_exists($user, 'getAuthIdentifierForBroadcasting')
|
||||
? $user->getAuthIdentifierForBroadcasting()
|
||||
: $user->getAuthIdentifier();
|
||||
|
||||
$signature = $this->generateAblySignature(
|
||||
$request->channel_name,
|
||||
$request->socket_id,
|
||||
$userData = array_filter([
|
||||
'user_id' => (string) $broadcastIdentifier,
|
||||
'user_info' => $result,
|
||||
])
|
||||
);
|
||||
|
||||
return [
|
||||
'auth' => $this->getPublicToken().':'.$signature,
|
||||
'channel_data' => json_encode($userData),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the signature needed for Ably authentication headers.
|
||||
*
|
||||
* @param string $channelName
|
||||
* @param string $socketId
|
||||
* @param array|null $userData
|
||||
* @return string
|
||||
*/
|
||||
public function generateAblySignature($channelName, $socketId, $userData = null)
|
||||
{
|
||||
return hash_hmac(
|
||||
'sha256',
|
||||
sprintf('%s:%s%s', $socketId, $channelName, $userData ? ':'.json_encode($userData) : ''),
|
||||
$this->getPrivateToken(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast the given event.
|
||||
*
|
||||
* @param array $channels
|
||||
* @param string $event
|
||||
* @param array $payload
|
||||
* @return void
|
||||
*/
|
||||
public function broadcast(array $channels, $event, array $payload = [])
|
||||
{
|
||||
foreach ($this->formatChannels($channels) as $channel) {
|
||||
$this->ably->channels->get($channel)->publish(
|
||||
$this->buildAblyMessage($event, $payload)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an Ably message object for broadcasting.
|
||||
*
|
||||
* @param string $event
|
||||
* @param array $payload
|
||||
* @return \Ably\Models\Message
|
||||
*/
|
||||
protected function buildAblyMessage($event, array $payload = [])
|
||||
{
|
||||
return tap(new AblyMessage, function ($message) use ($event, $payload) {
|
||||
$message->name = $event;
|
||||
$message->data = $payload;
|
||||
$message->connectionKey = data_get($payload, 'socket');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the channel is protected by authentication.
|
||||
*
|
||||
* @param string $channel
|
||||
* @return bool
|
||||
*/
|
||||
public function isGuardedChannel($channel)
|
||||
{
|
||||
return Str::startsWith($channel, ['private-', 'presence-']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove prefix from channel name.
|
||||
*
|
||||
* @param string $channel
|
||||
* @return string
|
||||
*/
|
||||
public function normalizeChannelName($channel)
|
||||
{
|
||||
if ($this->isGuardedChannel($channel)) {
|
||||
return Str::startsWith($channel, 'private-')
|
||||
? Str::replaceFirst('private-', '', $channel)
|
||||
: Str::replaceFirst('presence-', '', $channel);
|
||||
}
|
||||
|
||||
return $channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the channel array into an array of strings.
|
||||
*
|
||||
* @param array $channels
|
||||
* @return array
|
||||
*/
|
||||
protected function formatChannels(array $channels)
|
||||
{
|
||||
return array_map(function ($channel) {
|
||||
$channel = (string) $channel;
|
||||
|
||||
if (Str::startsWith($channel, ['private-', 'presence-'])) {
|
||||
return Str::startsWith($channel, 'private-')
|
||||
? Str::replaceFirst('private-', 'private:', $channel)
|
||||
: Str::replaceFirst('presence-', 'presence:', $channel);
|
||||
}
|
||||
|
||||
return 'public:'.$channel;
|
||||
}, $channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the public token value from the Ably key.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getPublicToken()
|
||||
{
|
||||
return Str::before($this->ably->options->key, ':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the private token value from the Ably key.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getPrivateToken()
|
||||
{
|
||||
return Str::after($this->ably->options->key, ':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying Ably SDK instance.
|
||||
*
|
||||
* @return \Ably\AblyRest
|
||||
*/
|
||||
public function getAbly()
|
||||
{
|
||||
return $this->ably;
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ namespace Illuminate\Broadcasting\Broadcasters;
|
||||
use Exception;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
|
||||
use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;
|
||||
use Illuminate\Contracts\Routing\BindingRegistrar;
|
||||
use Illuminate\Contracts\Routing\UrlRoutable;
|
||||
use Illuminate\Support\Arr;
|
||||
@@ -40,13 +41,19 @@ abstract class Broadcaster implements BroadcasterContract
|
||||
/**
|
||||
* Register a channel authenticator.
|
||||
*
|
||||
* @param string $channel
|
||||
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $channel
|
||||
* @param callable|string $callback
|
||||
* @param array $options
|
||||
* @return $this
|
||||
*/
|
||||
public function channel($channel, $callback, $options = [])
|
||||
{
|
||||
if ($channel instanceof HasBroadcastChannel) {
|
||||
$channel = $channel->broadcastChannelRoute();
|
||||
} elseif (is_string($channel) && class_exists($channel) && is_a($channel, HasBroadcastChannel::class, true)) {
|
||||
$channel = (new $channel)->broadcastChannelRoute();
|
||||
}
|
||||
|
||||
$this->channels[$channel] = $callback;
|
||||
|
||||
$this->channelOptions[$channel] = $options;
|
||||
@@ -317,7 +324,7 @@ abstract class Broadcaster implements BroadcasterContract
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if channel name from request match a pattern from registered channels.
|
||||
* Check if the channel name from the request matches a pattern from registered channels.
|
||||
*
|
||||
* @param string $channel
|
||||
* @param string $pattern
|
||||
|
@@ -5,6 +5,7 @@ namespace Illuminate\Broadcasting\Broadcasters;
|
||||
use Illuminate\Broadcasting\BroadcastException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Pusher\ApiErrorException;
|
||||
use Pusher\Pusher;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
@@ -42,8 +43,9 @@ class PusherBroadcaster extends Broadcaster
|
||||
{
|
||||
$channelName = $this->normalizeChannelName($request->channel_name);
|
||||
|
||||
if ($this->isGuardedChannel($request->channel_name) &&
|
||||
! $this->retrieveUser($request, $channelName)) {
|
||||
if (empty($request->channel_name) ||
|
||||
($this->isGuardedChannel($request->channel_name) &&
|
||||
! $this->retrieveUser($request, $channelName))) {
|
||||
throw new AccessDeniedHttpException;
|
||||
}
|
||||
|
||||
@@ -69,11 +71,17 @@ class PusherBroadcaster extends Broadcaster
|
||||
|
||||
$channelName = $this->normalizeChannelName($request->channel_name);
|
||||
|
||||
$user = $this->retrieveUser($request, $channelName);
|
||||
|
||||
$broadcastIdentifier = method_exists($user, 'getAuthIdentifierForBroadcasting')
|
||||
? $user->getAuthIdentifierForBroadcasting()
|
||||
: $user->getAuthIdentifier();
|
||||
|
||||
return $this->decodePusherResponse(
|
||||
$request,
|
||||
$this->pusher->presence_auth(
|
||||
$request->channel_name, $request->socket_id,
|
||||
$this->retrieveUser($request, $channelName)->getAuthIdentifier(), $result
|
||||
$broadcastIdentifier, $result
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -109,20 +117,44 @@ class PusherBroadcaster extends Broadcaster
|
||||
{
|
||||
$socket = Arr::pull($payload, 'socket');
|
||||
|
||||
$response = $this->pusher->trigger(
|
||||
$this->formatChannels($channels), $event, $payload, $socket, true
|
||||
);
|
||||
if ($this->pusherServerIsVersionFiveOrGreater()) {
|
||||
$parameters = $socket !== null ? ['socket_id' => $socket] : [];
|
||||
|
||||
if ((is_array($response) && $response['status'] >= 200 && $response['status'] <= 299)
|
||||
|| $response === true) {
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
throw new BroadcastException(
|
||||
! empty($response['body'])
|
||||
? sprintf('Pusher error: %s.', $response['body'])
|
||||
: 'Failed to connect to Pusher.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw new BroadcastException(
|
||||
! empty($response['body'])
|
||||
? sprintf('Pusher error: %s.', $response['body'])
|
||||
: 'Failed to connect to Pusher.'
|
||||
);
|
||||
/**
|
||||
* Determine if the Pusher PHP server is version 5.0 or greater.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function pusherServerIsVersionFiveOrGreater()
|
||||
{
|
||||
return class_exists(ApiErrorException::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,4 +166,15 @@ class PusherBroadcaster extends Broadcaster
|
||||
{
|
||||
return $this->pusher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Pusher SDK instance.
|
||||
*
|
||||
* @param \Pusher\Pusher $pusher
|
||||
* @return void
|
||||
*/
|
||||
public function setPusher($pusher)
|
||||
{
|
||||
$this->pusher = $pusher;
|
||||
}
|
||||
}
|
||||
|
@@ -20,16 +20,16 @@ class RedisBroadcaster extends Broadcaster
|
||||
/**
|
||||
* The Redis connection to use for broadcasting.
|
||||
*
|
||||
* @var string
|
||||
* @var ?string
|
||||
*/
|
||||
protected $connection;
|
||||
protected $connection = null;
|
||||
|
||||
/**
|
||||
* The Redis key prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
protected $prefix = '';
|
||||
|
||||
/**
|
||||
* Create a new broadcaster instance.
|
||||
@@ -60,8 +60,9 @@ class RedisBroadcaster extends Broadcaster
|
||||
str_replace($this->prefix, '', $request->channel_name)
|
||||
);
|
||||
|
||||
if ($this->isGuardedChannel($request->channel_name) &&
|
||||
! $this->retrieveUser($request, $channelName)) {
|
||||
if (empty($request->channel_name) ||
|
||||
($this->isGuardedChannel($request->channel_name) &&
|
||||
! $this->retrieveUser($request, $channelName))) {
|
||||
throw new AccessDeniedHttpException;
|
||||
}
|
||||
|
||||
@@ -85,8 +86,14 @@ class RedisBroadcaster extends Broadcaster
|
||||
|
||||
$channelName = $this->normalizeChannelName($request->channel_name);
|
||||
|
||||
$user = $this->retrieveUser($request, $channelName);
|
||||
|
||||
$broadcastIdentifier = method_exists($user, 'getAuthIdentifierForBroadcasting')
|
||||
? $user->getAuthIdentifierForBroadcasting()
|
||||
: $user->getAuthIdentifier();
|
||||
|
||||
return json_encode(['channel_data' => [
|
||||
'user_id' => $this->retrieveUser($request, $channelName)->getAuthIdentifier(),
|
||||
'user_id' => $broadcastIdentifier,
|
||||
'user_info' => $result,
|
||||
]]);
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ use Illuminate\Support\Str;
|
||||
trait UsePusherChannelConventions
|
||||
{
|
||||
/**
|
||||
* Return true if channel is protected by authentication.
|
||||
* Return true if the channel is protected by authentication.
|
||||
*
|
||||
* @param string $channel
|
||||
* @return bool
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Illuminate\Broadcasting;
|
||||
|
||||
use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;
|
||||
|
||||
class Channel
|
||||
{
|
||||
/**
|
||||
@@ -14,12 +16,12 @@ class Channel
|
||||
/**
|
||||
* Create a new channel instance.
|
||||
*
|
||||
* @param string $name
|
||||
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $name
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->name = $name instanceof HasBroadcastChannel ? $name->broadcastChannel() : $name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
40
vendor/laravel/framework/src/Illuminate/Broadcasting/InteractsWithBroadcasting.php
vendored
Normal file
40
vendor/laravel/framework/src/Illuminate/Broadcasting/InteractsWithBroadcasting.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Broadcasting;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
trait InteractsWithBroadcasting
|
||||
{
|
||||
/**
|
||||
* The broadcaster connection to use to broadcast the event.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $broadcastConnection = [null];
|
||||
|
||||
/**
|
||||
* Broadcast the event using a specific broadcaster.
|
||||
*
|
||||
* @param array|string|null $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function broadcastVia($connection = null)
|
||||
{
|
||||
$this->broadcastConnection = is_null($connection)
|
||||
? [null]
|
||||
: Arr::wrap($connection);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the broadcaster connections the event should be broadcast on.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function broadcastConnections()
|
||||
{
|
||||
return $this->broadcastConnection;
|
||||
}
|
||||
}
|
@@ -33,6 +33,21 @@ class PendingBroadcast
|
||||
$this->events = $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast the event using a specific broadcaster.
|
||||
*
|
||||
* @param string|null $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function via($connection = null)
|
||||
{
|
||||
if (method_exists($this->event, 'broadcastVia')) {
|
||||
$this->event->broadcastVia($connection);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast the event to everyone except the current user.
|
||||
*
|
||||
|
@@ -2,16 +2,20 @@
|
||||
|
||||
namespace Illuminate\Broadcasting;
|
||||
|
||||
use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;
|
||||
|
||||
class PrivateChannel extends Channel
|
||||
{
|
||||
/**
|
||||
* Create a new channel instance.
|
||||
*
|
||||
* @param string $name
|
||||
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $name
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($name)
|
||||
{
|
||||
$name = $name instanceof HasBroadcastChannel ? $name->broadcastChannel() : $name;
|
||||
|
||||
parent::__construct('private-'.$name);
|
||||
}
|
||||
}
|
||||
|
@@ -14,13 +14,14 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5|^8.0",
|
||||
"php": "^7.3|^8.0",
|
||||
"ext-json": "*",
|
||||
"psr/log": "^1.0",
|
||||
"illuminate/bus": "^7.0",
|
||||
"illuminate/contracts": "^7.0",
|
||||
"illuminate/queue": "^7.0",
|
||||
"illuminate/support": "^7.0"
|
||||
"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"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -29,11 +30,12 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.x-dev"
|
||||
"dev-master": "8.x-dev"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0)."
|
||||
"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)."
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
|
481
vendor/laravel/framework/src/Illuminate/Bus/Batch.php
vendored
Normal file
481
vendor/laravel/framework/src/Illuminate/Bus/Batch.php
vendored
Normal file
@@ -0,0 +1,481 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Queue\Factory as QueueFactory;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Queue\CallQueuedClosure;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use JsonSerializable;
|
||||
use Throwable;
|
||||
|
||||
class Batch implements Arrayable, JsonSerializable
|
||||
{
|
||||
/**
|
||||
* The queue factory implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Queue\Factory
|
||||
*/
|
||||
protected $queue;
|
||||
|
||||
/**
|
||||
* The repository implementation.
|
||||
*
|
||||
* @var \Illuminate\Bus\BatchRepository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* The batch ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The batch name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The total number of jobs that belong to the batch.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $totalJobs;
|
||||
|
||||
/**
|
||||
* The total number of jobs that are still pending.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $pendingJobs;
|
||||
|
||||
/**
|
||||
* The total number of jobs that have failed.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $failedJobs;
|
||||
|
||||
/**
|
||||
* The IDs of the jobs that have failed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $failedJobIds;
|
||||
|
||||
/**
|
||||
* The batch options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* The date indicating when the batch was created.
|
||||
*
|
||||
* @var \Carbon\CarbonImmutable
|
||||
*/
|
||||
public $createdAt;
|
||||
|
||||
/**
|
||||
* The date indicating when the batch was cancelled.
|
||||
*
|
||||
* @var \Carbon\CarbonImmutable|null
|
||||
*/
|
||||
public $cancelledAt;
|
||||
|
||||
/**
|
||||
* The date indicating when the batch was finished.
|
||||
*
|
||||
* @var \Carbon\CarbonImmutable|null
|
||||
*/
|
||||
public $finishedAt;
|
||||
|
||||
/**
|
||||
* Create a new batch instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Queue\Factory $queue
|
||||
* @param \Illuminate\Bus\BatchRepository $repository
|
||||
* @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 void
|
||||
*/
|
||||
public function __construct(QueueFactory $queue,
|
||||
BatchRepository $repository,
|
||||
string $id,
|
||||
string $name,
|
||||
int $totalJobs,
|
||||
int $pendingJobs,
|
||||
int $failedJobs,
|
||||
array $failedJobIds,
|
||||
array $options,
|
||||
CarbonImmutable $createdAt,
|
||||
?CarbonImmutable $cancelledAt = null,
|
||||
?CarbonImmutable $finishedAt = null)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
$this->repository = $repository;
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->totalJobs = $totalJobs;
|
||||
$this->pendingJobs = $pendingJobs;
|
||||
$this->failedJobs = $failedJobs;
|
||||
$this->failedJobIds = $failedJobIds;
|
||||
$this->options = $options;
|
||||
$this->createdAt = $createdAt;
|
||||
$this->cancelledAt = $cancelledAt;
|
||||
$this->finishedAt = $finishedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a fresh instance of the batch represented by this ID.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function fresh()
|
||||
{
|
||||
return $this->repository->find($this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional jobs to the batch.
|
||||
*
|
||||
* @param \Illuminate\Support\Enumerable|array $jobs
|
||||
* @return self
|
||||
*/
|
||||
public function add($jobs)
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
$jobs = Collection::wrap($jobs)->map(function ($job) use (&$count) {
|
||||
$job = $job instanceof Closure ? CallQueuedClosure::create($job) : $job;
|
||||
|
||||
if (is_array($job)) {
|
||||
$count += count($job);
|
||||
|
||||
return with($this->prepareBatchedChain($job), function ($chain) {
|
||||
return $chain->first()
|
||||
->allOnQueue($this->options['queue'] ?? null)
|
||||
->allOnConnection($this->options['connection'] ?? null)
|
||||
->chain($chain->slice(1)->values()->all());
|
||||
});
|
||||
} else {
|
||||
$job->withBatchId($this->id);
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
return $job;
|
||||
});
|
||||
|
||||
$this->repository->transaction(function () use ($jobs, $count) {
|
||||
$this->repository->incrementTotalJobs($this->id, $count);
|
||||
|
||||
$this->queue->connection($this->options['connection'] ?? null)->bulk(
|
||||
$jobs->all(),
|
||||
$data = '',
|
||||
$this->options['queue'] ?? null
|
||||
);
|
||||
});
|
||||
|
||||
return $this->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a chain that exists within the jobs being added.
|
||||
*
|
||||
* @param array $chain
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
protected function prepareBatchedChain(array $chain)
|
||||
{
|
||||
return collect($chain)->map(function ($job) {
|
||||
$job = $job instanceof Closure ? CallQueuedClosure::create($job) : $job;
|
||||
|
||||
return $job->withBatchId($this->id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of jobs that have been processed by the batch thus far.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function processedJobs()
|
||||
{
|
||||
return $this->totalJobs - $this->pendingJobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the percentage of jobs that have been processed (between 0-100).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function progress()
|
||||
{
|
||||
return $this->totalJobs > 0 ? round(($this->processedJobs() / $this->totalJobs) * 100) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that a job within the batch finished successfully, executing any callbacks if necessary.
|
||||
*
|
||||
* @param string $jobId
|
||||
* @return void
|
||||
*/
|
||||
public function recordSuccessfulJob(string $jobId)
|
||||
{
|
||||
$counts = $this->decrementPendingJobs($jobId);
|
||||
|
||||
if ($counts->pendingJobs === 0) {
|
||||
$this->repository->markAsFinished($this->id);
|
||||
}
|
||||
|
||||
if ($counts->pendingJobs === 0 && $this->hasThenCallbacks()) {
|
||||
$batch = $this->fresh();
|
||||
|
||||
collect($this->options['then'])->each(function ($handler) use ($batch) {
|
||||
$this->invokeHandlerCallback($handler, $batch);
|
||||
});
|
||||
}
|
||||
|
||||
if ($counts->allJobsHaveRanExactlyOnce() && $this->hasFinallyCallbacks()) {
|
||||
$batch = $this->fresh();
|
||||
|
||||
collect($this->options['finally'])->each(function ($handler) use ($batch) {
|
||||
$this->invokeHandlerCallback($handler, $batch);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the pending jobs for the batch.
|
||||
*
|
||||
* @param string $jobId
|
||||
* @return \Illuminate\Bus\UpdatedBatchJobCounts
|
||||
*/
|
||||
public function decrementPendingJobs(string $jobId)
|
||||
{
|
||||
return $this->repository->decrementPendingJobs($this->id, $jobId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch has finished executing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function finished()
|
||||
{
|
||||
return ! is_null($this->finishedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch has "success" callbacks.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasThenCallbacks()
|
||||
{
|
||||
return isset($this->options['then']) && ! empty($this->options['then']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch allows jobs to fail without cancelling the batch.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function allowsFailures()
|
||||
{
|
||||
return Arr::get($this->options, 'allowFailures', false) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch has job failures.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFailures()
|
||||
{
|
||||
return $this->failedJobs > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that a job within the batch failed to finish successfully, executing any callbacks if necessary.
|
||||
*
|
||||
* @param string $jobId
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public function recordFailedJob(string $jobId, $e)
|
||||
{
|
||||
$counts = $this->incrementFailedJobs($jobId);
|
||||
|
||||
if ($counts->failedJobs === 1 && ! $this->allowsFailures()) {
|
||||
$this->cancel();
|
||||
}
|
||||
|
||||
if ($counts->failedJobs === 1 && $this->hasCatchCallbacks()) {
|
||||
$batch = $this->fresh();
|
||||
|
||||
collect($this->options['catch'])->each(function ($handler) use ($batch, $e) {
|
||||
$this->invokeHandlerCallback($handler, $batch, $e);
|
||||
});
|
||||
}
|
||||
|
||||
if ($counts->allJobsHaveRanExactlyOnce() && $this->hasFinallyCallbacks()) {
|
||||
$batch = $this->fresh();
|
||||
|
||||
collect($this->options['finally'])->each(function ($handler) use ($batch, $e) {
|
||||
$this->invokeHandlerCallback($handler, $batch, $e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the failed jobs for the batch.
|
||||
*
|
||||
* @param string $jobId
|
||||
* @return \Illuminate\Bus\UpdatedBatchJobCounts
|
||||
*/
|
||||
public function incrementFailedJobs(string $jobId)
|
||||
{
|
||||
return $this->repository->incrementFailedJobs($this->id, $jobId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch has "catch" callbacks.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCatchCallbacks()
|
||||
{
|
||||
return isset($this->options['catch']) && ! empty($this->options['catch']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch has "finally" callbacks.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFinallyCallbacks()
|
||||
{
|
||||
return isset($this->options['finally']) && ! empty($this->options['finally']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the batch.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function cancel()
|
||||
{
|
||||
$this->repository->cancel($this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch has been cancelled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canceled()
|
||||
{
|
||||
return $this->cancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch has been cancelled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function cancelled()
|
||||
{
|
||||
return ! is_null($this->cancelledAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the batch from storage.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->repository->delete($this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a batch callback handler.
|
||||
*
|
||||
* @param callable $handler
|
||||
* @param \Illuminate\Bus\Batch $batch
|
||||
* @param \Throwable|null $e
|
||||
* @return void
|
||||
*/
|
||||
protected function invokeHandlerCallback($handler, Batch $batch, Throwable $e = null)
|
||||
{
|
||||
try {
|
||||
return $handler($batch, $e);
|
||||
} catch (Throwable $e) {
|
||||
if (function_exists('report')) {
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the batch to an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'totalJobs' => $this->totalJobs,
|
||||
'pendingJobs' => $this->pendingJobs,
|
||||
'processedJobs' => $this->processedJobs(),
|
||||
'progress' => $this->progress(),
|
||||
'failedJobs' => $this->failedJobs,
|
||||
'options' => $this->options,
|
||||
'createdAt' => $this->createdAt,
|
||||
'cancelledAt' => $this->cancelledAt,
|
||||
'finishedAt' => $this->finishedAt,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON serializable representation of the object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically access the batch's "options" via properties.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->options[$key] ?? null;
|
||||
}
|
||||
}
|
58
vendor/laravel/framework/src/Illuminate/Bus/BatchFactory.php
vendored
Normal file
58
vendor/laravel/framework/src/Illuminate/Bus/BatchFactory.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Contracts\Queue\Factory as QueueFactory;
|
||||
|
||||
class BatchFactory
|
||||
{
|
||||
/**
|
||||
* The queue factory implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Queue\Factory
|
||||
*/
|
||||
protected $queue;
|
||||
|
||||
/**
|
||||
* Create a new batch factory instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Queue\Factory $queue
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(QueueFactory $queue)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new batch instance.
|
||||
*
|
||||
* @param \Illuminate\Bus\BatchRepository $repository
|
||||
* @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 \Illuminate\Bus\Batch
|
||||
*/
|
||||
public function make(BatchRepository $repository,
|
||||
string $id,
|
||||
string $name,
|
||||
int $totalJobs,
|
||||
int $pendingJobs,
|
||||
int $failedJobs,
|
||||
array $failedJobIds,
|
||||
array $options,
|
||||
CarbonImmutable $createdAt,
|
||||
?CarbonImmutable $cancelledAt,
|
||||
?CarbonImmutable $finishedAt)
|
||||
{
|
||||
return new Batch($this->queue, $repository, $id, $name, $totalJobs, $pendingJobs, $failedJobs, $failedJobIds, $options, $createdAt, $cancelledAt, $finishedAt);
|
||||
}
|
||||
}
|
92
vendor/laravel/framework/src/Illuminate/Bus/BatchRepository.php
vendored
Normal file
92
vendor/laravel/framework/src/Illuminate/Bus/BatchRepository.php
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
use Closure;
|
||||
|
||||
interface BatchRepository
|
||||
{
|
||||
/**
|
||||
* Retrieve a list of batches.
|
||||
*
|
||||
* @param int $limit
|
||||
* @param mixed $before
|
||||
* @return \Illuminate\Bus\Batch[]
|
||||
*/
|
||||
public function get($limit, $before);
|
||||
|
||||
/**
|
||||
* Retrieve information about an existing batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return \Illuminate\Bus\Batch|null
|
||||
*/
|
||||
public function find(string $batchId);
|
||||
|
||||
/**
|
||||
* Store a new pending batch.
|
||||
*
|
||||
* @param \Illuminate\Bus\PendingBatch $batch
|
||||
* @return \Illuminate\Bus\Batch
|
||||
*/
|
||||
public function store(PendingBatch $batch);
|
||||
|
||||
/**
|
||||
* Increment the total number of jobs within the batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @param int $amount
|
||||
* @return void
|
||||
*/
|
||||
public function incrementTotalJobs(string $batchId, int $amount);
|
||||
|
||||
/**
|
||||
* Decrement the total number of pending jobs for the batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @param string $jobId
|
||||
* @return \Illuminate\Bus\UpdatedBatchJobCounts
|
||||
*/
|
||||
public function decrementPendingJobs(string $batchId, string $jobId);
|
||||
|
||||
/**
|
||||
* Increment the total number of failed jobs for the batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @param string $jobId
|
||||
* @return \Illuminate\Bus\UpdatedBatchJobCounts
|
||||
*/
|
||||
public function incrementFailedJobs(string $batchId, string $jobId);
|
||||
|
||||
/**
|
||||
* Mark the batch that has the given ID as finished.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return void
|
||||
*/
|
||||
public function markAsFinished(string $batchId);
|
||||
|
||||
/**
|
||||
* Cancel the batch that has the given ID.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return void
|
||||
*/
|
||||
public function cancel(string $batchId);
|
||||
|
||||
/**
|
||||
* Delete the batch that has the given ID.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return void
|
||||
*/
|
||||
public function delete(string $batchId);
|
||||
|
||||
/**
|
||||
* Execute the given Closure within a storage specific transaction.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(Closure $callback);
|
||||
}
|
52
vendor/laravel/framework/src/Illuminate/Bus/Batchable.php
vendored
Normal file
52
vendor/laravel/framework/src/Illuminate/Bus/Batchable.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
use Illuminate\Container\Container;
|
||||
|
||||
trait Batchable
|
||||
{
|
||||
/**
|
||||
* The batch ID (if applicable).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $batchId;
|
||||
|
||||
/**
|
||||
* Get the batch instance for the job, if applicable.
|
||||
*
|
||||
* @return \Illuminate\Bus\Batch|null
|
||||
*/
|
||||
public function batch()
|
||||
{
|
||||
if ($this->batchId) {
|
||||
return Container::getInstance()->make(BatchRepository::class)->find($this->batchId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the batch is still active and processing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function batching()
|
||||
{
|
||||
$batch = $this->batch();
|
||||
|
||||
return $batch && ! $batch->cancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the batch ID on the job.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return $this
|
||||
*/
|
||||
public function withBatchId(string $batchId)
|
||||
{
|
||||
$this->batchId = $batchId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -23,6 +23,8 @@ class BusServiceProvider extends ServiceProvider implements DeferrableProvider
|
||||
});
|
||||
});
|
||||
|
||||
$this->registerBatchServices();
|
||||
|
||||
$this->app->alias(
|
||||
Dispatcher::class, DispatcherContract::class
|
||||
);
|
||||
@@ -32,6 +34,24 @@ class BusServiceProvider extends ServiceProvider implements DeferrableProvider
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the batch handling services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerBatchServices()
|
||||
{
|
||||
$this->app->singleton(BatchRepository::class, DatabaseBatchRepository::class);
|
||||
|
||||
$this->app->singleton(DatabaseBatchRepository::class, function ($app) {
|
||||
return new DatabaseBatchRepository(
|
||||
$app->make(BatchFactory::class),
|
||||
$app->make('db')->connection($app->config->get('queue.batching.database')),
|
||||
$app->config->get('queue.batching.table', 'job_batches')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
@@ -43,6 +63,7 @@ class BusServiceProvider extends ServiceProvider implements DeferrableProvider
|
||||
Dispatcher::class,
|
||||
DispatcherContract::class,
|
||||
QueueingDispatcherContract::class,
|
||||
BatchRepository::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
347
vendor/laravel/framework/src/Illuminate/Bus/DatabaseBatchRepository.php
vendored
Normal file
347
vendor/laravel/framework/src/Illuminate/Bus/DatabaseBatchRepository.php
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Closure;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\PostgresConnection;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class DatabaseBatchRepository implements PrunableBatchRepository
|
||||
{
|
||||
/**
|
||||
* The batch factory instance.
|
||||
*
|
||||
* @var \Illuminate\Bus\BatchFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
/**
|
||||
* The database connection instance.
|
||||
*
|
||||
* @var \Illuminate\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The database table to use to store batch information.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* Create a new batch repository instance.
|
||||
*
|
||||
* @param \Illuminate\Bus\BatchFactory $factory
|
||||
* @param \Illuminate\Database\Connection $connection
|
||||
* @param string $table
|
||||
*/
|
||||
public function __construct(BatchFactory $factory, Connection $connection, string $table)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
$this->connection = $connection;
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of batches.
|
||||
*
|
||||
* @param int $limit
|
||||
* @param mixed $before
|
||||
* @return \Illuminate\Bus\Batch[]
|
||||
*/
|
||||
public function get($limit = 50, $before = null)
|
||||
{
|
||||
return $this->connection->table($this->table)
|
||||
->orderByDesc('id')
|
||||
->take($limit)
|
||||
->when($before, function ($q) use ($before) {
|
||||
return $q->where('id', '<', $before);
|
||||
})
|
||||
->get()
|
||||
->map(function ($batch) {
|
||||
return $this->toBatch($batch);
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information about an existing batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return \Illuminate\Bus\Batch|null
|
||||
*/
|
||||
public function find(string $batchId)
|
||||
{
|
||||
$batch = $this->connection->table($this->table)
|
||||
->where('id', $batchId)
|
||||
->first();
|
||||
|
||||
if ($batch) {
|
||||
return $this->toBatch($batch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new pending batch.
|
||||
*
|
||||
* @param \Illuminate\Bus\PendingBatch $batch
|
||||
* @return \Illuminate\Bus\Batch
|
||||
*/
|
||||
public function store(PendingBatch $batch)
|
||||
{
|
||||
$id = (string) Str::orderedUuid();
|
||||
|
||||
$this->connection->table($this->table)->insert([
|
||||
'id' => $id,
|
||||
'name' => $batch->name,
|
||||
'total_jobs' => 0,
|
||||
'pending_jobs' => 0,
|
||||
'failed_jobs' => 0,
|
||||
'failed_job_ids' => '[]',
|
||||
'options' => $this->serialize($batch->options),
|
||||
'created_at' => time(),
|
||||
'cancelled_at' => null,
|
||||
'finished_at' => null,
|
||||
]);
|
||||
|
||||
return $this->find($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the total number of jobs within the batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @param int $amount
|
||||
* @return void
|
||||
*/
|
||||
public function incrementTotalJobs(string $batchId, int $amount)
|
||||
{
|
||||
$this->connection->table($this->table)->where('id', $batchId)->update([
|
||||
'total_jobs' => new Expression('total_jobs + '.$amount),
|
||||
'pending_jobs' => new Expression('pending_jobs + '.$amount),
|
||||
'finished_at' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the total number of pending jobs for the batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @param string $jobId
|
||||
* @return \Illuminate\Bus\UpdatedBatchJobCounts
|
||||
*/
|
||||
public function decrementPendingJobs(string $batchId, string $jobId)
|
||||
{
|
||||
$values = $this->updateAtomicValues($batchId, function ($batch) use ($jobId) {
|
||||
return [
|
||||
'pending_jobs' => $batch->pending_jobs - 1,
|
||||
'failed_jobs' => $batch->failed_jobs,
|
||||
'failed_job_ids' => json_encode(array_values(array_diff(json_decode($batch->failed_job_ids, true), [$jobId]))),
|
||||
];
|
||||
});
|
||||
|
||||
return new UpdatedBatchJobCounts(
|
||||
$values['pending_jobs'],
|
||||
$values['failed_jobs']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the total number of failed jobs for the batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @param string $jobId
|
||||
* @return \Illuminate\Bus\UpdatedBatchJobCounts
|
||||
*/
|
||||
public function incrementFailedJobs(string $batchId, string $jobId)
|
||||
{
|
||||
$values = $this->updateAtomicValues($batchId, function ($batch) use ($jobId) {
|
||||
return [
|
||||
'pending_jobs' => $batch->pending_jobs,
|
||||
'failed_jobs' => $batch->failed_jobs + 1,
|
||||
'failed_job_ids' => json_encode(array_values(array_unique(array_merge(json_decode($batch->failed_job_ids, true), [$jobId])))),
|
||||
];
|
||||
});
|
||||
|
||||
return new UpdatedBatchJobCounts(
|
||||
$values['pending_jobs'],
|
||||
$values['failed_jobs']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an atomic value within the batch.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @param \Closure $callback
|
||||
* @return int|null
|
||||
*/
|
||||
protected function updateAtomicValues(string $batchId, Closure $callback)
|
||||
{
|
||||
return $this->connection->transaction(function () use ($batchId, $callback) {
|
||||
$batch = $this->connection->table($this->table)->where('id', $batchId)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
return is_null($batch) ? [] : tap($callback($batch), function ($values) use ($batchId) {
|
||||
$this->connection->table($this->table)->where('id', $batchId)->update($values);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the batch that has the given ID as finished.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return void
|
||||
*/
|
||||
public function markAsFinished(string $batchId)
|
||||
{
|
||||
$this->connection->table($this->table)->where('id', $batchId)->update([
|
||||
'finished_at' => time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the batch that has the given ID.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return void
|
||||
*/
|
||||
public function cancel(string $batchId)
|
||||
{
|
||||
$this->connection->table($this->table)->where('id', $batchId)->update([
|
||||
'cancelled_at' => time(),
|
||||
'finished_at' => time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the batch that has the given ID.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return void
|
||||
*/
|
||||
public function delete(string $batchId)
|
||||
{
|
||||
$this->connection->table($this->table)->where('id', $batchId)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune all of the entries older than the given date.
|
||||
*
|
||||
* @param \DateTimeInterface $before
|
||||
* @return int
|
||||
*/
|
||||
public function prune(DateTimeInterface $before)
|
||||
{
|
||||
$query = $this->connection->table($this->table)
|
||||
->whereNotNull('finished_at')
|
||||
->where('finished_at', '<', $before->getTimestamp());
|
||||
|
||||
$totalDeleted = 0;
|
||||
|
||||
do {
|
||||
$deleted = $query->take(1000)->delete();
|
||||
|
||||
$totalDeleted += $deleted;
|
||||
} while ($deleted !== 0);
|
||||
|
||||
return $totalDeleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune all of the unfinished entries older than the given date.
|
||||
*
|
||||
* @param \DateTimeInterface $before
|
||||
* @return int
|
||||
*/
|
||||
public function pruneUnfinished(DateTimeInterface $before)
|
||||
{
|
||||
$query = $this->connection->table($this->table)
|
||||
->whereNull('finished_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.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(Closure $callback)
|
||||
{
|
||||
return $this->connection->transaction(function () use ($callback) {
|
||||
return $callback();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
protected function serialize($value)
|
||||
{
|
||||
$serialized = serialize($value);
|
||||
|
||||
return $this->connection instanceof PostgresConnection
|
||||
? base64_encode($serialized)
|
||||
: $serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserialize the given value.
|
||||
*
|
||||
* @param string $serialized
|
||||
* @return mixed
|
||||
*/
|
||||
protected function unserialize($serialized)
|
||||
{
|
||||
if ($this->connection instanceof PostgresConnection &&
|
||||
! Str::contains($serialized, [':', ';'])) {
|
||||
$serialized = base64_decode($serialized);
|
||||
}
|
||||
|
||||
return unserialize($serialized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given raw batch to a Batch object.
|
||||
*
|
||||
* @param object $batch
|
||||
* @return \Illuminate\Bus\Batch
|
||||
*/
|
||||
protected function toBatch($batch)
|
||||
{
|
||||
return $this->factory->make(
|
||||
$this,
|
||||
$batch->id,
|
||||
$batch->name,
|
||||
(int) $batch->total_jobs,
|
||||
(int) $batch->pending_jobs,
|
||||
(int) $batch->failed_jobs,
|
||||
json_decode($batch->failed_job_ids, true),
|
||||
$this->unserialize($batch->options),
|
||||
CarbonImmutable::createFromTimestamp($batch->created_at),
|
||||
$batch->cancelled_at ? CarbonImmutable::createFromTimestamp($batch->cancelled_at) : $batch->cancelled_at,
|
||||
$batch->finished_at ? CarbonImmutable::createFromTimestamp($batch->finished_at) : $batch->finished_at
|
||||
);
|
||||
}
|
||||
}
|
@@ -7,7 +7,11 @@ use Illuminate\Contracts\Bus\QueueingDispatcher;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Queue\Queue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\PendingChain;
|
||||
use Illuminate\Pipeline\Pipeline;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Jobs\SyncJob;
|
||||
use Illuminate\Support\Collection;
|
||||
use RuntimeException;
|
||||
|
||||
class Dispatcher implements QueueingDispatcher
|
||||
@@ -69,35 +73,100 @@ class Dispatcher implements QueueingDispatcher
|
||||
*/
|
||||
public function dispatch($command)
|
||||
{
|
||||
if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
|
||||
return $this->dispatchToQueue($command);
|
||||
}
|
||||
|
||||
return $this->dispatchNow($command);
|
||||
return $this->queueResolver && $this->commandShouldBeQueued($command)
|
||||
? $this->dispatchToQueue($command)
|
||||
: $this->dispatchNow($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a command to its appropriate handler in the current process.
|
||||
*
|
||||
* Queueable jobs will be dispatched to the "sync" queue.
|
||||
*
|
||||
* @param mixed $command
|
||||
* @param mixed $handler
|
||||
* @return mixed
|
||||
*/
|
||||
public function dispatchSync($command, $handler = null)
|
||||
{
|
||||
if ($this->queueResolver &&
|
||||
$this->commandShouldBeQueued($command) &&
|
||||
method_exists($command, 'onConnection')) {
|
||||
return $this->dispatchToQueue($command->onConnection('sync'));
|
||||
}
|
||||
|
||||
return $this->dispatchNow($command, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a command to its appropriate handler in the current process without using the synchronous queue.
|
||||
*
|
||||
* @param mixed $command
|
||||
* @param mixed $handler
|
||||
* @return mixed
|
||||
*/
|
||||
public function dispatchNow($command, $handler = null)
|
||||
{
|
||||
$uses = class_uses_recursive($command);
|
||||
|
||||
if (in_array(InteractsWithQueue::class, $uses) &&
|
||||
in_array(Queueable::class, $uses) &&
|
||||
! $command->job) {
|
||||
$command->setJob(new SyncJob($this->container, json_encode([]), 'sync', 'sync'));
|
||||
}
|
||||
|
||||
if ($handler || $handler = $this->getCommandHandler($command)) {
|
||||
$callback = function ($command) use ($handler) {
|
||||
return $handler->handle($command);
|
||||
$method = method_exists($handler, 'handle') ? 'handle' : '__invoke';
|
||||
|
||||
return $handler->{$method}($command);
|
||||
};
|
||||
} else {
|
||||
$callback = function ($command) {
|
||||
return $this->container->call([$command, 'handle']);
|
||||
$method = method_exists($command, 'handle') ? 'handle' : '__invoke';
|
||||
|
||||
return $this->container->call([$command, $method]);
|
||||
};
|
||||
}
|
||||
|
||||
return $this->pipeline->send($command)->through($this->pipes)->then($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find the batch with the given ID.
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return \Illuminate\Bus\Batch|null
|
||||
*/
|
||||
public function findBatch(string $batchId)
|
||||
{
|
||||
return $this->container->make(BatchRepository::class)->find($batchId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new batch of queueable jobs.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection|array|mixed $jobs
|
||||
* @return \Illuminate\Bus\PendingBatch
|
||||
*/
|
||||
public function batch($jobs)
|
||||
{
|
||||
return new PendingBatch($this->container, Collection::wrap($jobs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new chain of queueable jobs.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection|array $jobs
|
||||
* @return \Illuminate\Foundation\Bus\PendingChain
|
||||
*/
|
||||
public function chain($jobs)
|
||||
{
|
||||
$jobs = Collection::wrap($jobs);
|
||||
|
||||
return new PendingChain($jobs->shift(), $jobs->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given command has a handler.
|
||||
*
|
||||
@@ -140,6 +209,8 @@ class Dispatcher implements QueueingDispatcher
|
||||
*
|
||||
* @param mixed $command
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function dispatchToQueue($command)
|
||||
{
|
||||
|
26
vendor/laravel/framework/src/Illuminate/Bus/Events/BatchDispatched.php
vendored
Normal file
26
vendor/laravel/framework/src/Illuminate/Bus/Events/BatchDispatched.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus\Events;
|
||||
|
||||
use Illuminate\Bus\Batch;
|
||||
|
||||
class BatchDispatched
|
||||
{
|
||||
/**
|
||||
* The batch instance.
|
||||
*
|
||||
* @var \Illuminate\Bus\Batch
|
||||
*/
|
||||
public $batch;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param \Illuminate\Bus\Batch $batch
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Batch $batch)
|
||||
{
|
||||
$this->batch = $batch;
|
||||
}
|
||||
}
|
272
vendor/laravel/framework/src/Illuminate/Bus/PendingBatch.php
vendored
Normal file
272
vendor/laravel/framework/src/Illuminate/Bus/PendingBatch.php
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
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 Throwable;
|
||||
|
||||
class PendingBatch
|
||||
{
|
||||
/**
|
||||
* The IoC container instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Container\Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* The batch name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = '';
|
||||
|
||||
/**
|
||||
* The jobs that belong to the batch.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
public $jobs;
|
||||
|
||||
/**
|
||||
* The batch options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* Create a new pending batch instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Container\Container $container
|
||||
* @param \Illuminate\Support\Collection $jobs
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Container $container, Collection $jobs)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->jobs = $jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add jobs to the batch.
|
||||
*
|
||||
* @param iterable $jobs
|
||||
* @return $this
|
||||
*/
|
||||
public function add($jobs)
|
||||
{
|
||||
foreach ($jobs as $job) {
|
||||
$this->jobs->push($job);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to be executed after all jobs in the batch have executed successfully.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function then($callback)
|
||||
{
|
||||
$this->options['then'][] = $callback instanceof Closure
|
||||
? SerializableClosureFactory::make($callback)
|
||||
: $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "then" callbacks that have been registered with the pending batch.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function thenCallbacks()
|
||||
{
|
||||
return $this->options['then'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to be executed after the first failing job in the batch.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function catch($callback)
|
||||
{
|
||||
$this->options['catch'][] = $callback instanceof Closure
|
||||
? SerializableClosureFactory::make($callback)
|
||||
: $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "catch" callbacks that have been registered with the pending batch.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function catchCallbacks()
|
||||
{
|
||||
return $this->options['catch'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to be executed after the batch has finished executing.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function finally($callback)
|
||||
{
|
||||
$this->options['finally'][] = $callback instanceof Closure
|
||||
? SerializableClosureFactory::make($callback)
|
||||
: $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "finally" callbacks that have been registered with the pending batch.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function finallyCallbacks()
|
||||
{
|
||||
return $this->options['finally'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the batch should not be cancelled when a job within the batch fails.
|
||||
*
|
||||
* @param bool $allowFailures
|
||||
* @return $this
|
||||
*/
|
||||
public function allowFailures($allowFailures = true)
|
||||
{
|
||||
$this->options['allowFailures'] = $allowFailures;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the pending batch allows jobs to fail without cancelling the batch.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function allowsFailures()
|
||||
{
|
||||
return Arr::get($this->options, 'allowFailures', false) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name for the batch.
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function name(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the queue connection that the batched jobs should run on.
|
||||
*
|
||||
* @param string $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function onConnection(string $connection)
|
||||
{
|
||||
$this->options['connection'] = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connection used by the pending batch.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function connection()
|
||||
{
|
||||
return $this->options['connection'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the queue that the batched jobs should run on.
|
||||
*
|
||||
* @param string $queue
|
||||
* @return $this
|
||||
*/
|
||||
public function onQueue(string $queue)
|
||||
{
|
||||
$this->options['queue'] = $queue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the queue used by the pending batch.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function queue()
|
||||
{
|
||||
return $this->options['queue'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional data into the batch's options array.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
*/
|
||||
public function withOption(string $key, $value)
|
||||
{
|
||||
$this->options[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the batch.
|
||||
*
|
||||
* @return \Illuminate\Bus\Batch
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function dispatch()
|
||||
{
|
||||
$repository = $this->container->make(BatchRepository::class);
|
||||
|
||||
try {
|
||||
$batch = $repository->store($this);
|
||||
|
||||
$batch = $batch->add($this->jobs);
|
||||
} catch (Throwable $e) {
|
||||
if (isset($batch)) {
|
||||
$repository->delete($batch->id);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->container->make(EventDispatcher::class)->dispatch(
|
||||
new BatchDispatched($batch)
|
||||
);
|
||||
|
||||
return $batch;
|
||||
}
|
||||
}
|
16
vendor/laravel/framework/src/Illuminate/Bus/PrunableBatchRepository.php
vendored
Normal file
16
vendor/laravel/framework/src/Illuminate/Bus/PrunableBatchRepository.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
use DateTimeInterface;
|
||||
|
||||
interface PrunableBatchRepository extends BatchRepository
|
||||
{
|
||||
/**
|
||||
* Prune all of the entries older than the given date.
|
||||
*
|
||||
* @param \DateTimeInterface $before
|
||||
* @return int
|
||||
*/
|
||||
public function prune(DateTimeInterface $before);
|
||||
}
|
@@ -37,6 +37,13 @@ trait Queueable
|
||||
*/
|
||||
public $chainQueue;
|
||||
|
||||
/**
|
||||
* The callbacks to be executed on chain failure.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
public $chainCatchCallbacks;
|
||||
|
||||
/**
|
||||
* The number of seconds before the job should be made available.
|
||||
*
|
||||
@@ -44,6 +51,13 @@ trait Queueable
|
||||
*/
|
||||
public $delay;
|
||||
|
||||
/**
|
||||
* Indicates whether the job should be dispatched after all database transactions have committed.
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
public $afterCommit;
|
||||
|
||||
/**
|
||||
* The middleware the job should be dispatched through.
|
||||
*
|
||||
@@ -125,6 +139,30 @@ trait Queueable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the job should be dispatched after all database transactions have committed.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function afterCommit()
|
||||
{
|
||||
$this->afterCommit = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the job should not wait until database transactions have been committed before dispatching.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function beforeCommit()
|
||||
{
|
||||
$this->afterCommit = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the middleware the job should be dispatched through.
|
||||
*
|
||||
@@ -158,6 +196,8 @@ trait Queueable
|
||||
*
|
||||
* @param mixed $job
|
||||
* @return string
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function serializeJob($job)
|
||||
{
|
||||
@@ -190,7 +230,21 @@ trait Queueable
|
||||
|
||||
$next->chainConnection = $this->chainConnection;
|
||||
$next->chainQueue = $this->chainQueue;
|
||||
$next->chainCatchCallbacks = $this->chainCatchCallbacks;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke all of the chain's failed job callbacks.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public function invokeChainCatchCallbacks($e)
|
||||
{
|
||||
collect($this->chainCatchCallbacks)->each(function ($callback) use ($e) {
|
||||
$callback($e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
48
vendor/laravel/framework/src/Illuminate/Bus/UniqueLock.php
vendored
Normal file
48
vendor/laravel/framework/src/Illuminate/Bus/UniqueLock.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
use Illuminate\Contracts\Cache\Repository as Cache;
|
||||
|
||||
class UniqueLock
|
||||
{
|
||||
/**
|
||||
* The cache repository implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Cache\Repository
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Create a new unique lock manager instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Cache $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to acquire a lock for the given job.
|
||||
*
|
||||
* @param mixed $job
|
||||
* @return bool
|
||||
*/
|
||||
public function acquire($job)
|
||||
{
|
||||
$uniqueId = method_exists($job, 'uniqueId')
|
||||
? $job->uniqueId()
|
||||
: ($job->uniqueId ?? '');
|
||||
|
||||
$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();
|
||||
}
|
||||
}
|
43
vendor/laravel/framework/src/Illuminate/Bus/UpdatedBatchJobCounts.php
vendored
Normal file
43
vendor/laravel/framework/src/Illuminate/Bus/UpdatedBatchJobCounts.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Bus;
|
||||
|
||||
class UpdatedBatchJobCounts
|
||||
{
|
||||
/**
|
||||
* The number of pending jobs remaining for the batch.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $pendingJobs;
|
||||
|
||||
/**
|
||||
* The number of failed jobs that belong to the batch.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $failedJobs;
|
||||
|
||||
/**
|
||||
* Create a new batch job counts object.
|
||||
*
|
||||
* @param int $pendingJobs
|
||||
* @param int $failedJobs
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(int $pendingJobs = 0, int $failedJobs = 0)
|
||||
{
|
||||
$this->pendingJobs = $pendingJobs;
|
||||
$this->failedJobs = $failedJobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if all jobs have run exactly once.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function allJobsHaveRanExactlyOnce()
|
||||
{
|
||||
return ($this->pendingJobs - $this->failedJobs) === 0;
|
||||
}
|
||||
}
|
@@ -14,10 +14,11 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5|^8.0",
|
||||
"illuminate/contracts": "^7.0",
|
||||
"illuminate/pipeline": "^7.0",
|
||||
"illuminate/support": "^7.0"
|
||||
"php": "^7.3|^8.0",
|
||||
"illuminate/collections": "^8.0",
|
||||
"illuminate/contracts": "^8.0",
|
||||
"illuminate/pipeline": "^8.0",
|
||||
"illuminate/support": "^8.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -26,7 +27,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.x-dev"
|
||||
"dev-master": "8.x-dev"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
|
85
vendor/laravel/framework/src/Illuminate/Cache/CacheLock.php
vendored
Normal file
85
vendor/laravel/framework/src/Illuminate/Cache/CacheLock.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Cache;
|
||||
|
||||
class CacheLock extends Lock
|
||||
{
|
||||
/**
|
||||
* The cache store implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Cache\Store
|
||||
*/
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* Create a new lock instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Cache\Store $store
|
||||
* @param string $name
|
||||
* @param int $seconds
|
||||
* @param string|null $owner
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($store, $name, $seconds, $owner = null)
|
||||
{
|
||||
parent::__construct($name, $seconds, $owner);
|
||||
|
||||
$this->store = $store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to acquire the lock.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function acquire()
|
||||
{
|
||||
if (method_exists($this->store, 'add') && $this->seconds > 0) {
|
||||
return $this->store->add(
|
||||
$this->name, $this->owner, $this->seconds
|
||||
);
|
||||
}
|
||||
|
||||
if (! is_null($this->store->get($this->name))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($this->seconds > 0)
|
||||
? $this->store->put($this->name, $this->owner, $this->seconds)
|
||||
: $this->store->forever($this->name, $this->owner, $this->seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the lock.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function release()
|
||||
{
|
||||
if ($this->isOwnedByCurrentProcess()) {
|
||||
return $this->store->forget($this->name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases this lock regardless of ownership.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function forceRelease()
|
||||
{
|
||||
$this->store->forget($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner value written into the driver for this lock.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getCurrentOwner()
|
||||
{
|
||||
return $this->store->get($this->name);
|
||||
}
|
||||
}
|
@@ -199,7 +199,11 @@ class CacheManager implements FactoryContract
|
||||
|
||||
$connection = $config['connection'] ?? 'default';
|
||||
|
||||
return $this->repository(new RedisStore($redis, $this->getPrefix($config), $connection));
|
||||
$store = new RedisStore($redis, $this->getPrefix($config), $connection);
|
||||
|
||||
return $this->repository(
|
||||
$store->setLockConnection($config['lock_connection'] ?? $connection)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,15 +216,17 @@ class CacheManager implements FactoryContract
|
||||
{
|
||||
$connection = $this->app['db']->connection($config['connection'] ?? null);
|
||||
|
||||
return $this->repository(
|
||||
new DatabaseStore(
|
||||
$connection,
|
||||
$config['table'],
|
||||
$this->getPrefix($config),
|
||||
$config['lock_table'] ?? 'cache_locks',
|
||||
$config['lock_lottery'] ?? [2, 100]
|
||||
)
|
||||
$store = new DatabaseStore(
|
||||
$connection,
|
||||
$config['table'],
|
||||
$this->getPrefix($config),
|
||||
$config['lock_table'] ?? 'cache_locks',
|
||||
$config['lock_lottery'] ?? [2, 100]
|
||||
);
|
||||
|
||||
return $this->repository($store->setLockConnection(
|
||||
$this->app['db']->connection($config['lock_connection'] ?? $config['connection'] ?? null)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,21 +237,11 @@ class CacheManager implements FactoryContract
|
||||
*/
|
||||
protected function createDynamodbDriver(array $config)
|
||||
{
|
||||
$dynamoConfig = [
|
||||
'region' => $config['region'],
|
||||
'version' => 'latest',
|
||||
'endpoint' => $config['endpoint'] ?? null,
|
||||
];
|
||||
|
||||
if ($config['key'] && $config['secret']) {
|
||||
$dynamoConfig['credentials'] = Arr::only(
|
||||
$config, ['key', 'secret', 'token']
|
||||
);
|
||||
}
|
||||
$client = $this->newDynamodbClient($config);
|
||||
|
||||
return $this->repository(
|
||||
new DynamoDbStore(
|
||||
new DynamoDbClient($dynamoConfig),
|
||||
$client,
|
||||
$config['table'],
|
||||
$config['attributes']['key'] ?? 'key',
|
||||
$config['attributes']['value'] ?? 'value',
|
||||
@@ -255,6 +251,28 @@ class CacheManager implements FactoryContract
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new DynamoDb Client instance.
|
||||
*
|
||||
* @return DynamoDbClient
|
||||
*/
|
||||
protected function newDynamodbClient(array $config)
|
||||
{
|
||||
$dynamoConfig = [
|
||||
'region' => $config['region'],
|
||||
'version' => 'latest',
|
||||
'endpoint' => $config['endpoint'] ?? null,
|
||||
];
|
||||
|
||||
if (isset($config['key']) && isset($config['secret'])) {
|
||||
$dynamoConfig['credentials'] = Arr::only(
|
||||
$config, ['key', 'secret', 'token']
|
||||
);
|
||||
}
|
||||
|
||||
return new DynamoDbClient($dynamoConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new cache repository with the given implementation.
|
||||
*
|
||||
@@ -314,7 +332,11 @@ class CacheManager implements FactoryContract
|
||||
*/
|
||||
protected function getConfig($name)
|
||||
{
|
||||
return $this->app['config']["cache.stores.{$name}"];
|
||||
if (! is_null($name) && $name !== 'null') {
|
||||
return $this->app['config']["cache.stores.{$name}"];
|
||||
}
|
||||
|
||||
return ['driver' => 'null'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,6 +379,19 @@ class CacheManager implements FactoryContract
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect the given driver and remove from local cache.
|
||||
*
|
||||
* @param string|null $name
|
||||
* @return void
|
||||
*/
|
||||
public function purge($name = null)
|
||||
{
|
||||
$name = $name ?? $this->getDefaultDriver();
|
||||
|
||||
unset($this->stores[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom driver creator Closure.
|
||||
*
|
||||
|
@@ -30,6 +30,12 @@ class CacheServiceProvider extends ServiceProvider implements DeferrableProvider
|
||||
$this->app->singleton('memcached.connector', function () {
|
||||
return new MemcachedConnector;
|
||||
});
|
||||
|
||||
$this->app->singleton(RateLimiter::class, function ($app) {
|
||||
return new RateLimiter($app->make('cache')->driver(
|
||||
$app['config']->get('cache.limiter')
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +46,7 @@ class CacheServiceProvider extends ServiceProvider implements DeferrableProvider
|
||||
public function provides()
|
||||
{
|
||||
return [
|
||||
'cache', 'cache.store', 'cache.psr6', 'memcached.connector',
|
||||
'cache', 'cache.store', 'cache.psr6', 'memcached.connector', RateLimiter::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -116,7 +116,7 @@ class ClearCommand extends Command
|
||||
*/
|
||||
protected function tags()
|
||||
{
|
||||
return array_filter(explode(',', $this->option('tags')));
|
||||
return array_filter(explode(',', $this->option('tags') ?? ''));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -14,10 +14,16 @@ class CreateCacheTable extends Migration
|
||||
public function up()
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->unique();
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,5 +34,6 @@ class CreateCacheTable extends Migration
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
}
|
||||
|
@@ -136,4 +136,14 @@ class DatabaseLock extends Lock
|
||||
{
|
||||
return optional($this->connection->table($this->table)->where('key', $this->name)->first())->owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the database connection being used to manage the lock.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConnectionName()
|
||||
{
|
||||
return $this->connection->getName();
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,13 @@ class DatabaseStore implements LockProvider, Store
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The database connection instance that should be used to manage locks.
|
||||
*
|
||||
* @var \Illuminate\Database\ConnectionInterface
|
||||
*/
|
||||
protected $lockConnection;
|
||||
|
||||
/**
|
||||
* The name of the cache table.
|
||||
*
|
||||
@@ -155,8 +162,6 @@ class DatabaseStore implements LockProvider, Store
|
||||
'expiration' => $expiration,
|
||||
]) >= 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,7 +272,7 @@ class DatabaseStore implements LockProvider, Store
|
||||
public function lock($name, $seconds = 0, $owner = null)
|
||||
{
|
||||
return new DatabaseLock(
|
||||
$this->connection,
|
||||
$this->lockConnection ?? $this->connection,
|
||||
$this->lockTable,
|
||||
$this->prefix.$name,
|
||||
$seconds,
|
||||
@@ -333,6 +338,19 @@ class DatabaseStore implements LockProvider, Store
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the name of the connection that should be used to manage locks.
|
||||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function setLockConnection($connection)
|
||||
{
|
||||
$this->lockConnection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache key prefix.
|
||||
*
|
||||
|
@@ -34,9 +34,11 @@ class DynamoDbLock extends Lock
|
||||
*/
|
||||
public function acquire()
|
||||
{
|
||||
return $this->dynamo->add(
|
||||
$this->name, $this->owner, $this->seconds
|
||||
);
|
||||
if ($this->seconds > 0) {
|
||||
return $this->dynamo->add($this->name, $this->owner, $this->seconds);
|
||||
} else {
|
||||
return $this->dynamo->add($this->name, $this->owner, 86400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -525,4 +525,14 @@ class DynamoDbStore implements LockProvider, Store
|
||||
{
|
||||
$this->prefix = ! empty($prefix) ? $prefix.':' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DynamoDb Client instance.
|
||||
*
|
||||
* @return DynamoDbClient
|
||||
*/
|
||||
public function getClient()
|
||||
{
|
||||
return $this->dynamo;
|
||||
}
|
||||
}
|
||||
|
@@ -3,13 +3,16 @@
|
||||
namespace Illuminate\Cache;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Cache\LockProvider;
|
||||
use Illuminate\Contracts\Cache\Store;
|
||||
use Illuminate\Contracts\Filesystem\LockTimeoutException;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Filesystem\LockableFile;
|
||||
use Illuminate\Support\InteractsWithTime;
|
||||
|
||||
class FileStore implements Store
|
||||
class FileStore implements Store, LockProvider
|
||||
{
|
||||
use InteractsWithTime, RetrievesMultipleKeys;
|
||||
use InteractsWithTime, HasCacheLock, RetrievesMultipleKeys;
|
||||
|
||||
/**
|
||||
* The Illuminate Filesystem instance.
|
||||
@@ -75,7 +78,7 @@ class FileStore implements Store
|
||||
);
|
||||
|
||||
if ($result !== false && $result > 0) {
|
||||
$this->ensureFileHasCorrectPermissions($path);
|
||||
$this->ensurePermissionsAreCorrect($path);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -83,6 +86,45 @@ class FileStore implements Store
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an item in the cache if the key doesn't exist.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $seconds
|
||||
* @return bool
|
||||
*/
|
||||
public function add($key, $value, $seconds)
|
||||
{
|
||||
$this->ensureCacheDirectoryExists($path = $this->path($key));
|
||||
|
||||
$file = new LockableFile($path, 'c+');
|
||||
|
||||
try {
|
||||
$file->getExclusiveLock();
|
||||
} catch (LockTimeoutException $e) {
|
||||
$file->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$expire = $file->read(10);
|
||||
|
||||
if (empty($expire) || $this->currentTime() >= $expire) {
|
||||
$file->truncate()
|
||||
->write($this->expiration($seconds).serialize($value))
|
||||
->close();
|
||||
|
||||
$this->ensurePermissionsAreCorrect($path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$file->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the file cache directory if necessary.
|
||||
*
|
||||
@@ -91,18 +133,24 @@ class FileStore implements Store
|
||||
*/
|
||||
protected function ensureCacheDirectoryExists($path)
|
||||
{
|
||||
if (! $this->files->exists(dirname($path))) {
|
||||
$this->files->makeDirectory(dirname($path), 0777, true, true);
|
||||
$directory = dirname($path);
|
||||
|
||||
if (! $this->files->exists($directory)) {
|
||||
$this->files->makeDirectory($directory, 0777, true, true);
|
||||
|
||||
// We're creating two levels of directories (e.g. 7e/24), so we check them both...
|
||||
$this->ensurePermissionsAreCorrect($directory);
|
||||
$this->ensurePermissionsAreCorrect(dirname($directory));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the cache file has the correct permissions.
|
||||
* Ensure the created node has the correct permissions.
|
||||
*
|
||||
* @param string $path
|
||||
* @return void
|
||||
*/
|
||||
protected function ensureFileHasCorrectPermissions($path)
|
||||
protected function ensurePermissionsAreCorrect($path)
|
||||
{
|
||||
if (is_null($this->filePermission) ||
|
||||
intval($this->files->chmod($path), 8) == $this->filePermission) {
|
||||
|
31
vendor/laravel/framework/src/Illuminate/Cache/HasCacheLock.php
vendored
Normal file
31
vendor/laravel/framework/src/Illuminate/Cache/HasCacheLock.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Cache;
|
||||
|
||||
trait HasCacheLock
|
||||
{
|
||||
/**
|
||||
* Get a lock instance.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $seconds
|
||||
* @param string|null $owner
|
||||
* @return \Illuminate\Contracts\Cache\Lock
|
||||
*/
|
||||
public function lock($name, $seconds = 0, $owner = null)
|
||||
{
|
||||
return new CacheLock($this, $name, $seconds, $owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a lock instance using the owner identifier.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $owner
|
||||
* @return \Illuminate\Contracts\Cache\Lock
|
||||
*/
|
||||
public function restoreLock($name, $owner)
|
||||
{
|
||||
return $this->lock($name, 0, $owner);
|
||||
}
|
||||
}
|
@@ -105,7 +105,7 @@ abstract class Lock implements LockContract
|
||||
*
|
||||
* @param int $seconds
|
||||
* @param callable|null $callback
|
||||
* @return bool
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Cache\LockTimeoutException
|
||||
*/
|
||||
@@ -153,7 +153,7 @@ abstract class Lock implements LockContract
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the number of milliseconds to sleep in between blocked lock aquisition attempts.
|
||||
* Specify the number of milliseconds to sleep in between blocked lock acquisition attempts.
|
||||
*
|
||||
* @param int $milliseconds
|
||||
* @return $this
|
||||
|
46
vendor/laravel/framework/src/Illuminate/Cache/NoLock.php
vendored
Normal file
46
vendor/laravel/framework/src/Illuminate/Cache/NoLock.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Cache;
|
||||
|
||||
class NoLock extends Lock
|
||||
{
|
||||
/**
|
||||
* Attempt to acquire the lock.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function acquire()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the lock.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function release()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases this lock in disregard of ownership.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function forceRelease()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner value written into the driver for this lock.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getCurrentOwner()
|
||||
{
|
||||
return $this->owner;
|
||||
}
|
||||
}
|
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace Illuminate\Cache;
|
||||
|
||||
class NullStore extends TaggableStore
|
||||
use Illuminate\Contracts\Cache\LockProvider;
|
||||
|
||||
class NullStore extends TaggableStore implements LockProvider
|
||||
{
|
||||
use RetrievesMultipleKeys;
|
||||
|
||||
@@ -10,7 +12,7 @@ class NullStore extends TaggableStore
|
||||
* Retrieve an item from the cache by key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
* @return void
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
@@ -35,7 +37,7 @@ class NullStore extends TaggableStore
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return int|bool
|
||||
* @return bool
|
||||
*/
|
||||
public function increment($key, $value = 1)
|
||||
{
|
||||
@@ -47,7 +49,7 @@ class NullStore extends TaggableStore
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return int|bool
|
||||
* @return bool
|
||||
*/
|
||||
public function decrement($key, $value = 1)
|
||||
{
|
||||
@@ -66,6 +68,31 @@ class NullStore extends TaggableStore
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a lock instance.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $seconds
|
||||
* @param string|null $owner
|
||||
* @return \Illuminate\Contracts\Cache\Lock
|
||||
*/
|
||||
public function lock($name, $seconds = 0, $owner = null)
|
||||
{
|
||||
return new NoLock($name, $seconds, $owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a lock instance using the owner identifier.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $owner
|
||||
* @return \Illuminate\Contracts\Cache\Lock
|
||||
*/
|
||||
public function restoreLock($name, $owner)
|
||||
{
|
||||
return $this->lock($name, 0, $owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an item from the cache.
|
||||
*
|
||||
|
35
vendor/laravel/framework/src/Illuminate/Cache/PhpRedisLock.php
vendored
Normal file
35
vendor/laravel/framework/src/Illuminate/Cache/PhpRedisLock.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Cache;
|
||||
|
||||
use Illuminate\Redis\Connections\PhpRedisConnection;
|
||||
|
||||
class PhpRedisLock extends RedisLock
|
||||
{
|
||||
/**
|
||||
* Create a new phpredis lock instance.
|
||||
*
|
||||
* @param \Illuminate\Redis\Connections\PhpRedisConnection $redis
|
||||
* @param string $name
|
||||
* @param int $seconds
|
||||
* @param string|null $owner
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(PhpRedisConnection $redis, string $name, int $seconds, ?string $owner = null)
|
||||
{
|
||||
parent::__construct($redis, $name, $seconds, $owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function release()
|
||||
{
|
||||
return (bool) $this->redis->eval(
|
||||
LuaScripts::releaseLock(),
|
||||
1,
|
||||
$this->name,
|
||||
...$this->redis->pack([$this->owner])
|
||||
);
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Illuminate\Cache;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Cache\Repository as Cache;
|
||||
use Illuminate\Support\InteractsWithTime;
|
||||
|
||||
@@ -16,6 +17,13 @@ class RateLimiter
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The configured limit object resolvers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $limiters = [];
|
||||
|
||||
/**
|
||||
* Create a new rate limiter instance.
|
||||
*
|
||||
@@ -27,6 +35,51 @@ class RateLimiter
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a named limiter configuration.
|
||||
*
|
||||
* @param string $name
|
||||
* @param \Closure $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function for(string $name, Closure $callback)
|
||||
{
|
||||
$this->limiters[$name] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the given named rate limiter.
|
||||
*
|
||||
* @param string $name
|
||||
* @return \Closure
|
||||
*/
|
||||
public function limiter(string $name)
|
||||
{
|
||||
return $this->limiters[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to execute a callback if it's not limited.
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $maxAttempts
|
||||
* @param \Closure $callback
|
||||
* @param int $decaySeconds
|
||||
* @return mixed
|
||||
*/
|
||||
public function attempt($key, $maxAttempts, Closure $callback, $decaySeconds = 60)
|
||||
{
|
||||
if ($this->tooManyAttempts($key, $maxAttempts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return tap($callback() ?: true, function () use ($key, $decaySeconds) {
|
||||
$this->hit($key, $decaySeconds);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given key has been "accessed" too many times.
|
||||
*
|
||||
@@ -36,6 +89,8 @@ class RateLimiter
|
||||
*/
|
||||
public function tooManyAttempts($key, $maxAttempts)
|
||||
{
|
||||
$key = $this->cleanRateLimiterKey($key);
|
||||
|
||||
if ($this->attempts($key) >= $maxAttempts) {
|
||||
if ($this->cache->has($key.':timer')) {
|
||||
return true;
|
||||
@@ -56,6 +111,8 @@ class RateLimiter
|
||||
*/
|
||||
public function hit($key, $decaySeconds = 60)
|
||||
{
|
||||
$key = $this->cleanRateLimiterKey($key);
|
||||
|
||||
$this->cache->add(
|
||||
$key.':timer', $this->availableAt($decaySeconds), $decaySeconds
|
||||
);
|
||||
@@ -79,6 +136,8 @@ class RateLimiter
|
||||
*/
|
||||
public function attempts($key)
|
||||
{
|
||||
$key = $this->cleanRateLimiterKey($key);
|
||||
|
||||
return $this->cache->get($key, 0);
|
||||
}
|
||||
|
||||
@@ -90,9 +149,27 @@ class RateLimiter
|
||||
*/
|
||||
public function resetAttempts($key)
|
||||
{
|
||||
$key = $this->cleanRateLimiterKey($key);
|
||||
|
||||
return $this->cache->forget($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of retries left for the given key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $maxAttempts
|
||||
* @return int
|
||||
*/
|
||||
public function remaining($key, $maxAttempts)
|
||||
{
|
||||
$key = $this->cleanRateLimiterKey($key);
|
||||
|
||||
$attempts = $this->attempts($key);
|
||||
|
||||
return $maxAttempts - $attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of retries left for the given key.
|
||||
*
|
||||
@@ -102,9 +179,7 @@ class RateLimiter
|
||||
*/
|
||||
public function retriesLeft($key, $maxAttempts)
|
||||
{
|
||||
$attempts = $this->attempts($key);
|
||||
|
||||
return $maxAttempts - $attempts;
|
||||
return $this->remaining($key, $maxAttempts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,6 +190,8 @@ class RateLimiter
|
||||
*/
|
||||
public function clear($key)
|
||||
{
|
||||
$key = $this->cleanRateLimiterKey($key);
|
||||
|
||||
$this->resetAttempts($key);
|
||||
|
||||
$this->cache->forget($key.':timer');
|
||||
@@ -128,6 +205,19 @@ class RateLimiter
|
||||
*/
|
||||
public function availableIn($key)
|
||||
{
|
||||
return $this->cache->get($key.':timer') - $this->currentTime();
|
||||
$key = $this->cleanRateLimiterKey($key);
|
||||
|
||||
return max(0, $this->cache->get($key.':timer') - $this->currentTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the rate limiter key from unicode characters.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function cleanRateLimiterKey($key)
|
||||
{
|
||||
return preg_replace('/&([a-z])[a-z]+;/i', '$1', htmlentities($key));
|
||||
}
|
||||
}
|
||||
|
18
vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/GlobalLimit.php
vendored
Normal file
18
vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/GlobalLimit.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Cache\RateLimiting;
|
||||
|
||||
class GlobalLimit extends Limit
|
||||
{
|
||||
/**
|
||||
* Create a new limit instance.
|
||||
*
|
||||
* @param int $maxAttempts
|
||||
* @param int $decayMinutes
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(int $maxAttempts, int $decayMinutes = 1)
|
||||
{
|
||||
parent::__construct('', $maxAttempts, $decayMinutes);
|
||||
}
|
||||
}
|
132
vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/Limit.php
vendored
Normal file
132
vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/Limit.php
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Cache\RateLimiting;
|
||||
|
||||
class Limit
|
||||
{
|
||||
/**
|
||||
* The rate limit signature key.
|
||||
*
|
||||
* @var mixed|string
|
||||
*/
|
||||
public $key;
|
||||
|
||||
/**
|
||||
* The maximum number of attempts allowed within the given number of minutes.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $maxAttempts;
|
||||
|
||||
/**
|
||||
* The number of minutes until the rate limit is reset.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $decayMinutes;
|
||||
|
||||
/**
|
||||
* The response generator callback.
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
public $responseCallback;
|
||||
|
||||
/**
|
||||
* Create a new limit instance.
|
||||
*
|
||||
* @param mixed|string $key
|
||||
* @param int $maxAttempts
|
||||
* @param int $decayMinutes
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($key = '', int $maxAttempts = 60, int $decayMinutes = 1)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->maxAttempts = $maxAttempts;
|
||||
$this->decayMinutes = $decayMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new rate limit.
|
||||
*
|
||||
* @param int $maxAttempts
|
||||
* @return static
|
||||
*/
|
||||
public static function perMinute($maxAttempts)
|
||||
{
|
||||
return new static('', $maxAttempts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new rate limit using minutes as decay time.
|
||||
*
|
||||
* @param int $decayMinutes
|
||||
* @param int $maxAttempts
|
||||
* @return static
|
||||
*/
|
||||
public static function perMinutes($decayMinutes, $maxAttempts)
|
||||
{
|
||||
return new static('', $maxAttempts, $decayMinutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new rate limit using hours as decay time.
|
||||
*
|
||||
* @param int $maxAttempts
|
||||
* @param int $decayHours
|
||||
* @return static
|
||||
*/
|
||||
public static function perHour($maxAttempts, $decayHours = 1)
|
||||
{
|
||||
return new static('', $maxAttempts, 60 * $decayHours);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new rate limit using days as decay time.
|
||||
*
|
||||
* @param int $maxAttempts
|
||||
* @param int $decayDays
|
||||
* @return static
|
||||
*/
|
||||
public static function perDay($maxAttempts, $decayDays = 1)
|
||||
{
|
||||
return new static('', $maxAttempts, 60 * 24 * $decayDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new unlimited rate limit.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function none()
|
||||
{
|
||||
return new Unlimited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key of the rate limit.
|
||||
*
|
||||
* @param string $key
|
||||
* @return $this
|
||||
*/
|
||||
public function by($key)
|
||||
{
|
||||
$this->key = $key;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback that should generate the response when the limit is exceeded.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function response(callable $callback)
|
||||
{
|
||||
$this->responseCallback = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
16
vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/Unlimited.php
vendored
Normal file
16
vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/Unlimited.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Cache\RateLimiting;
|
||||
|
||||
class Unlimited extends GlobalLimit
|
||||
{
|
||||
/**
|
||||
* Create a new limit instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(PHP_INT_MAX);
|
||||
}
|
||||
}
|
@@ -70,4 +70,14 @@ class RedisLock extends Lock
|
||||
{
|
||||
return $this->redis->get($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the Redis connection being used to manage the lock.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConnectionName()
|
||||
{
|
||||
return $this->redis->getName();
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ namespace Illuminate\Cache;
|
||||
|
||||
use Illuminate\Contracts\Cache\LockProvider;
|
||||
use Illuminate\Contracts\Redis\Factory as Redis;
|
||||
use Illuminate\Redis\Connections\PhpRedisConnection;
|
||||
|
||||
class RedisStore extends TaggableStore implements LockProvider
|
||||
{
|
||||
@@ -22,12 +23,19 @@ class RedisStore extends TaggableStore implements LockProvider
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* The Redis connection that should be used.
|
||||
* The Redis connection instance that should be used to manage locks.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The name of the connection that should be used for locks.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $lockConnection;
|
||||
|
||||
/**
|
||||
* Create a new Redis store.
|
||||
*
|
||||
@@ -181,7 +189,15 @@ class RedisStore extends TaggableStore implements LockProvider
|
||||
*/
|
||||
public function lock($name, $seconds = 0, $owner = null)
|
||||
{
|
||||
return new RedisLock($this->connection(), $this->prefix.$name, $seconds, $owner);
|
||||
$lockName = $this->prefix.$name;
|
||||
|
||||
$lockConnection = $this->lockConnection();
|
||||
|
||||
if ($lockConnection instanceof PhpRedisConnection) {
|
||||
return new PhpRedisLock($lockConnection, $lockName, $seconds, $owner);
|
||||
}
|
||||
|
||||
return new RedisLock($lockConnection, $lockName, $seconds, $owner);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,7 +259,17 @@ class RedisStore extends TaggableStore implements LockProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection name to be used.
|
||||
* Get the Redis connection instance that should be used to manage locks.
|
||||
*
|
||||
* @return \Illuminate\Redis\Connections\Connection
|
||||
*/
|
||||
public function lockConnection()
|
||||
{
|
||||
return $this->redis->connection($this->lockConnection ?? $this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the name of the connection that should be used to store data.
|
||||
*
|
||||
* @param string $connection
|
||||
* @return void
|
||||
@@ -253,6 +279,19 @@ class RedisStore extends TaggableStore implements LockProvider
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the name of the connection that should be used to manage locks.
|
||||
*
|
||||
* @param string $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function setLockConnection($connection)
|
||||
{
|
||||
$this->lockConnection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Redis database instance.
|
||||
*
|
||||
|
@@ -10,6 +10,7 @@ class RedisTaggedCache extends TaggedCache
|
||||
* @var string
|
||||
*/
|
||||
const REFERENCE_KEY_FOREVER = 'forever_ref';
|
||||
|
||||
/**
|
||||
* Standard reference key.
|
||||
*
|
||||
@@ -41,13 +42,13 @@ class RedisTaggedCache extends TaggedCache
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @return int|bool
|
||||
*/
|
||||
public function increment($key, $value = 1)
|
||||
{
|
||||
$this->pushStandardKeys($this->tags->getNamespace(), $key);
|
||||
|
||||
parent::increment($key, $value);
|
||||
return parent::increment($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,13 +56,13 @@ class RedisTaggedCache extends TaggedCache
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @return int|bool
|
||||
*/
|
||||
public function decrement($key, $value = 1)
|
||||
{
|
||||
$this->pushStandardKeys($this->tags->getNamespace(), $key);
|
||||
|
||||
parent::decrement($key, $value);
|
||||
return parent::decrement($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +89,9 @@ class RedisTaggedCache extends TaggedCache
|
||||
$this->deleteForeverKeys();
|
||||
$this->deleteStandardKeys();
|
||||
|
||||
return parent::flush();
|
||||
$this->tags->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,13 +178,26 @@ class RedisTaggedCache extends TaggedCache
|
||||
*/
|
||||
protected function deleteValues($referenceKey)
|
||||
{
|
||||
$values = array_unique($this->store->connection()->smembers($referenceKey));
|
||||
$cursor = $defaultCursorValue = '0';
|
||||
|
||||
if (count($values) > 0) {
|
||||
foreach (array_chunk($values, 1000) as $valuesChunk) {
|
||||
do {
|
||||
[$cursor, $valuesChunk] = $this->store->connection()->sscan(
|
||||
$referenceKey, $cursor, ['match' => '*', 'count' => 1000]
|
||||
);
|
||||
|
||||
// PhpRedis client returns false if set does not exist or empty. Array destruction
|
||||
// on false stores null in each variable. If valuesChunk is null, it means that
|
||||
// there were not results from the previously executed "sscan" Redis command.
|
||||
if (is_null($valuesChunk)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$valuesChunk = array_unique($valuesChunk);
|
||||
|
||||
if (count($valuesChunk) > 0) {
|
||||
$this->store->connection()->del(...$valuesChunk);
|
||||
}
|
||||
}
|
||||
} while (((string) $cursor) !== $defaultCursorValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -131,6 +131,8 @@ class Repository implements ArrayAccess, CacheContract
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
@@ -219,6 +221,8 @@ class Repository implements ArrayAccess, CacheContract
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
@@ -276,6 +280,8 @@ class Repository implements ArrayAccess, CacheContract
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
@@ -292,8 +298,12 @@ class Repository implements ArrayAccess, CacheContract
|
||||
*/
|
||||
public function add($key, $value, $ttl = null)
|
||||
{
|
||||
$seconds = null;
|
||||
|
||||
if ($ttl !== null) {
|
||||
if ($this->getSeconds($ttl) <= 0) {
|
||||
$seconds = $this->getSeconds($ttl);
|
||||
|
||||
if ($seconds <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -301,8 +311,6 @@ class Repository implements ArrayAccess, CacheContract
|
||||
// has a chance to override this logic. Some drivers better support the way
|
||||
// this operation should work with a total "atomic" implementation of it.
|
||||
if (method_exists($this->store, 'add')) {
|
||||
$seconds = $this->getSeconds($ttl);
|
||||
|
||||
return $this->store->add(
|
||||
$this->itemKey($key), $value, $seconds
|
||||
);
|
||||
@@ -313,7 +321,7 @@ class Repository implements ArrayAccess, CacheContract
|
||||
// so it exists for subsequent requests. Then, we will return true so it is
|
||||
// easy to know if the value gets added. Otherwise, we will return false.
|
||||
if (is_null($this->get($key))) {
|
||||
return $this->put($key, $value, $ttl);
|
||||
return $this->put($key, $value, $seconds);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -365,7 +373,7 @@ class Repository implements ArrayAccess, CacheContract
|
||||
* Get an item from the cache, or execute the given Closure and store the result.
|
||||
*
|
||||
* @param string $key
|
||||
* @param \DateTimeInterface|\DateInterval|int|null $ttl
|
||||
* @param \Closure|\DateTimeInterface|\DateInterval|int|null $ttl
|
||||
* @param \Closure $callback
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -380,7 +388,7 @@ class Repository implements ArrayAccess, CacheContract
|
||||
return $value;
|
||||
}
|
||||
|
||||
$this->put($key, $value = $callback(), $ttl);
|
||||
$this->put($key, $value = $callback(), value($ttl));
|
||||
|
||||
return $value;
|
||||
}
|
||||
@@ -437,6 +445,8 @@ class Repository implements ArrayAccess, CacheContract
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
@@ -445,6 +455,8 @@ class Repository implements ArrayAccess, CacheContract
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
@@ -461,6 +473,8 @@ class Repository implements ArrayAccess, CacheContract
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
@@ -477,7 +491,7 @@ class Repository implements ArrayAccess, CacheContract
|
||||
*/
|
||||
public function tags($names)
|
||||
{
|
||||
if (! method_exists($this->store, 'tags')) {
|
||||
if (! $this->supportsTags()) {
|
||||
throw new BadMethodCallException('This cache store does not support tagging.');
|
||||
}
|
||||
|
||||
@@ -501,6 +515,33 @@ class Repository implements ArrayAccess, CacheContract
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the number of seconds for the given TTL.
|
||||
*
|
||||
* @param \DateTimeInterface|\DateInterval|int $ttl
|
||||
* @return int
|
||||
*/
|
||||
protected function getSeconds($ttl)
|
||||
{
|
||||
$duration = $this->parseDateInterval($ttl);
|
||||
|
||||
if ($duration instanceof DateTimeInterface) {
|
||||
$duration = Carbon::now()->diffInRealSeconds($duration, false);
|
||||
}
|
||||
|
||||
return (int) ($duration > 0 ? $duration : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current store supports tags.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportsTags()
|
||||
{
|
||||
return method_exists($this->store, 'tags');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default cache time.
|
||||
*
|
||||
@@ -537,7 +578,7 @@ class Repository implements ArrayAccess, CacheContract
|
||||
/**
|
||||
* Fire an event for this cache instance.
|
||||
*
|
||||
* @param string $event
|
||||
* @param object|string $event
|
||||
* @return void
|
||||
*/
|
||||
protected function event($event)
|
||||
@@ -574,6 +615,7 @@ class Repository implements ArrayAccess, CacheContract
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($key)
|
||||
{
|
||||
return $this->has($key);
|
||||
@@ -585,6 +627,7 @@ class Repository implements ArrayAccess, CacheContract
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
@@ -597,6 +640,7 @@ class Repository implements ArrayAccess, CacheContract
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
$this->put($key, $value, $this->default);
|
||||
@@ -608,28 +652,12 @@ class Repository implements ArrayAccess, CacheContract
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($key)
|
||||
{
|
||||
$this->forget($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the number of seconds for the given TTL.
|
||||
*
|
||||
* @param \DateTimeInterface|\DateInterval|int $ttl
|
||||
* @return int
|
||||
*/
|
||||
protected function getSeconds($ttl)
|
||||
{
|
||||
$duration = $this->parseDateInterval($ttl);
|
||||
|
||||
if ($duration instanceof DateTimeInterface) {
|
||||
$duration = Carbon::now()->diffInRealSeconds($duration, false);
|
||||
}
|
||||
|
||||
return (int) $duration > 0 ? $duration : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dynamic calls into macros or pass missing methods to the store.
|
||||
*
|
||||
|
@@ -16,8 +16,12 @@ trait RetrievesMultipleKeys
|
||||
{
|
||||
$return = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$return[$key] = $this->get($key);
|
||||
$keys = collect($keys)->mapWithKeys(function ($value, $key) {
|
||||
return [is_string($key) ? $key : $value => is_string($key) ? $value : null];
|
||||
})->all();
|
||||
|
||||
foreach ($keys as $key => $default) {
|
||||
$return[$key] = $this->get($key, $default);
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
@@ -56,6 +56,26 @@ class TagSet
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all the tags in the set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
array_walk($this->names, [$this, 'flushTag']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the tag from the cache.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function flushTag($name)
|
||||
{
|
||||
$this->store->forget($this->tagKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a unique namespace that changes when any of the tags are flushed.
|
||||
*
|
||||
|
@@ -52,11 +52,11 @@ class TaggedCache extends Repository
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @return int|bool
|
||||
*/
|
||||
public function increment($key, $value = 1)
|
||||
{
|
||||
$this->store->increment($this->itemKey($key), $value);
|
||||
return $this->store->increment($this->itemKey($key), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,11 +64,11 @@ class TaggedCache extends Repository
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @return int|bool
|
||||
*/
|
||||
public function decrement($key, $value = 1)
|
||||
{
|
||||
$this->store->decrement($this->itemKey($key), $value);
|
||||
return $this->store->decrement($this->itemKey($key), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +105,7 @@ class TaggedCache extends Repository
|
||||
/**
|
||||
* Fire an event for this cache instance.
|
||||
*
|
||||
* @param string $event
|
||||
* @param \Illuminate\Cache\Events\CacheEvent $event
|
||||
* @return void
|
||||
*/
|
||||
protected function event($event)
|
||||
|
@@ -14,9 +14,14 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5|^8.0",
|
||||
"illuminate/contracts": "^7.0",
|
||||
"illuminate/support": "^7.0"
|
||||
"php": "^7.3|^8.0",
|
||||
"illuminate/collections": "^8.0",
|
||||
"illuminate/contracts": "^8.0",
|
||||
"illuminate/macroable": "^8.0",
|
||||
"illuminate/support": "^8.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/simple-cache-implementation": "1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -25,15 +30,15 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.x-dev"
|
||||
"dev-master": "8.x-dev"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"ext-memcached": "Required to use the memcache cache driver.",
|
||||
"illuminate/database": "Required to use the database cache driver (^7.0).",
|
||||
"illuminate/filesystem": "Required to use the file cache driver (^7.0).",
|
||||
"illuminate/redis": "Required to use the redis cache driver (^7.0).",
|
||||
"symfony/cache": "Required to PSR-6 cache bridge (^5.0)."
|
||||
"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)."
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
|
111
vendor/laravel/framework/src/Illuminate/Support/Arr.php → vendor/laravel/framework/src/Illuminate/Collections/Arr.php
vendored
Executable file → Normal file
111
vendor/laravel/framework/src/Illuminate/Support/Arr.php → vendor/laravel/framework/src/Illuminate/Collections/Arr.php
vendored
Executable file → Normal file
@@ -121,6 +121,23 @@ class Arr
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a flatten "dot" notation array into an expanded array.
|
||||
*
|
||||
* @param iterable $array
|
||||
* @return array
|
||||
*/
|
||||
public static function undot($array)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
static::set($results, $key, $value);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the given array except for a specified array of keys.
|
||||
*
|
||||
@@ -144,6 +161,10 @@ class Arr
|
||||
*/
|
||||
public static function exists($array, $key)
|
||||
{
|
||||
if ($array instanceof Enumerable) {
|
||||
return $array->has($key);
|
||||
}
|
||||
|
||||
if ($array instanceof ArrayAccess) {
|
||||
return $array->offsetExists($key);
|
||||
}
|
||||
@@ -389,6 +410,19 @@ class Arr
|
||||
return array_keys($keys) !== $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an array is a list.
|
||||
*
|
||||
* An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between.
|
||||
*
|
||||
* @param array $array
|
||||
* @return bool
|
||||
*/
|
||||
public static function isList($array)
|
||||
{
|
||||
return ! self::isAssoc($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subset of the items from the given array.
|
||||
*
|
||||
@@ -405,7 +439,7 @@ class Arr
|
||||
* Pluck an array of values from an array.
|
||||
*
|
||||
* @param iterable $array
|
||||
* @param string|array $value
|
||||
* @param string|array|int|null $value
|
||||
* @param string|array|null $key
|
||||
* @return array
|
||||
*/
|
||||
@@ -463,7 +497,7 @@ class Arr
|
||||
*/
|
||||
public static function prepend($array, $value, $key = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
if (func_num_args() == 2) {
|
||||
array_unshift($array, $value);
|
||||
} else {
|
||||
$array = [$key => $value] + $array;
|
||||
@@ -476,7 +510,7 @@ class Arr
|
||||
* Get a value from the array, and remove it.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $key
|
||||
* @param string|int $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -489,16 +523,28 @@ class Arr
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the array into a query string.
|
||||
*
|
||||
* @param array $array
|
||||
* @return string
|
||||
*/
|
||||
public static function query($array)
|
||||
{
|
||||
return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one or a specified number of random values from an array.
|
||||
*
|
||||
* @param array $array
|
||||
* @param int|null $number
|
||||
* @param bool|false $preserveKeys
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function random($array, $number = null)
|
||||
public static function random($array, $number = null, $preserveKeys = false)
|
||||
{
|
||||
$requested = is_null($number) ? 1 : $number;
|
||||
|
||||
@@ -522,8 +568,14 @@ class Arr
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach ((array) $keys as $key) {
|
||||
$results[] = $array[$key];
|
||||
if ($preserveKeys) {
|
||||
foreach ((array) $keys as $key) {
|
||||
$results[$key] = $array[$key];
|
||||
}
|
||||
} else {
|
||||
foreach ((array) $keys as $key) {
|
||||
$results[] = $array[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
@@ -593,7 +645,7 @@ class Arr
|
||||
* Sort the array using the given callback or "dot" notation.
|
||||
*
|
||||
* @param array $array
|
||||
* @param callable|string|null $callback
|
||||
* @param callable|array|string|null $callback
|
||||
* @return array
|
||||
*/
|
||||
public static function sort($array, $callback = null)
|
||||
@@ -605,34 +657,52 @@ class Arr
|
||||
* Recursively sort an array by keys and values.
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $options
|
||||
* @param bool $descending
|
||||
* @return array
|
||||
*/
|
||||
public static function sortRecursive($array)
|
||||
public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false)
|
||||
{
|
||||
foreach ($array as &$value) {
|
||||
if (is_array($value)) {
|
||||
$value = static::sortRecursive($value);
|
||||
$value = static::sortRecursive($value, $options, $descending);
|
||||
}
|
||||
}
|
||||
|
||||
if (static::isAssoc($array)) {
|
||||
ksort($array);
|
||||
$descending
|
||||
? krsort($array, $options)
|
||||
: ksort($array, $options);
|
||||
} else {
|
||||
sort($array);
|
||||
$descending
|
||||
? rsort($array, $options)
|
||||
: sort($array, $options);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the array into a query string.
|
||||
* Conditionally compile classes from an array into a CSS class list.
|
||||
*
|
||||
* @param array $array
|
||||
* @return string
|
||||
*/
|
||||
public static function query($array)
|
||||
public static function toCssClasses($array)
|
||||
{
|
||||
return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
|
||||
$classList = static::wrap($array);
|
||||
|
||||
$classes = [];
|
||||
|
||||
foreach ($classList as $class => $constraint) {
|
||||
if (is_numeric($class)) {
|
||||
$classes[] = $constraint;
|
||||
} elseif ($constraint) {
|
||||
$classes[] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' ', $classes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -647,6 +717,19 @@ class Arr
|
||||
return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter items where the value is not null.
|
||||
*
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
public static function whereNotNull($array)
|
||||
{
|
||||
return static::where($array, function ($value) {
|
||||
return ! is_null($value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given value is not an array and not null, wrap it in one.
|
||||
*
|
@@ -4,11 +4,12 @@ namespace Illuminate\Support;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString;
|
||||
use Illuminate\Support\Traits\EnumeratesValues;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use stdClass;
|
||||
|
||||
class Collection implements ArrayAccess, Enumerable
|
||||
class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
|
||||
{
|
||||
use EnumeratesValues, Macroable;
|
||||
|
||||
@@ -31,23 +32,15 @@ class Collection implements ArrayAccess, Enumerable
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new collection by invoking the callback a given amount of times.
|
||||
* Create a collection with the given range.
|
||||
*
|
||||
* @param int $number
|
||||
* @param callable|null $callback
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @return static
|
||||
*/
|
||||
public static function times($number, callable $callback = null)
|
||||
public static function range($from, $to)
|
||||
{
|
||||
if ($number < 1) {
|
||||
return new static;
|
||||
}
|
||||
|
||||
if (is_null($callback)) {
|
||||
return new static(range(1, $number));
|
||||
}
|
||||
|
||||
return (new static(range(1, $number)))->map($callback);
|
||||
return new static(range($from, $to));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +128,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
|
||||
$collection = isset($key) ? $this->pluck($key) : $this;
|
||||
|
||||
$counts = new self;
|
||||
$counts = new static;
|
||||
|
||||
$collection->each(function ($value) use ($counts) {
|
||||
$counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
|
||||
@@ -183,6 +176,19 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return $this->contains($this->operatorForWhere(...func_get_args()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an item is not contained in the collection.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $operator
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function doesntContain($key, $operator = null, $value = null)
|
||||
{
|
||||
return ! $this->contains(...func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross join with the given lists, returning all possible permutations.
|
||||
*
|
||||
@@ -268,7 +274,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
/**
|
||||
* Retrieve duplicate items from the collection.
|
||||
*
|
||||
* @param callable|null $callback
|
||||
* @param callable|string|null $callback
|
||||
* @param bool $strict
|
||||
* @return static
|
||||
*/
|
||||
@@ -296,7 +302,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
/**
|
||||
* Retrieve duplicate items from the collection using strict comparison.
|
||||
*
|
||||
* @param callable|null $callback
|
||||
* @param callable|string|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function duplicatesStrict($callback = null)
|
||||
@@ -391,7 +397,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
/**
|
||||
* Remove an item from the collection by key.
|
||||
*
|
||||
* @param string|array $keys
|
||||
* @param string|int|array $keys
|
||||
* @return $this
|
||||
*/
|
||||
public function forget($keys)
|
||||
@@ -412,13 +418,31 @@ class Collection implements ArrayAccess, Enumerable
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if ($this->offsetExists($key)) {
|
||||
if (array_key_exists($key, $this->items)) {
|
||||
return $this->items[$key];
|
||||
}
|
||||
|
||||
return value($default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from the collection by key or add it to collection if it does not exist.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOrPut($key, $value)
|
||||
{
|
||||
if (array_key_exists($key, $this->items)) {
|
||||
return $this->items[$key];
|
||||
}
|
||||
|
||||
$this->offsetSet($key, $value = value($value));
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group an associative array by a field or using a callback.
|
||||
*
|
||||
@@ -501,7 +525,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
$keys = is_array($key) ? $key : func_get_args();
|
||||
|
||||
foreach ($keys as $value) {
|
||||
if (! $this->offsetExists($value)) {
|
||||
if (! array_key_exists($value, $this->items)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -509,6 +533,29 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any of the keys exist in the collection.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAny($key)
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$keys = is_array($key) ? $key : func_get_args();
|
||||
|
||||
foreach ($keys as $value) {
|
||||
if ($this->has($value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate values of a given key as a string.
|
||||
*
|
||||
@@ -520,11 +567,11 @@ class Collection implements ArrayAccess, Enumerable
|
||||
{
|
||||
$first = $this->first();
|
||||
|
||||
if (is_array($first) || is_object($first)) {
|
||||
return implode($glue, $this->pluck($value)->all());
|
||||
if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
|
||||
return implode($glue ?? '', $this->pluck($value)->all());
|
||||
}
|
||||
|
||||
return implode($value, $this->items);
|
||||
return implode($value ?? '', $this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,6 +608,16 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return empty($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the collection contains a single item.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function containsOneItem()
|
||||
{
|
||||
return $this->count() === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join all items from the collection using a string. The final items can use a separate glue string.
|
||||
*
|
||||
@@ -616,7 +673,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
/**
|
||||
* Get the values of a given key.
|
||||
*
|
||||
* @param string|array $value
|
||||
* @param string|array|int|null $value
|
||||
* @param string|null $key
|
||||
* @return static
|
||||
*/
|
||||
@@ -749,8 +806,8 @@ class Collection implements ArrayAccess, Enumerable
|
||||
|
||||
$position = 0;
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
if ($position % $step === $offset) {
|
||||
foreach ($this->slice($offset)->items as $item) {
|
||||
if ($position % $step === 0) {
|
||||
$new[] = $item;
|
||||
}
|
||||
|
||||
@@ -782,13 +839,30 @@ class Collection implements ArrayAccess, Enumerable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and remove the last item from the collection.
|
||||
* Get and remove the last N items from the collection.
|
||||
*
|
||||
* @param int $count
|
||||
* @return mixed
|
||||
*/
|
||||
public function pop()
|
||||
public function pop($count = 1)
|
||||
{
|
||||
return array_pop($this->items);
|
||||
if ($count === 1) {
|
||||
return array_pop($this->items);
|
||||
}
|
||||
|
||||
if ($this->isEmpty()) {
|
||||
return new static;
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
$collectionCount = $this->count();
|
||||
|
||||
foreach (range(1, min($count, $collectionCount)) as $item) {
|
||||
array_push($results, array_pop($this->items));
|
||||
}
|
||||
|
||||
return new static($results);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -800,7 +874,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
*/
|
||||
public function prepend($value, $key = null)
|
||||
{
|
||||
$this->items = Arr::prepend($this->items, $value, $key);
|
||||
$this->items = Arr::prepend($this->items, ...func_get_args());
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -880,18 +954,6 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return new static(Arr::random($this->items, $number));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the collection to a single value.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $initial
|
||||
* @return mixed
|
||||
*/
|
||||
public function reduce(callable $callback, $initial = null)
|
||||
{
|
||||
return array_reduce($this->items, $callback, $initial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the collection items with the given items.
|
||||
*
|
||||
@@ -947,13 +1009,30 @@ class Collection implements ArrayAccess, Enumerable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and remove the first item from the collection.
|
||||
* Get and remove the first N items from the collection.
|
||||
*
|
||||
* @param int $count
|
||||
* @return mixed
|
||||
*/
|
||||
public function shift()
|
||||
public function shift($count = 1)
|
||||
{
|
||||
return array_shift($this->items);
|
||||
if ($count === 1) {
|
||||
return array_shift($this->items);
|
||||
}
|
||||
|
||||
if ($this->isEmpty()) {
|
||||
return new static;
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
$collectionCount = $this->count();
|
||||
|
||||
foreach (range(1, min($count, $collectionCount)) as $item) {
|
||||
array_push($results, array_shift($this->items));
|
||||
}
|
||||
|
||||
return new static($results);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -967,6 +1046,22 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return new static(Arr::shuffle($this->items, $seed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create chunks representing a "sliding window" view of the items in the collection.
|
||||
*
|
||||
* @param int $size
|
||||
* @param int $step
|
||||
* @return static
|
||||
*/
|
||||
public function sliding($size = 2, $step = 1)
|
||||
{
|
||||
$chunks = floor(($this->count() - $size) / $step) + 1;
|
||||
|
||||
return static::times($chunks, function ($number) use ($size, $step) {
|
||||
return $this->slice(($number - 1) * $step, $size);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the first {$count} items.
|
||||
*
|
||||
@@ -1049,6 +1144,74 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a collection into a certain number of groups, and fill the first groups completely.
|
||||
*
|
||||
* @param int $numberOfGroups
|
||||
* @return static
|
||||
*/
|
||||
public function splitIn($numberOfGroups)
|
||||
{
|
||||
return $this->chunk(ceil($this->count() / $numberOfGroups));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $operator
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Illuminate\Support\ItemNotFoundException
|
||||
* @throws \Illuminate\Support\MultipleItemsFoundException
|
||||
*/
|
||||
public function sole($key = null, $operator = null, $value = null)
|
||||
{
|
||||
$filter = func_num_args() > 1
|
||||
? $this->operatorForWhere(...func_get_args())
|
||||
: $key;
|
||||
|
||||
$items = $this->when($filter)->filter($filter);
|
||||
|
||||
if ($items->isEmpty()) {
|
||||
throw new ItemNotFoundException;
|
||||
}
|
||||
|
||||
if ($items->count() > 1) {
|
||||
throw new MultipleItemsFoundException;
|
||||
}
|
||||
|
||||
return $items->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first item in the collection but throw an exception if no matching items exist.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $operator
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Illuminate\Support\ItemNotFoundException
|
||||
*/
|
||||
public function firstOrFail($key = null, $operator = null, $value = null)
|
||||
{
|
||||
$filter = func_num_args() > 1
|
||||
? $this->operatorForWhere(...func_get_args())
|
||||
: $key;
|
||||
|
||||
$placeholder = new stdClass();
|
||||
|
||||
$item = $this->first($filter, $placeholder);
|
||||
|
||||
if ($item === $placeholder) {
|
||||
throw new ItemNotFoundException;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk the collection into chunks of the given size.
|
||||
*
|
||||
@@ -1070,6 +1233,19 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return new static($chunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk the collection into chunks with a callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return static
|
||||
*/
|
||||
public function chunkWhile(callable $callback)
|
||||
{
|
||||
return new static(
|
||||
$this->lazy()->chunkWhile($callback)->mapInto(static::class)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort through each item with a callback.
|
||||
*
|
||||
@@ -1082,7 +1258,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
|
||||
$callback && is_callable($callback)
|
||||
? uasort($items, $callback)
|
||||
: asort($items, $callback);
|
||||
: asort($items, $callback ?? SORT_REGULAR);
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
@@ -1105,20 +1281,24 @@ class Collection implements ArrayAccess, Enumerable
|
||||
/**
|
||||
* Sort the collection using the given callback.
|
||||
*
|
||||
* @param callable|string $callback
|
||||
* @param callable|array|string $callback
|
||||
* @param int $options
|
||||
* @param bool $descending
|
||||
* @return static
|
||||
*/
|
||||
public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
|
||||
{
|
||||
if (is_array($callback) && ! is_callable($callback)) {
|
||||
return $this->sortByMany($callback);
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
$callback = $this->valueRetriever($callback);
|
||||
|
||||
// First we will loop through the items and get the comparator from a callback
|
||||
// function which we were given. Then, we will sort the returned values and
|
||||
// and grab the corresponding values for the sorted keys from this array.
|
||||
// grab all the corresponding values for the sorted keys from this array.
|
||||
foreach ($this->items as $key => $value) {
|
||||
$results[$key] = $callback($value, $key);
|
||||
}
|
||||
@@ -1136,6 +1316,50 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return new static($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the collection using multiple comparisons.
|
||||
*
|
||||
* @param array $comparisons
|
||||
* @return static
|
||||
*/
|
||||
protected function sortByMany(array $comparisons = [])
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
usort($items, function ($a, $b) use ($comparisons) {
|
||||
foreach ($comparisons as $comparison) {
|
||||
$comparison = Arr::wrap($comparison);
|
||||
|
||||
$prop = $comparison[0];
|
||||
|
||||
$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 {
|
||||
$values = [data_get($a, $prop), data_get($b, $prop)];
|
||||
|
||||
if (! $ascending) {
|
||||
$values = array_reverse($values);
|
||||
}
|
||||
|
||||
$result = $values[0] <=> $values[1];
|
||||
}
|
||||
|
||||
if ($result === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
});
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the collection in descending order using the given callback.
|
||||
*
|
||||
@@ -1175,6 +1399,21 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return $this->sortKeys($options, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the collection keys using a callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return static
|
||||
*/
|
||||
public function sortKeysUsing(callable $callback)
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
uksort($items, $callback);
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splice a portion of the underlying collection array.
|
||||
*
|
||||
@@ -1189,7 +1428,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return new static(array_splice($this->items, $offset));
|
||||
}
|
||||
|
||||
return new static(array_splice($this->items, $offset, $length, $replacement));
|
||||
return new static(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1242,6 +1481,42 @@ class Collection implements ArrayAccess, Enumerable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a flatten "dot" notation array into an expanded array.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function undot()
|
||||
{
|
||||
return new static(Arr::undot($this->all()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only unique items from the collection array.
|
||||
*
|
||||
* @param string|callable|null $key
|
||||
* @param bool $strict
|
||||
* @return static
|
||||
*/
|
||||
public function unique($key = null, $strict = false)
|
||||
{
|
||||
if (is_null($key) && $strict === false) {
|
||||
return new static(array_unique($this->items, SORT_REGULAR));
|
||||
}
|
||||
|
||||
$callback = $this->valueRetriever($key);
|
||||
|
||||
$exists = [];
|
||||
|
||||
return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
|
||||
if (in_array($id = $callback($item, $key), $exists, $strict)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$exists[] = $id;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the keys on the underlying array.
|
||||
*
|
||||
@@ -1291,6 +1566,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->items);
|
||||
@@ -1301,6 +1577,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->items);
|
||||
@@ -1346,9 +1623,10 @@ class Collection implements ArrayAccess, Enumerable
|
||||
* @param mixed $key
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($key)
|
||||
{
|
||||
return array_key_exists($key, $this->items);
|
||||
return isset($this->items[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1357,6 +1635,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
* @param mixed $key
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
{
|
||||
return $this->items[$key];
|
||||
@@ -1369,6 +1648,7 @@ class Collection implements ArrayAccess, Enumerable
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
@@ -1381,9 +1661,10 @@ class Collection implements ArrayAccess, Enumerable
|
||||
/**
|
||||
* Unset the item at a given offset.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $key
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($key)
|
||||
{
|
||||
unset($this->items[$key]);
|
@@ -27,6 +27,15 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public static function times($number, callable $callback = null);
|
||||
|
||||
/**
|
||||
* Create a collection with the given range.
|
||||
*
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @return static
|
||||
*/
|
||||
public static function range($from, $to);
|
||||
|
||||
/**
|
||||
* Wrap the given value in a collection if applicable.
|
||||
*
|
||||
@@ -43,6 +52,13 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public static function unwrap($value);
|
||||
|
||||
/**
|
||||
* Create a new instance with no items.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function empty();
|
||||
|
||||
/**
|
||||
* Get all items in the enumerable.
|
||||
*
|
||||
@@ -118,6 +134,14 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public function contains($key, $operator = null, $value = null);
|
||||
|
||||
/**
|
||||
* Cross join with the given lists, returning all possible permutations.
|
||||
*
|
||||
* @param mixed ...$lists
|
||||
* @return static
|
||||
*/
|
||||
public function crossJoin(...$lists);
|
||||
|
||||
/**
|
||||
* Dump the collection and end the script.
|
||||
*
|
||||
@@ -187,7 +211,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
/**
|
||||
* Retrieve duplicate items.
|
||||
*
|
||||
* @param callable|null $callback
|
||||
* @param callable|string|null $callback
|
||||
* @param bool $strict
|
||||
* @return static
|
||||
*/
|
||||
@@ -196,7 +220,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
/**
|
||||
* Retrieve duplicate items using strict comparison.
|
||||
*
|
||||
* @param callable|null $callback
|
||||
* @param callable|string|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function duplicatesStrict($callback = null);
|
||||
@@ -309,6 +333,22 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public function where($key, $operator = null, $value = null);
|
||||
|
||||
/**
|
||||
* Filter items where the value for the given key is null.
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return static
|
||||
*/
|
||||
public function whereNull($key = null);
|
||||
|
||||
/**
|
||||
* Filter items where the value for the given key is not null.
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return static
|
||||
*/
|
||||
public function whereNotNull($key = null);
|
||||
|
||||
/**
|
||||
* Filter items by the given key value pair using strict comparison.
|
||||
*
|
||||
@@ -375,9 +415,9 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
public function whereNotInStrict($key, $values);
|
||||
|
||||
/**
|
||||
* Filter the items, removing any items that don't match the given type.
|
||||
* Filter the items, removing any items that don't match the given type(s).
|
||||
*
|
||||
* @param string $type
|
||||
* @param string|string[] $type
|
||||
* @return static
|
||||
*/
|
||||
public function whereInstanceOf($type);
|
||||
@@ -401,6 +441,14 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public function firstWhere($key, $operator = null, $value = null);
|
||||
|
||||
/**
|
||||
* Get a flattened array of the items in the collection.
|
||||
*
|
||||
* @param int $depth
|
||||
* @return static
|
||||
*/
|
||||
public function flatten($depth = INF);
|
||||
|
||||
/**
|
||||
* Flip the values with their keys.
|
||||
*
|
||||
@@ -727,6 +775,22 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public function skip($count);
|
||||
|
||||
/**
|
||||
* Skip items in the collection until the given condition is met.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function skipUntil($value);
|
||||
|
||||
/**
|
||||
* Skip items in the collection while the given condition is met.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function skipWhile($value);
|
||||
|
||||
/**
|
||||
* Get a slice of items from the enumerable.
|
||||
*
|
||||
@@ -752,6 +816,14 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public function chunk($size);
|
||||
|
||||
/**
|
||||
* Chunk the collection into chunks with a callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return static
|
||||
*/
|
||||
public function chunkWhile(callable $callback);
|
||||
|
||||
/**
|
||||
* Sort through each item with a callback.
|
||||
*
|
||||
@@ -820,6 +892,22 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public function take($limit);
|
||||
|
||||
/**
|
||||
* Take items in the collection until the given condition is met.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function takeUntil($value);
|
||||
|
||||
/**
|
||||
* Take items in the collection while the given condition is met.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function takeWhile($value);
|
||||
|
||||
/**
|
||||
* Pass the collection to the given callback and then return it.
|
||||
*
|
||||
@@ -894,6 +982,17 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable,
|
||||
*/
|
||||
public function countBy($callback = null);
|
||||
|
||||
/**
|
||||
* Zip the collection together with one or more arrays.
|
||||
*
|
||||
* e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
|
||||
* => [[1, 4], [2, 5], [3, 6]]
|
||||
*
|
||||
* @param mixed ...$items
|
||||
* @return static
|
||||
*/
|
||||
public function zip($items);
|
||||
|
||||
/**
|
||||
* Collect the values into a collection.
|
||||
*
|
9
vendor/laravel/framework/src/Illuminate/Collections/ItemNotFoundException.php
vendored
Normal file
9
vendor/laravel/framework/src/Illuminate/Collections/ItemNotFoundException.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Support;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class ItemNotFoundException extends RuntimeException
|
||||
{
|
||||
}
|
21
vendor/laravel/framework/src/Illuminate/Collections/LICENSE.md
vendored
Normal file
21
vendor/laravel/framework/src/Illuminate/Collections/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.
|
@@ -4,12 +4,14 @@ namespace Illuminate\Support;
|
||||
|
||||
use ArrayIterator;
|
||||
use Closure;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString;
|
||||
use Illuminate\Support\Traits\EnumeratesValues;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use IteratorAggregate;
|
||||
use stdClass;
|
||||
|
||||
class LazyCollection implements Enumerable
|
||||
class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable
|
||||
{
|
||||
use EnumeratesValues, Macroable;
|
||||
|
||||
@@ -38,39 +40,7 @@ class LazyCollection implements Enumerable
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with no items.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function empty()
|
||||
{
|
||||
return new static([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance by invoking the callback a given amount of times.
|
||||
*
|
||||
* @param int $number
|
||||
* @param callable|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public static function times($number, callable $callback = null)
|
||||
{
|
||||
if ($number < 1) {
|
||||
return new static;
|
||||
}
|
||||
|
||||
$instance = new static(function () use ($number) {
|
||||
for ($current = 1; $current <= $number; $current++) {
|
||||
yield $current;
|
||||
}
|
||||
});
|
||||
|
||||
return is_null($callback) ? $instance : $instance->map($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an enumerable with the given range.
|
||||
* Create a collection with the given range.
|
||||
*
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
@@ -79,8 +49,14 @@ class LazyCollection implements Enumerable
|
||||
public static function range($from, $to)
|
||||
{
|
||||
return new static(function () use ($from, $to) {
|
||||
for (; $from <= $to; $from++) {
|
||||
yield $from;
|
||||
if ($from <= $to) {
|
||||
for (; $from <= $to; $from++) {
|
||||
yield $from;
|
||||
}
|
||||
} else {
|
||||
for (; $from >= $to; $from--) {
|
||||
yield $from;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -229,6 +205,19 @@ class LazyCollection implements Enumerable
|
||||
return $this->contains($this->operatorForWhere(...func_get_args()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an item is not contained in the enumerable.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $operator
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function doesntContain($key, $operator = null, $value = null)
|
||||
{
|
||||
return ! $this->contains(...func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross join the given iterables, returning all possible permutations.
|
||||
*
|
||||
@@ -341,7 +330,7 @@ class LazyCollection implements Enumerable
|
||||
/**
|
||||
* Retrieve duplicate items.
|
||||
*
|
||||
* @param callable|null $callback
|
||||
* @param callable|string|null $callback
|
||||
* @param bool $strict
|
||||
* @return static
|
||||
*/
|
||||
@@ -353,7 +342,7 @@ class LazyCollection implements Enumerable
|
||||
/**
|
||||
* Retrieve duplicate items using strict comparison.
|
||||
*
|
||||
* @param callable|null $callback
|
||||
* @param callable|string|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function duplicatesStrict($callback = null)
|
||||
@@ -537,6 +526,25 @@ class LazyCollection implements Enumerable
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any of the keys exist in the collection.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAny($key)
|
||||
{
|
||||
$keys = array_flip(is_array($key) ? $key : func_get_args());
|
||||
|
||||
foreach ($this as $key => $value) {
|
||||
if (array_key_exists($key, $keys)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate values of a given key as a string.
|
||||
*
|
||||
@@ -572,7 +580,7 @@ class LazyCollection implements Enumerable
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the items is empty or not.
|
||||
* Determine if the items are empty or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -581,6 +589,16 @@ class LazyCollection implements Enumerable
|
||||
return ! $this->getIterator()->valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the collection contains a single item.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function containsOneItem()
|
||||
{
|
||||
return $this->take(2)->count() === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join all items from the collection using a string. The final items can use a separate glue string.
|
||||
*
|
||||
@@ -778,8 +796,8 @@ class LazyCollection implements Enumerable
|
||||
return new static(function () use ($step, $offset) {
|
||||
$position = 0;
|
||||
|
||||
foreach ($this as $item) {
|
||||
if ($position % $step === $offset) {
|
||||
foreach ($this->slice($offset) as $item) {
|
||||
if ($position % $step === 0) {
|
||||
yield $item;
|
||||
}
|
||||
|
||||
@@ -852,24 +870,6 @@ class LazyCollection implements Enumerable
|
||||
return is_null($number) ? $result : new static($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the collection to a single value.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $initial
|
||||
* @return mixed
|
||||
*/
|
||||
public function reduce(callable $callback, $initial = null)
|
||||
{
|
||||
$result = $initial;
|
||||
|
||||
foreach ($this as $value) {
|
||||
$result = $callback($result, $value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the collection items with the given items.
|
||||
*
|
||||
@@ -953,6 +953,45 @@ class LazyCollection implements Enumerable
|
||||
return $this->passthru('shuffle', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create chunks representing a "sliding window" view of the items in the collection.
|
||||
*
|
||||
* @param int $size
|
||||
* @param int $step
|
||||
* @return static
|
||||
*/
|
||||
public function sliding($size = 2, $step = 1)
|
||||
{
|
||||
return new static(function () use ($size, $step) {
|
||||
$iterator = $this->getIterator();
|
||||
|
||||
$chunk = [];
|
||||
|
||||
while ($iterator->valid()) {
|
||||
$chunk[$iterator->key()] = $iterator->current();
|
||||
|
||||
if (count($chunk) == $size) {
|
||||
yield tap(new static($chunk), function () use (&$chunk, $step) {
|
||||
$chunk = array_slice($chunk, $step, null, true);
|
||||
});
|
||||
|
||||
// If the $step between chunks is bigger than each chunk's $size
|
||||
// we will skip the extra items (which should never be in any
|
||||
// chunk) before we continue to the next chunk in the loop.
|
||||
if ($step > $size) {
|
||||
$skip = $step - $size;
|
||||
|
||||
for ($i = 0; $i < $skip && $iterator->valid(); $i++) {
|
||||
$iterator->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$iterator->next();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the first {$count} items.
|
||||
*
|
||||
@@ -1043,6 +1082,55 @@ class LazyCollection implements Enumerable
|
||||
return $this->passthru('split', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $operator
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Illuminate\Support\ItemNotFoundException
|
||||
* @throws \Illuminate\Support\MultipleItemsFoundException
|
||||
*/
|
||||
public function sole($key = null, $operator = null, $value = null)
|
||||
{
|
||||
$filter = func_num_args() > 1
|
||||
? $this->operatorForWhere(...func_get_args())
|
||||
: $key;
|
||||
|
||||
return $this
|
||||
->when($filter)
|
||||
->filter($filter)
|
||||
->take(2)
|
||||
->collect()
|
||||
->sole();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first item in the collection but throw an exception if no matching items exist.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $operator
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Illuminate\Support\ItemNotFoundException
|
||||
*/
|
||||
public function firstOrFail($key = null, $operator = null, $value = null)
|
||||
{
|
||||
$filter = func_num_args() > 1
|
||||
? $this->operatorForWhere(...func_get_args())
|
||||
: $key;
|
||||
|
||||
return $this
|
||||
->when($filter)
|
||||
->filter($filter)
|
||||
->take(1)
|
||||
->collect()
|
||||
->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk the collection into chunks of the given size.
|
||||
*
|
||||
@@ -1082,6 +1170,54 @@ class LazyCollection implements Enumerable
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a collection into a certain number of groups, and fill the first groups completely.
|
||||
*
|
||||
* @param int $numberOfGroups
|
||||
* @return static
|
||||
*/
|
||||
public function splitIn($numberOfGroups)
|
||||
{
|
||||
return $this->chunk(ceil($this->count() / $numberOfGroups));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk the collection into chunks with a callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return static
|
||||
*/
|
||||
public function chunkWhile(callable $callback)
|
||||
{
|
||||
return new static(function () use ($callback) {
|
||||
$iterator = $this->getIterator();
|
||||
|
||||
$chunk = new Collection;
|
||||
|
||||
if ($iterator->valid()) {
|
||||
$chunk[$iterator->key()] = $iterator->current();
|
||||
|
||||
$iterator->next();
|
||||
}
|
||||
|
||||
while ($iterator->valid()) {
|
||||
if (! $callback($iterator->current(), $iterator->key(), $chunk)) {
|
||||
yield new static($chunk);
|
||||
|
||||
$chunk = new Collection;
|
||||
}
|
||||
|
||||
$chunk[$iterator->key()] = $iterator->current();
|
||||
|
||||
$iterator->next();
|
||||
}
|
||||
|
||||
if ($chunk->isNotEmpty()) {
|
||||
yield new static($chunk);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort through each item with a callback.
|
||||
*
|
||||
@@ -1152,6 +1288,17 @@ class LazyCollection implements Enumerable
|
||||
return $this->passthru('sortKeysDesc', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the collection keys using a callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return static
|
||||
*/
|
||||
public function sortKeysUsing(callable $callback)
|
||||
{
|
||||
return $this->passthru('sortKeysUsing', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the first or last {$limit} items.
|
||||
*
|
||||
@@ -1202,6 +1349,21 @@ class LazyCollection implements Enumerable
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Take items in the collection until a given point in time.
|
||||
*
|
||||
* @param \DateTimeInterface $timeout
|
||||
* @return static
|
||||
*/
|
||||
public function takeUntilTimeout(DateTimeInterface $timeout)
|
||||
{
|
||||
$timeout = $timeout->getTimestamp();
|
||||
|
||||
return $this->takeWhile(function () use ($timeout) {
|
||||
return $this->now() < $timeout;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Take items in the collection while the given condition is met.
|
||||
*
|
||||
@@ -1212,7 +1374,9 @@ class LazyCollection implements Enumerable
|
||||
{
|
||||
$callback = $this->useAsCallable($value) ? $value : $this->equality($value);
|
||||
|
||||
return $this->takeUntil($this->negate($callback));
|
||||
return $this->takeUntil(function ($item, $key) use ($callback) {
|
||||
return ! $callback($item, $key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1232,6 +1396,40 @@ class LazyCollection implements Enumerable
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a flatten "dot" notation array into an expanded array.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function undot()
|
||||
{
|
||||
return $this->passthru('undot', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only unique items from the collection array.
|
||||
*
|
||||
* @param string|callable|null $key
|
||||
* @param bool $strict
|
||||
* @return static
|
||||
*/
|
||||
public function unique($key = null, $strict = false)
|
||||
{
|
||||
$callback = $this->valueRetriever($key);
|
||||
|
||||
return new static(function () use ($callback, $strict) {
|
||||
$exists = [];
|
||||
|
||||
foreach ($this as $key => $item) {
|
||||
if (! in_array($id = $callback($item, $key), $exists, $strict)) {
|
||||
yield $key => $item;
|
||||
|
||||
$exists[] = $id;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the keys on the underlying array.
|
||||
*
|
||||
@@ -1305,6 +1503,7 @@ class LazyCollection implements Enumerable
|
||||
*
|
||||
* @return \Traversable
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return $this->makeIterator($this->source);
|
||||
@@ -1315,6 +1514,7 @@ class LazyCollection implements Enumerable
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
if (is_array($this->source)) {
|
||||
@@ -1372,4 +1572,14 @@ class LazyCollection implements Enumerable
|
||||
yield from $this->collect()->$method(...$params);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current time.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function now()
|
||||
{
|
||||
return time();
|
||||
}
|
||||
}
|
9
vendor/laravel/framework/src/Illuminate/Collections/MultipleItemsFoundException.php
vendored
Normal file
9
vendor/laravel/framework/src/Illuminate/Collections/MultipleItemsFoundException.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Support;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class MultipleItemsFoundException extends RuntimeException
|
||||
{
|
||||
}
|
@@ -15,11 +15,14 @@ use Illuminate\Support\HigherOrderWhenProxy;
|
||||
use JsonSerializable;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
use Traversable;
|
||||
use UnexpectedValueException;
|
||||
use UnitEnum;
|
||||
|
||||
/**
|
||||
* @property-read HigherOrderCollectionProxy $average
|
||||
* @property-read HigherOrderCollectionProxy $avg
|
||||
* @property-read HigherOrderCollectionProxy $contains
|
||||
* @property-read HigherOrderCollectionProxy $doesntContain
|
||||
* @property-read HigherOrderCollectionProxy $each
|
||||
* @property-read HigherOrderCollectionProxy $every
|
||||
* @property-read HigherOrderCollectionProxy $filter
|
||||
@@ -35,21 +38,33 @@ use Traversable;
|
||||
* @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 $until
|
||||
*/
|
||||
trait EnumeratesValues
|
||||
{
|
||||
/**
|
||||
* Indicates that the object's string representation should be escaped when __toString is invoked.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeWhenCastingToString = false;
|
||||
|
||||
/**
|
||||
* The methods that can be proxied.
|
||||
*
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $proxies = [
|
||||
'average',
|
||||
'avg',
|
||||
'contains',
|
||||
'doesntContain',
|
||||
'each',
|
||||
'every',
|
||||
'filter',
|
||||
@@ -109,6 +124,34 @@ trait EnumeratesValues
|
||||
return $value instanceof Enumerable ? $value->all() : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with no items.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function empty()
|
||||
{
|
||||
return new static([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new collection by invoking the callback a given amount of times.
|
||||
*
|
||||
* @param int $number
|
||||
* @param callable|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public static function times($number, callable $callback = null)
|
||||
{
|
||||
if ($number < 1) {
|
||||
return new static;
|
||||
}
|
||||
|
||||
return static::range(1, $number)
|
||||
->when($callback)
|
||||
->map($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for the "avg" method.
|
||||
*
|
||||
@@ -519,7 +562,7 @@ trait EnumeratesValues
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter items where the given key is not null.
|
||||
* Filter items where the value for the given key is null.
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return static
|
||||
@@ -530,7 +573,7 @@ trait EnumeratesValues
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter items where the given key is null.
|
||||
* Filter items where the value for the given key is not null.
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return static
|
||||
@@ -637,14 +680,24 @@ trait EnumeratesValues
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the items, removing any items that don't match the given type.
|
||||
* Filter the items, removing any items that don't match the given type(s).
|
||||
*
|
||||
* @param string $type
|
||||
* @param string|string[] $type
|
||||
* @return static
|
||||
*/
|
||||
public function whereInstanceOf($type)
|
||||
{
|
||||
return $this->filter(function ($value) use ($type) {
|
||||
if (is_array($type)) {
|
||||
foreach ($type as $classType) {
|
||||
if ($value instanceof $classType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $value instanceof $type;
|
||||
});
|
||||
}
|
||||
@@ -660,6 +713,33 @@ trait EnumeratesValues
|
||||
return $callback($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the collection into a new class.
|
||||
*
|
||||
* @param string $class
|
||||
* @return mixed
|
||||
*/
|
||||
public function pipeInto($class)
|
||||
{
|
||||
return new $class($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the collection through a series of callable pipes and return the result.
|
||||
*
|
||||
* @param array<callable> $pipes
|
||||
* @return mixed
|
||||
*/
|
||||
public function pipeThrough($pipes)
|
||||
{
|
||||
return static::make($pipes)->reduce(
|
||||
function ($carry, $pipe) {
|
||||
return $pipe($carry);
|
||||
},
|
||||
$this,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the collection to the given callback and then return it.
|
||||
*
|
||||
@@ -673,6 +753,79 @@ trait EnumeratesValues
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the collection to a single value.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $initial
|
||||
* @return mixed
|
||||
*/
|
||||
public function reduce(callable $callback, $initial = null)
|
||||
{
|
||||
$result = $initial;
|
||||
|
||||
foreach ($this as $key => $value) {
|
||||
$result = $callback($result, $value, $key);
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed ...$initial
|
||||
* @return array
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function reduceSpread(callable $callback, ...$initial)
|
||||
{
|
||||
$result = $initial;
|
||||
|
||||
foreach ($this as $key => $value) {
|
||||
$result = call_user_func_array($callback, array_merge($result, [$value, $key]));
|
||||
|
||||
if (! is_array($result)) {
|
||||
throw new UnexpectedValueException(sprintf(
|
||||
"%s::reduceMany expects reducer to return an array, but got a '%s' instead.",
|
||||
class_basename(static::class), gettype($result)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce an associative collection to a single value.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
@@ -690,28 +843,6 @@ trait EnumeratesValues
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only unique items from the collection array.
|
||||
*
|
||||
* @param string|callable|null $key
|
||||
* @param bool $strict
|
||||
* @return static
|
||||
*/
|
||||
public function unique($key = null, $strict = false)
|
||||
{
|
||||
$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.
|
||||
*
|
||||
@@ -723,21 +854,6 @@ trait EnumeratesValues
|
||||
return $this->unique($key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take items in the collection until the given condition is met.
|
||||
*
|
||||
* This is an alias to the "takeUntil" method.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*
|
||||
* @deprecated Use the "takeUntil" method directly.
|
||||
*/
|
||||
public function until($value)
|
||||
{
|
||||
return $this->takeUntil($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the values into a collection.
|
||||
*
|
||||
@@ -765,6 +881,7 @@ trait EnumeratesValues
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return array_map(function ($value) {
|
||||
@@ -809,7 +926,22 @@ trait EnumeratesValues
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toJson();
|
||||
return $this->escapeWhenCastingToString
|
||||
? e($this->toJson())
|
||||
: $this->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's string representation should be escaped when __toString is invoked.
|
||||
*
|
||||
* @param bool $escape
|
||||
* @return $this
|
||||
*/
|
||||
public function escapeWhenCastingToString($escape = true)
|
||||
{
|
||||
$this->escapeWhenCastingToString = $escape;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -860,6 +992,8 @@ trait EnumeratesValues
|
||||
return (array) $items->jsonSerialize();
|
||||
} elseif ($items instanceof Traversable) {
|
||||
return iterator_to_array($items);
|
||||
} elseif ($items instanceof UnitEnum) {
|
||||
return [$items];
|
||||
}
|
||||
|
||||
return (array) $items;
|
41
vendor/laravel/framework/src/Illuminate/Collections/composer.json
vendored
Normal file
41
vendor/laravel/framework/src/Illuminate/Collections/composer.json
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "illuminate/collections",
|
||||
"description": "The Illuminate Collections package.",
|
||||
"license": "MIT",
|
||||
"homepage": "https://laravel.com",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"illuminate/contracts": "^8.0",
|
||||
"illuminate/macroable": "^8.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Illuminate\\Support\\": ""
|
||||
},
|
||||
"files": [
|
||||
"helpers.php"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "8.x-dev"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/var-dumper": "Required to use the dump method (^5.4)."
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
186
vendor/laravel/framework/src/Illuminate/Collections/helpers.php
vendored
Normal file
186
vendor/laravel/framework/src/Illuminate/Collections/helpers.php
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
if (! function_exists('collect')) {
|
||||
/**
|
||||
* Create a collection from the given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
function collect($value = null)
|
||||
{
|
||||
return new Collection($value);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('data_fill')) {
|
||||
/**
|
||||
* Fill in data where it's missing.
|
||||
*
|
||||
* @param mixed $target
|
||||
* @param string|array $key
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
function data_fill(&$target, $key, $value)
|
||||
{
|
||||
return data_set($target, $key, $value, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('data_get')) {
|
||||
/**
|
||||
* Get an item from an array or object using "dot" notation.
|
||||
*
|
||||
* @param mixed $target
|
||||
* @param string|array|int|null $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
function data_get($target, $key, $default = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
return $target;
|
||||
}
|
||||
|
||||
$key = is_array($key) ? $key : explode('.', $key);
|
||||
|
||||
foreach ($key as $i => $segment) {
|
||||
unset($key[$i]);
|
||||
|
||||
if (is_null($segment)) {
|
||||
return $target;
|
||||
}
|
||||
|
||||
if ($segment === '*') {
|
||||
if ($target instanceof Collection) {
|
||||
$target = $target->all();
|
||||
} elseif (! is_array($target)) {
|
||||
return value($default);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($target as $item) {
|
||||
$result[] = data_get($item, $key);
|
||||
}
|
||||
|
||||
return in_array('*', $key) ? Arr::collapse($result) : $result;
|
||||
}
|
||||
|
||||
if (Arr::accessible($target) && Arr::exists($target, $segment)) {
|
||||
$target = $target[$segment];
|
||||
} elseif (is_object($target) && isset($target->{$segment})) {
|
||||
$target = $target->{$segment};
|
||||
} else {
|
||||
return value($default);
|
||||
}
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('data_set')) {
|
||||
/**
|
||||
* Set an item on an array or object using dot notation.
|
||||
*
|
||||
* @param mixed $target
|
||||
* @param string|array $key
|
||||
* @param mixed $value
|
||||
* @param bool $overwrite
|
||||
* @return mixed
|
||||
*/
|
||||
function data_set(&$target, $key, $value, $overwrite = true)
|
||||
{
|
||||
$segments = is_array($key) ? $key : explode('.', $key);
|
||||
|
||||
if (($segment = array_shift($segments)) === '*') {
|
||||
if (! Arr::accessible($target)) {
|
||||
$target = [];
|
||||
}
|
||||
|
||||
if ($segments) {
|
||||
foreach ($target as &$inner) {
|
||||
data_set($inner, $segments, $value, $overwrite);
|
||||
}
|
||||
} elseif ($overwrite) {
|
||||
foreach ($target as &$inner) {
|
||||
$inner = $value;
|
||||
}
|
||||
}
|
||||
} elseif (Arr::accessible($target)) {
|
||||
if ($segments) {
|
||||
if (! Arr::exists($target, $segment)) {
|
||||
$target[$segment] = [];
|
||||
}
|
||||
|
||||
data_set($target[$segment], $segments, $value, $overwrite);
|
||||
} elseif ($overwrite || ! Arr::exists($target, $segment)) {
|
||||
$target[$segment] = $value;
|
||||
}
|
||||
} elseif (is_object($target)) {
|
||||
if ($segments) {
|
||||
if (! isset($target->{$segment})) {
|
||||
$target->{$segment} = [];
|
||||
}
|
||||
|
||||
data_set($target->{$segment}, $segments, $value, $overwrite);
|
||||
} elseif ($overwrite || ! isset($target->{$segment})) {
|
||||
$target->{$segment} = $value;
|
||||
}
|
||||
} else {
|
||||
$target = [];
|
||||
|
||||
if ($segments) {
|
||||
data_set($target[$segment], $segments, $value, $overwrite);
|
||||
} elseif ($overwrite) {
|
||||
$target[$segment] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('head')) {
|
||||
/**
|
||||
* Get the first element of an array. Useful for method chaining.
|
||||
*
|
||||
* @param array $array
|
||||
* @return mixed
|
||||
*/
|
||||
function head($array)
|
||||
{
|
||||
return reset($array);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('last')) {
|
||||
/**
|
||||
* Get the last element from an array.
|
||||
*
|
||||
* @param array $array
|
||||
* @return mixed
|
||||
*/
|
||||
function last($array)
|
||||
{
|
||||
return end($array);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('value')) {
|
||||
/**
|
||||
* Return the default value of the given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
function value($value, ...$args)
|
||||
{
|
||||
return $value instanceof Closure ? $value(...$args) : $value;
|
||||
}
|
||||
}
|
@@ -99,7 +99,7 @@ class Repository implements ArrayAccess, ConfigContract
|
||||
*/
|
||||
public function prepend($key, $value)
|
||||
{
|
||||
$array = $this->get($key);
|
||||
$array = $this->get($key, []);
|
||||
|
||||
array_unshift($array, $value);
|
||||
|
||||
@@ -115,7 +115,7 @@ class Repository implements ArrayAccess, ConfigContract
|
||||
*/
|
||||
public function push($key, $value)
|
||||
{
|
||||
$array = $this->get($key);
|
||||
$array = $this->get($key, []);
|
||||
|
||||
$array[] = $value;
|
||||
|
||||
@@ -138,6 +138,7 @@ class Repository implements ArrayAccess, ConfigContract
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($key)
|
||||
{
|
||||
return $this->has($key);
|
||||
@@ -149,6 +150,7 @@ class Repository implements ArrayAccess, ConfigContract
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
@@ -161,6 +163,7 @@ class Repository implements ArrayAccess, ConfigContract
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
$this->set($key, $value);
|
||||
@@ -172,6 +175,7 @@ class Repository implements ArrayAccess, ConfigContract
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($key)
|
||||
{
|
||||
$this->set($key, null);
|
||||
|
@@ -14,9 +14,9 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5|^8.0",
|
||||
"illuminate/contracts": "^7.0",
|
||||
"illuminate/support": "^7.0"
|
||||
"php": "^7.3|^8.0",
|
||||
"illuminate/collections": "^8.0",
|
||||
"illuminate/contracts": "^8.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.x-dev"
|
||||
"dev-master": "8.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
|
@@ -19,7 +19,6 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\StringInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
|
||||
@@ -77,6 +76,8 @@ class Application extends SymfonyApplication implements ApplicationContract
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function run(InputInterface $input = null, OutputInterface $output = null)
|
||||
{
|
||||
@@ -86,7 +87,7 @@ class Application extends SymfonyApplication implements ApplicationContract
|
||||
|
||||
$this->events->dispatch(
|
||||
new CommandStarting(
|
||||
$commandName, $input, $output = $output ?: new ConsoleOutput
|
||||
$commandName, $input, $output = $output ?: new BufferedConsoleOutput
|
||||
)
|
||||
);
|
||||
|
||||
@@ -116,7 +117,7 @@ class Application extends SymfonyApplication implements ApplicationContract
|
||||
*/
|
||||
public static function artisanBinary()
|
||||
{
|
||||
return defined('ARTISAN_BINARY') ? ProcessUtils::escapeArgument(ARTISAN_BINARY) : 'artisan';
|
||||
return ProcessUtils::escapeArgument(defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +210,7 @@ class Application extends SymfonyApplication implements ApplicationContract
|
||||
$input = new ArrayInput($parameters);
|
||||
}
|
||||
|
||||
return [$command, $input ?? null];
|
||||
return [$command, $input];
|
||||
}
|
||||
|
||||
/**
|
||||
|
41
vendor/laravel/framework/src/Illuminate/Console/BufferedConsoleOutput.php
vendored
Normal file
41
vendor/laravel/framework/src/Illuminate/Console/BufferedConsoleOutput.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Console;
|
||||
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
|
||||
class BufferedConsoleOutput extends ConsoleOutput
|
||||
{
|
||||
/**
|
||||
* The current buffer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $buffer = '';
|
||||
|
||||
/**
|
||||
* Empties the buffer and returns its content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function fetch()
|
||||
{
|
||||
return tap($this->buffer, function () {
|
||||
$this->buffer = '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite(string $message, bool $newline)
|
||||
{
|
||||
$this->buffer .= $message;
|
||||
|
||||
if ($newline) {
|
||||
$this->buffer .= \PHP_EOL;
|
||||
}
|
||||
|
||||
return parent::doWrite($message, $newline);
|
||||
}
|
||||
}
|
@@ -38,14 +38,14 @@ class Command extends SymfonyCommand
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string|null
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* The console command help text.
|
||||
*
|
||||
* @var string|null
|
||||
* @var string
|
||||
*/
|
||||
protected $help;
|
||||
|
||||
@@ -131,7 +131,9 @@ class Command extends SymfonyCommand
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
return (int) $this->laravel->call([$this, 'handle']);
|
||||
$method = method_exists($this, 'handle') ? 'handle' : '__invoke';
|
||||
|
||||
return (int) $this->laravel->call([$this, $method]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,6 +163,8 @@ class Command extends SymfonyCommand
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isHidden()
|
||||
{
|
||||
@@ -169,6 +173,8 @@ class Command extends SymfonyCommand
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setHidden(bool $hidden)
|
||||
{
|
||||
|
@@ -29,7 +29,7 @@ trait CallsCommands
|
||||
}
|
||||
|
||||
/**
|
||||
* Call another console command silently.
|
||||
* Call another console command without output.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Command\Command|string $command
|
||||
* @param array $arguments
|
||||
@@ -40,6 +40,18 @@ trait CallsCommands
|
||||
return $this->runCommand($command, $arguments, new NullOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call another console command without output.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Command\Command|string $command
|
||||
* @param array $arguments
|
||||
* @return int
|
||||
*/
|
||||
public function callSilently($command, array $arguments = [])
|
||||
{
|
||||
return $this->callSilent($command, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the given the console command.
|
||||
*
|
||||
|
44
vendor/laravel/framework/src/Illuminate/Console/Concerns/CreatesMatchingTest.php
vendored
Normal file
44
vendor/laravel/framework/src/Illuminate/Console/Concerns/CreatesMatchingTest.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Console\Concerns;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
trait CreatesMatchingTest
|
||||
{
|
||||
/**
|
||||
* Add the standard command options for generating matching tests.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addTestOptions()
|
||||
{
|
||||
foreach (['test' => 'PHPUnit', 'pest' => 'Pest'] as $option => $name) {
|
||||
$this->getDefinition()->addOption(new InputOption(
|
||||
$option,
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
"Generate an accompanying {$name} test for the {$this->type}"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the matching test case if requested.
|
||||
*
|
||||
* @param string $path
|
||||
* @return void
|
||||
*/
|
||||
protected function handleTestCreation($path)
|
||||
{
|
||||
if (! $this->option('test') && ! $this->option('pest')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->call('make:test', [
|
||||
'name' => Str::of($path)->after($this->laravel['path'])->beforeLast('.php')->append('Test')->replace('\\', '/'),
|
||||
'--pest' => $this->option('pest'),
|
||||
]);
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Illuminate\Console\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Console\OutputStyle;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Support\Str;
|
||||
@@ -237,6 +238,38 @@ trait InteractsWithIO
|
||||
$table->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a given callback while advancing a progress bar.
|
||||
*
|
||||
* @param iterable|int $totalSteps
|
||||
* @param \Closure $callback
|
||||
* @return mixed|void
|
||||
*/
|
||||
public function withProgressBar($totalSteps, Closure $callback)
|
||||
{
|
||||
$bar = $this->output->createProgressBar(
|
||||
is_iterable($totalSteps) ? count($totalSteps) : $totalSteps
|
||||
);
|
||||
|
||||
$bar->start();
|
||||
|
||||
if (is_iterable($totalSteps)) {
|
||||
foreach ($totalSteps as $value) {
|
||||
$callback($value, $bar);
|
||||
|
||||
$bar->advance();
|
||||
}
|
||||
} else {
|
||||
$callback($bar);
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
|
||||
if (is_iterable($totalSteps)) {
|
||||
return $totalSteps;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a string as information output.
|
||||
*
|
||||
@@ -332,7 +365,18 @@ trait InteractsWithIO
|
||||
$this->comment('* '.$string.' *');
|
||||
$this->comment(str_repeat('*', $length));
|
||||
|
||||
$this->output->newLine();
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a blank line.
|
||||
*
|
||||
* @param int $count
|
||||
* @return void
|
||||
*/
|
||||
public function newLine($count = 1)
|
||||
{
|
||||
$this->output->newLine($count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
26
vendor/laravel/framework/src/Illuminate/Console/Events/ScheduledBackgroundTaskFinished.php
vendored
Normal file
26
vendor/laravel/framework/src/Illuminate/Console/Events/ScheduledBackgroundTaskFinished.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Console\Events;
|
||||
|
||||
use Illuminate\Console\Scheduling\Event;
|
||||
|
||||
class ScheduledBackgroundTaskFinished
|
||||
{
|
||||
/**
|
||||
* The scheduled event that ran.
|
||||
*
|
||||
* @var \Illuminate\Console\Scheduling\Event
|
||||
*/
|
||||
public $task;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Event $task
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Event $task)
|
||||
{
|
||||
$this->task = $task;
|
||||
}
|
||||
}
|
@@ -26,6 +26,7 @@ class ScheduledTaskFailed
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Event $task
|
||||
* @param \Throwable $exception
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Event $task, Throwable $exception)
|
||||
{
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Illuminate\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -25,7 +26,7 @@ abstract class GeneratorCommand extends Command
|
||||
/**
|
||||
* Reserved names that cannot be used for generation.
|
||||
*
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
protected $reservedNames = [
|
||||
'__halt_compiler',
|
||||
@@ -108,6 +109,10 @@ abstract class GeneratorCommand extends Command
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (in_array(CreatesMatchingTest::class, class_uses_recursive($this))) {
|
||||
$this->addTestOptions();
|
||||
}
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
@@ -159,6 +164,10 @@ abstract class GeneratorCommand extends Command
|
||||
$this->files->put($path, $this->sortImports($this->buildClass($name)));
|
||||
|
||||
$this->info($this->type.' created successfully.');
|
||||
|
||||
if (in_array(CreatesMatchingTest::class, class_uses_recursive($this))) {
|
||||
$this->handleTestCreation($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,19 +180,42 @@ abstract class GeneratorCommand extends Command
|
||||
{
|
||||
$name = ltrim($name, '\\/');
|
||||
|
||||
$name = str_replace('/', '\\', $name);
|
||||
|
||||
$rootNamespace = $this->rootNamespace();
|
||||
|
||||
if (Str::startsWith($name, $rootNamespace)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
$name = str_replace('/', '\\', $name);
|
||||
|
||||
return $this->qualifyClass(
|
||||
$this->getDefaultNamespace(trim($rootNamespace, '\\')).'\\'.$name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Qualify the given model class base name.
|
||||
*
|
||||
* @param string $model
|
||||
* @return string
|
||||
*/
|
||||
protected function qualifyModel(string $model)
|
||||
{
|
||||
$model = ltrim($model, '\\/');
|
||||
|
||||
$model = str_replace('/', '\\', $model);
|
||||
|
||||
$rootNamespace = $this->rootNamespace();
|
||||
|
||||
if (Str::startsWith($model, $rootNamespace)) {
|
||||
return $model;
|
||||
}
|
||||
|
||||
return is_dir(app_path('Models'))
|
||||
? $rootNamespace.'Models\\'.$model
|
||||
: $rootNamespace.$model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
|
@@ -68,4 +68,14 @@ class OutputStyle extends SymfonyStyle
|
||||
{
|
||||
return $this->output->isDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying Symfony output implementation.
|
||||
*
|
||||
* @return \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
public function getOutput()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Support\Reflector;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Throwable;
|
||||
|
||||
class CallbackEvent extends Event
|
||||
{
|
||||
@@ -77,6 +78,12 @@ class CallbackEvent extends Event
|
||||
$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();
|
||||
|
||||
|
@@ -52,7 +52,7 @@ class CommandBuilder
|
||||
$finished = Application::formatCommandString('schedule:finish').' "'.$event->mutexName().'"';
|
||||
|
||||
if (windows_os()) {
|
||||
return 'start /b cmd /c "('.$event->command.' & '.$finished.' "%errorlevel%")'.$redirect.$output.' 2>&1"';
|
||||
return 'start /b cmd /v:on /c "('.$event->command.' & '.$finished.' ^!ERRORLEVEL^!)'.$redirect.$output.' 2>&1"';
|
||||
}
|
||||
|
||||
return $this->ensureCorrectUser($event,
|
||||
|
@@ -10,13 +10,14 @@ use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Contracts\Mail\Mailer;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Reflector;
|
||||
use Illuminate\Support\Stringable;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use Illuminate\Support\Traits\ReflectsClosures;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Throwable;
|
||||
|
||||
class Event
|
||||
{
|
||||
@@ -86,7 +87,7 @@ class Event
|
||||
public $expiresAt = 1440;
|
||||
|
||||
/**
|
||||
* Indicates if the command should run in background.
|
||||
* Indicates if the command should run in the background.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
@@ -218,11 +219,17 @@ class Event
|
||||
*/
|
||||
protected function runCommandInForeground(Container $container)
|
||||
{
|
||||
$this->callBeforeCallbacks($container);
|
||||
try {
|
||||
$this->callBeforeCallbacks($container);
|
||||
|
||||
$this->exitCode = Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run();
|
||||
$this->exitCode = Process::fromShellCommandline(
|
||||
$this->buildCommand(), base_path(), null, null, null
|
||||
)->run();
|
||||
|
||||
$this->callAfterCallbacks($container);
|
||||
$this->callAfterCallbacks($container);
|
||||
} finally {
|
||||
$this->removeMutex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,9 +240,15 @@ class Event
|
||||
*/
|
||||
protected function runCommandInBackground(Container $container)
|
||||
{
|
||||
$this->callBeforeCallbacks($container);
|
||||
try {
|
||||
$this->callBeforeCallbacks($container);
|
||||
|
||||
Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run();
|
||||
Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run();
|
||||
} catch (Throwable $exception) {
|
||||
$this->removeMutex();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,7 +288,11 @@ class Event
|
||||
{
|
||||
$this->exitCode = (int) $exitCode;
|
||||
|
||||
$this->callAfterCallbacks($container);
|
||||
try {
|
||||
$this->callAfterCallbacks($container);
|
||||
} finally {
|
||||
$this->removeMutex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,13 +338,13 @@ class Event
|
||||
*/
|
||||
protected function expressionPasses()
|
||||
{
|
||||
$date = Carbon::now();
|
||||
$date = Date::now();
|
||||
|
||||
if ($this->timezone) {
|
||||
$date->setTimezone($this->timezone);
|
||||
$date = $date->setTimezone($this->timezone);
|
||||
}
|
||||
|
||||
return CronExpression::factory($this->expression)->isDue($date->toDateTimeString());
|
||||
return (new CronExpression($this->expression))->isDue($date->toDateTimeString());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,7 +492,7 @@ class Event
|
||||
*/
|
||||
protected function emailOutput(Mailer $mailer, $addresses, $onlyIfOutputExists = false)
|
||||
{
|
||||
$text = file_exists($this->output) ? file_get_contents($this->output) : '';
|
||||
$text = is_file($this->output) ? file_get_contents($this->output) : '';
|
||||
|
||||
if ($onlyIfOutputExists && empty($text)) {
|
||||
return;
|
||||
@@ -586,7 +603,7 @@ class Event
|
||||
}
|
||||
|
||||
/**
|
||||
* State that the command should run in background.
|
||||
* State that the command should run in the background.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -647,9 +664,7 @@ class Event
|
||||
|
||||
$this->expiresAt = $expiresAt;
|
||||
|
||||
return $this->then(function () {
|
||||
$this->mutex->forget($this);
|
||||
})->skip(function () {
|
||||
return $this->skip(function () {
|
||||
return $this->mutex->exists($this);
|
||||
});
|
||||
}
|
||||
@@ -728,6 +743,12 @@ class Event
|
||||
*/
|
||||
public function then(Closure $callback)
|
||||
{
|
||||
$parameters = $this->closureParameterTypes($callback);
|
||||
|
||||
if (Arr::get($parameters, 'output') === Stringable::class) {
|
||||
return $this->thenWithOutput($callback);
|
||||
}
|
||||
|
||||
$this->afterCallbacks[] = $callback;
|
||||
|
||||
return $this;
|
||||
@@ -755,6 +776,12 @@ class Event
|
||||
*/
|
||||
public function onSuccess(Closure $callback)
|
||||
{
|
||||
$parameters = $this->closureParameterTypes($callback);
|
||||
|
||||
if (Arr::get($parameters, 'output') === Stringable::class) {
|
||||
return $this->onSuccessWithOutput($callback);
|
||||
}
|
||||
|
||||
return $this->then(function (Container $container) use ($callback) {
|
||||
if (0 === $this->exitCode) {
|
||||
$container->call($callback);
|
||||
@@ -784,6 +811,12 @@ class Event
|
||||
*/
|
||||
public function onFailure(Closure $callback)
|
||||
{
|
||||
$parameters = $this->closureParameterTypes($callback);
|
||||
|
||||
if (Arr::get($parameters, 'output') === Stringable::class) {
|
||||
return $this->onFailureWithOutput($callback);
|
||||
}
|
||||
|
||||
return $this->then(function (Container $container) use ($callback) {
|
||||
if (0 !== $this->exitCode) {
|
||||
$container->call($callback);
|
||||
@@ -815,11 +848,11 @@ class Event
|
||||
protected function withOutputCallback(Closure $callback, $onlyIfOutputExists = false)
|
||||
{
|
||||
return function (Container $container) use ($callback, $onlyIfOutputExists) {
|
||||
$output = $this->output && file_exists($this->output) ? file_get_contents($this->output) : '';
|
||||
$output = $this->output && is_file($this->output) ? file_get_contents($this->output) : '';
|
||||
|
||||
return $onlyIfOutputExists && empty($output)
|
||||
? null
|
||||
: $container->call($callback, ['output' => $output]);
|
||||
: $container->call($callback, ['output' => new Stringable($output)]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -871,9 +904,8 @@ class Event
|
||||
*/
|
||||
public function nextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
|
||||
{
|
||||
return Date::instance(CronExpression::factory(
|
||||
$this->getExpression()
|
||||
)->getNextRunDate($currentTime, $nth, $allowCurrentDate, $this->timezone));
|
||||
return Date::instance((new CronExpression($this->getExpression()))
|
||||
->getNextRunDate($currentTime, $nth, $allowCurrentDate, $this->timezone));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -898,4 +930,16 @@ class Event
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the mutex for the event.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeMutex()
|
||||
{
|
||||
if ($this->withoutOverlapping) {
|
||||
$this->mutex->forget($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -262,10 +262,23 @@ trait ManagesFrequencies
|
||||
* @return $this
|
||||
*/
|
||||
public function twiceDaily($first = 1, $second = 13)
|
||||
{
|
||||
return $this->twiceDailyAt($first, $second, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the event to run twice daily at a given offset.
|
||||
*
|
||||
* @param int $first
|
||||
* @param int $second
|
||||
* @param int $offset
|
||||
* @return $this
|
||||
*/
|
||||
public function twiceDailyAt($first = 1, $second = 13, $offset = 0)
|
||||
{
|
||||
$hours = $first.','.$second;
|
||||
|
||||
return $this->spliceIntoPosition(1, 0)
|
||||
return $this->spliceIntoPosition(1, $offset)
|
||||
->spliceIntoPosition(2, $hours);
|
||||
}
|
||||
|
||||
@@ -276,7 +289,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function weekdays()
|
||||
{
|
||||
return $this->spliceIntoPosition(5, '1-5');
|
||||
return $this->days(Schedule::MONDAY.'-'.Schedule::FRIDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,7 +299,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function weekends()
|
||||
{
|
||||
return $this->spliceIntoPosition(5, '0,6');
|
||||
return $this->days(Schedule::SATURDAY.','.Schedule::SUNDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,7 +309,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function mondays()
|
||||
{
|
||||
return $this->days(1);
|
||||
return $this->days(Schedule::MONDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,7 +319,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function tuesdays()
|
||||
{
|
||||
return $this->days(2);
|
||||
return $this->days(Schedule::TUESDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,7 +329,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function wednesdays()
|
||||
{
|
||||
return $this->days(3);
|
||||
return $this->days(Schedule::WEDNESDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,7 +339,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function thursdays()
|
||||
{
|
||||
return $this->days(4);
|
||||
return $this->days(Schedule::THURSDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,7 +349,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function fridays()
|
||||
{
|
||||
return $this->days(5);
|
||||
return $this->days(Schedule::FRIDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,7 +359,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function saturdays()
|
||||
{
|
||||
return $this->days(6);
|
||||
return $this->days(Schedule::SATURDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +369,7 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function sundays()
|
||||
{
|
||||
return $this->days(0);
|
||||
return $this->days(Schedule::SUNDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,15 +387,15 @@ trait ManagesFrequencies
|
||||
/**
|
||||
* Schedule the event to run weekly on a given day and time.
|
||||
*
|
||||
* @param int $day
|
||||
* @param array|mixed $dayOfWeek
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function weeklyOn($day, $time = '0:0')
|
||||
public function weeklyOn($dayOfWeek, $time = '0:0')
|
||||
{
|
||||
$this->dailyAt($time);
|
||||
|
||||
return $this->spliceIntoPosition(5, $day);
|
||||
return $this->days($dayOfWeek);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -400,15 +413,15 @@ trait ManagesFrequencies
|
||||
/**
|
||||
* Schedule the event to run monthly on a given day and time.
|
||||
*
|
||||
* @param int $day
|
||||
* @param int $dayOfMonth
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function monthlyOn($day = 1, $time = '0:0')
|
||||
public function monthlyOn($dayOfMonth = 1, $time = '0:0')
|
||||
{
|
||||
$this->dailyAt($time);
|
||||
|
||||
return $this->spliceIntoPosition(3, $day);
|
||||
return $this->spliceIntoPosition(3, $dayOfMonth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -421,13 +434,11 @@ trait ManagesFrequencies
|
||||
*/
|
||||
public function twiceMonthly($first = 1, $second = 16, $time = '0:0')
|
||||
{
|
||||
$days = $first.','.$second;
|
||||
$daysOfMonth = $first.','.$second;
|
||||
|
||||
$this->dailyAt($time);
|
||||
|
||||
return $this->spliceIntoPosition(1, 0)
|
||||
->spliceIntoPosition(2, 0)
|
||||
->spliceIntoPosition(3, $days);
|
||||
return $this->spliceIntoPosition(3, $daysOfMonth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -469,6 +480,22 @@ trait ManagesFrequencies
|
||||
->spliceIntoPosition(4, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the event to run yearly on a given month, day, and time.
|
||||
*
|
||||
* @param int $month
|
||||
* @param int|string $dayOfMonth
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function yearlyOn($month = 1, $dayOfMonth = 1, $time = '0:0')
|
||||
{
|
||||
$this->dailyAt($time);
|
||||
|
||||
return $this->spliceIntoPosition(3, $dayOfMonth)
|
||||
->spliceIntoPosition(4, $month);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the days of the week the command should run on.
|
||||
*
|
||||
|
@@ -4,10 +4,13 @@ namespace Illuminate\Console\Scheduling;
|
||||
|
||||
use Closure;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Bus\UniqueLock;
|
||||
use Illuminate\Console\Application;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Contracts\Cache\Repository as Cache;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\CallQueuedClosure;
|
||||
use Illuminate\Support\ProcessUtils;
|
||||
@@ -19,6 +22,14 @@ 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;
|
||||
|
||||
/**
|
||||
* All of the events on the schedule.
|
||||
*
|
||||
@@ -59,6 +70,8 @@ class Schedule
|
||||
*
|
||||
* @param \DateTimeZone|string|null $timezone
|
||||
* @return void
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function __construct($timezone = null)
|
||||
{
|
||||
@@ -107,7 +120,11 @@ class Schedule
|
||||
public function command($command, array $parameters = [])
|
||||
{
|
||||
if (class_exists($command)) {
|
||||
$command = Container::getInstance()->make($command)->getName();
|
||||
$command = Container::getInstance()->make($command);
|
||||
|
||||
return $this->exec(
|
||||
Application::formatCommandString($command->getName()), $parameters,
|
||||
)->description($command->getDescription());
|
||||
}
|
||||
|
||||
return $this->exec(
|
||||
@@ -143,6 +160,8 @@ class Schedule
|
||||
* @param string|null $queue
|
||||
* @param string|null $connection
|
||||
* @return void
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function dispatchToQueue($job, $queue, $connection)
|
||||
{
|
||||
@@ -156,6 +175,35 @@ class Schedule
|
||||
$job = CallQueuedClosure::create($job);
|
||||
}
|
||||
|
||||
if ($job instanceof ShouldBeUnique) {
|
||||
return $this->dispatchUniqueJobToQueue($job, $queue, $connection);
|
||||
}
|
||||
|
||||
$this->getDispatcher()->dispatch(
|
||||
$job->onConnection($connection)->onQueue($queue)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the given unique job to the queue.
|
||||
*
|
||||
* @param object $job
|
||||
* @param string|null $queue
|
||||
* @param string|null $connection
|
||||
* @return void
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function dispatchUniqueJobToQueue($job, $queue, $connection)
|
||||
{
|
||||
if (! Container::getInstance()->bound(Cache::class)) {
|
||||
throw new RuntimeException('Cache driver not available. Scheduling unique jobs not supported.');
|
||||
}
|
||||
|
||||
if (! (new UniqueLock(Container::getInstance()->make(Cache::class)))->acquire($job)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getDispatcher()->dispatch(
|
||||
$job->onConnection($connection)->onQueue($queue)
|
||||
);
|
||||
@@ -293,6 +341,8 @@ class Schedule
|
||||
* Get the job dispatcher, if available.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Bus\Dispatcher
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getDispatcher()
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user