updated backend

This commit is contained in:
Torsten 2020-01-17 10:58:01 +01:00
parent 1fea0f4fe7
commit 11521c2339
3459 changed files with 72735 additions and 168535 deletions

View file

@ -20,17 +20,17 @@ Laravel is a web application framework with expressive, elegant syntax. We belie
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, yet powerful, providing tools needed for large, robust applications. A superb combination of simplicity, elegance, and innovation give you tools you need to build any application with which you are tasked.
Laravel is accessible, yet powerful, providing tools needed for large, robust applications. A superb combination of simplicity, elegance, and innovation gives you a complete toolset required to build any application with which you are tasked.
## Learning Laravel
Laravel has the most extensive and thorough documentation and video tutorial library of any modern web application framework. The [Laravel documentation](https://laravel.com/docs) is thorough, complete, and makes it a breeze to get started learning the framework.
Laravel has the most extensive and thorough documentation and video tutorial library of any modern web application framework. The [Laravel documentation](https://laravel.com/docs) is in-depth and complete, making it a breeze to get started learning the framework.
If you're not in the mood to read, [Laracasts](https://laracasts.com) contains over 900 video tutorials covering a range of topics including Laravel, modern PHP, unit testing, JavaScript, and more. Boost the skill level of yourself and your entire team by digging into our comprehensive video library.
If you're not in the mood to read, [Laracasts](https://laracasts.com) contains over 1100 video tutorials covering a range of topics including Laravel, modern PHP, unit testing, JavaScript, and more. Boost the skill level of yourself and your entire team by digging into our comprehensive video library.
## Contributing
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](http://laravel.com/docs/contributions).
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
@ -38,8 +38,8 @@ In order to ensure that the Laravel community is welcoming to all, please review
## Security Vulnerabilities
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell at taylor@laravel.com. All security vulnerabilities will be promptly addressed.
Please review [our security policy](https://github.com/laravel/framework/security/policy) on how to report security vulnerabilities.
## License
The Laravel framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT).
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

View file

@ -15,28 +15,32 @@
}
],
"require": {
"php": ">=5.6.4",
"php": "^7.1.3",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"doctrine/inflector": "~1.1",
"erusev/parsedown": "~1.6",
"league/flysystem": "~1.0",
"monolog/monolog": "~1.11",
"mtdowling/cron-expression": "~1.0",
"nesbot/carbon": "~1.20",
"paragonie/random_compat": "~1.4|~2.0",
"ramsey/uuid": "~3.0",
"swiftmailer/swiftmailer": "~5.4",
"symfony/console": "~3.2",
"symfony/debug": "~3.2",
"symfony/finder": "~3.2",
"symfony/http-foundation": "~3.2",
"symfony/http-kernel": "~3.2",
"symfony/process": "~3.2",
"symfony/routing": "~3.2",
"symfony/var-dumper": "~3.2",
"tijsverkoyen/css-to-inline-styles": "~2.2",
"vlucas/phpdotenv": "~2.2"
"doctrine/inflector": "^1.1",
"dragonmantank/cron-expression": "^2.0",
"egulias/email-validator": "^2.0",
"erusev/parsedown": "^1.7",
"league/flysystem": "^1.0.8",
"monolog/monolog": "^1.12",
"nesbot/carbon": "^1.26.3 || ^2.0",
"opis/closure": "^3.1",
"psr/container": "^1.0",
"psr/simple-cache": "^1.0",
"ramsey/uuid": "^3.7",
"swiftmailer/swiftmailer": "^6.0",
"symfony/console": "^4.2",
"symfony/debug": "^4.2",
"symfony/finder": "^4.2",
"symfony/http-foundation": "^4.2",
"symfony/http-kernel": "^4.2",
"symfony/process": "^4.2",
"symfony/routing": "^4.2",
"symfony/var-dumper": "^4.2",
"tijsverkoyen/css-to-inline-styles": "^2.2.1",
"vlucas/phpdotenv": "^3.3"
},
"replace": {
"illuminate/auth": "self.version",
@ -51,7 +55,6 @@
"illuminate/database": "self.version",
"illuminate/encryption": "self.version",
"illuminate/events": "self.version",
"illuminate/exception": "self.version",
"illuminate/filesystem": "self.version",
"illuminate/hashing": "self.version",
"illuminate/http": "self.version",
@ -67,18 +70,26 @@
"illuminate/support": "self.version",
"illuminate/translation": "self.version",
"illuminate/validation": "self.version",
"illuminate/view": "self.version",
"tightenco/collect": "self.version"
"illuminate/view": "self.version"
},
"conflict": {
"tightenco/collect": "<5.5.33"
},
"require-dev": {
"aws/aws-sdk-php": "~3.0",
"doctrine/dbal": "~2.5",
"mockery/mockery": "~0.9.4",
"pda/pheanstalk": "~3.0",
"phpunit/phpunit": "~5.7",
"predis/predis": "~1.0",
"symfony/css-selector": "~3.2",
"symfony/dom-crawler": "~3.2"
"aws/aws-sdk-php": "^3.0",
"doctrine/dbal": "^2.6",
"filp/whoops": "^2.1.4",
"guzzlehttp/guzzle": "^6.3",
"league/flysystem-cached-adapter": "^1.0",
"mockery/mockery": "^1.0",
"moontoast/math": "^1.1",
"orchestra/testbench-core": "3.8.*",
"pda/pheanstalk": "^4.0",
"phpunit/phpunit": "^7.5|^8.0",
"predis/predis": "^1.1.1",
"symfony/css-selector": "^4.2",
"symfony/dom-crawler": "^4.2",
"true/punycode": "^2.1"
},
"autoload": {
"files": [
@ -99,24 +110,32 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "5.8-dev"
}
},
"suggest": {
"aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~3.0).",
"doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.5).",
"fzaninotto/faker": "Required to use the eloquent factory builder (~1.4).",
"guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~6.0).",
"laravel/tinker": "Required to use the tinker console command (~1.0).",
"league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).",
"league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0).",
"nexmo/client": "Required to use the Nexmo transport (~1.0).",
"pda/pheanstalk": "Required to use the beanstalk queue driver (~3.0).",
"predis/predis": "Required to use the redis cache and queue drivers (~1.0).",
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).",
"symfony/css-selector": "Required to use some of the crawler integration testing tools (~3.2).",
"symfony/dom-crawler": "Required to use most of the crawler integration testing tools (~3.2).",
"symfony/psr-http-message-bridge": "Required to psr7 bridging features (0.2.*)."
"ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
"ext-pcntl": "Required to use all features of the queue worker.",
"ext-posix": "Required to use all features of the queue worker.",
"aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (^3.0).",
"doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).",
"filp/whoops": "Required for friendly error pages in development (^2.1.4).",
"fzaninotto/faker": "Required to use the eloquent factory builder (^1.4).",
"guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (^6.0).",
"laravel/tinker": "Required to use the tinker console command (^1.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-rackspace": "Required to use the Flysystem Rackspace driver (^1.0).",
"league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
"moontoast/math": "Required to use ordered UUIDs (^1.1).",
"nexmo/client": "Required to use the Nexmo transport (^1.0).",
"pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
"predis/predis": "Required to use the redis cache and queue drivers (^1.0).",
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0).",
"symfony/css-selector": "Required to use some of the crawler integration testing tools (^4.2).",
"symfony/dom-crawler": "Required to use most of the crawler integration testing tools (^4.2).",
"symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^1.1).",
"wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)."
},
"config": {
"sort-packages": true

View file

@ -2,6 +2,10 @@
namespace Illuminate\Auth\Access;
use Exception;
use ReflectionClass;
use ReflectionFunction;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Illuminate\Contracts\Container\Container;
@ -53,6 +57,20 @@ class Gate implements GateContract
*/
protected $afterCallbacks = [];
/**
* All of the defined abilities using class@method notation.
*
* @var array
*/
protected $stringCallbacks = [];
/**
* The callback to be used to guess policy names.
*
* @var callable|null
*/
protected $guessPolicyNamesUsingCallback;
/**
* Create a new gate instance.
*
@ -62,10 +80,12 @@ class Gate implements GateContract
* @param array $policies
* @param array $beforeCallbacks
* @param array $afterCallbacks
* @param callable|null $guessPolicyNamesUsingCallback
* @return void
*/
public function __construct(Container $container, callable $userResolver, array $abilities = [],
array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = [])
array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = [],
callable $guessPolicyNamesUsingCallback = null)
{
$this->policies = $policies;
$this->container = $container;
@ -73,17 +93,26 @@ class Gate implements GateContract
$this->userResolver = $userResolver;
$this->afterCallbacks = $afterCallbacks;
$this->beforeCallbacks = $beforeCallbacks;
$this->guessPolicyNamesUsingCallback = $guessPolicyNamesUsingCallback;
}
/**
* Determine if a given ability has been defined.
*
* @param string $ability
* @param string|array $ability
* @return bool
*/
public function has($ability)
{
return isset($this->abilities[$ability]);
$abilities = is_array($ability) ? $ability : func_get_args();
foreach ($abilities as $ability) {
if (! isset($this->abilities[$ability])) {
return false;
}
}
return true;
}
/**
@ -99,8 +128,10 @@ class Gate implements GateContract
{
if (is_callable($callback)) {
$this->abilities[$ability] = $callback;
} elseif (is_string($callback) && Str::contains($callback, '@')) {
$this->abilities[$ability] = $this->buildAbilityCallback($callback);
} elseif (is_string($callback)) {
$this->stringCallbacks[$ability] = $callback;
$this->abilities[$ability] = $this->buildAbilityCallback($ability, $callback);
} else {
throw new InvalidArgumentException("Callback must be a callable or a 'Class@method' string.");
}
@ -113,16 +144,17 @@ class Gate implements GateContract
*
* @param string $name
* @param string $class
* @param array $abilities
* @param array|null $abilities
* @return $this
*/
public function resource($name, $class, array $abilities = null)
{
$abilities = $abilities ?: [
'view' => 'view',
'create' => 'create',
'update' => 'update',
'delete' => 'delete',
'viewAny' => 'viewAny',
'view' => 'view',
'create' => 'create',
'update' => 'update',
'delete' => 'delete',
];
foreach ($abilities as $ability => $method) {
@ -135,15 +167,36 @@ class Gate implements GateContract
/**
* Create the ability callback for a callback string.
*
* @param string $ability
* @param string $callback
* @return \Closure
*/
protected function buildAbilityCallback($callback)
protected function buildAbilityCallback($ability, $callback)
{
return function () use ($callback) {
list($class, $method) = Str::parseCallback($callback);
return function () use ($ability, $callback) {
if (Str::contains($callback, '@')) {
[$class, $method] = Str::parseCallback($callback);
} else {
$class = $callback;
}
return $this->resolvePolicy($class)->{$method}(...func_get_args());
$policy = $this->resolvePolicy($class);
$arguments = func_get_args();
$user = array_shift($arguments);
$result = $this->callPolicyBefore(
$policy, $user, $ability, $arguments
);
if (! is_null($result)) {
return $result;
}
return isset($method)
? $policy->{$method}(...func_get_args())
: $policy(...func_get_args());
};
}
@ -212,19 +265,47 @@ class Gate implements GateContract
}
/**
* Determine if the given ability should be granted for the current user.
* Determine if all of the given abilities should be granted for the current user.
*
* @param string $ability
* @param iterable|string $abilities
* @param array|mixed $arguments
* @return bool
*/
public function check($ability, $arguments = [])
public function check($abilities, $arguments = [])
{
try {
return (bool) $this->raw($ability, $arguments);
} catch (AuthorizationException $e) {
return false;
}
return collect($abilities)->every(function ($ability) use ($arguments) {
try {
return (bool) $this->raw($ability, $arguments);
} catch (AuthorizationException $e) {
return false;
}
});
}
/**
* Determine if any one of the given abilities should be granted for the current user.
*
* @param iterable|string $abilities
* @param array|mixed $arguments
* @return bool
*/
public function any($abilities, $arguments = [])
{
return collect($abilities)->contains(function ($ability) use ($arguments) {
return $this->check($ability, $arguments);
});
}
/**
* Determine if all of the given abilities should be denied for the current user.
*
* @param iterable|string $abilities
* @param array|mixed $arguments
* @return bool
*/
public function none($abilities, $arguments = [])
{
return ! $this->any($abilities, $arguments);
}
/**
@ -254,13 +335,11 @@ class Gate implements GateContract
* @param array|mixed $arguments
* @return mixed
*/
protected function raw($ability, $arguments = [])
public function raw($ability, $arguments = [])
{
if (! $user = $this->resolveUser()) {
return false;
}
$arguments = Arr::wrap($arguments);
$arguments = array_wrap($arguments);
$user = $this->resolveUser();
// First we will call the "before" callbacks for the Gate. If any of these give
// back a non-null response, we will immediately return that result in order
@ -276,17 +355,95 @@ 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.
$this->callAfterCallbacks(
return $this->callAfterCallbacks(
$user, $ability, $arguments, $result
);
}
return $result;
/**
* Determine whether the callback/method can be called with the given user.
*
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param \Closure|string|array $class
* @param string|null $method
* @return bool
*/
protected function canBeCalledWithUser($user, $class, $method = null)
{
if (! is_null($user)) {
return true;
}
if (! is_null($method)) {
return $this->methodAllowsGuests($class, $method);
}
if (is_array($class)) {
$className = is_string($class[0]) ? $class[0] : get_class($class[0]);
return $this->methodAllowsGuests($className, $class[1]);
}
return $this->callbackAllowsGuests($class);
}
/**
* Determine if the given class method allows guests.
*
* @param string $class
* @param string $method
* @return bool
*/
protected function methodAllowsGuests($class, $method)
{
try {
$reflection = new ReflectionClass($class);
$method = $reflection->getMethod($method);
} catch (Exception $e) {
return false;
}
if ($method) {
$parameters = $method->getParameters();
return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]);
}
return false;
}
/**
* Determine if the callback allows guests.
*
* @param callable $callback
* @return bool
*
* @throws \ReflectionException
*/
protected function callbackAllowsGuests($callback)
{
$parameters = (new ReflectionFunction($callback))->getParameters();
return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]);
}
/**
* Determine if the given parameter allows guests.
*
* @param \ReflectionParameter $parameter
* @return bool
*/
protected function parameterAllowsGuests($parameter)
{
return ($parameter->getClass() && $parameter->allowsNull()) ||
($parameter->isDefaultValueAvailable() && is_null($parameter->getDefaultValue()));
}
/**
* Resolve and call the appropriate authorization callback.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param string $ability
* @param array $arguments
* @return bool
@ -301,17 +458,19 @@ class Gate implements GateContract
/**
* Call all of the before callbacks and return if a result is given.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param string $ability
* @param array $arguments
* @return bool|null
*/
protected function callBeforeCallbacks($user, $ability, array $arguments)
{
$arguments = array_merge([$user, $ability], [$arguments]);
foreach ($this->beforeCallbacks as $before) {
if (! is_null($result = $before(...$arguments))) {
if (! $this->canBeCalledWithUser($user, $before)) {
continue;
}
if (! is_null($result = $before($user, $ability, $arguments))) {
return $result;
}
}
@ -324,39 +483,53 @@ class Gate implements GateContract
* @param string $ability
* @param array $arguments
* @param bool $result
* @return void
* @return bool|null
*/
protected function callAfterCallbacks($user, $ability, array $arguments, $result)
{
$arguments = array_merge([$user, $ability, $result], [$arguments]);
foreach ($this->afterCallbacks as $after) {
$after(...$arguments);
if (! $this->canBeCalledWithUser($user, $after)) {
continue;
}
$afterResult = $after($user, $ability, $result, $arguments);
$result = $result ?? $afterResult;
}
return $result;
}
/**
* Resolve the callable for the given ability and arguments.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param string $ability
* @param array $arguments
* @return callable
*/
protected function resolveAuthCallback($user, $ability, array $arguments)
{
if (isset($arguments[0])) {
if (! is_null($policy = $this->getPolicyFor($arguments[0]))) {
return $this->resolvePolicyCallback($user, $ability, $arguments, $policy);
if (isset($arguments[0]) &&
! is_null($policy = $this->getPolicyFor($arguments[0])) &&
$callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) {
return $callback;
}
if (isset($this->stringCallbacks[$ability])) {
[$class, $method] = Str::parseCallback($this->stringCallbacks[$ability]);
if ($this->canBeCalledWithUser($user, $class, $method ?: '__invoke')) {
return $this->abilities[$ability];
}
}
if (isset($this->abilities[$ability])) {
if (isset($this->abilities[$ability]) &&
$this->canBeCalledWithUser($user, $this->abilities[$ability])) {
return $this->abilities[$ability];
}
return function () {
return false;
};
}
@ -373,13 +546,19 @@ class Gate implements GateContract
}
if (! is_string($class)) {
return null;
return;
}
if (isset($this->policies[$class])) {
return $this->resolvePolicy($this->policies[$class]);
}
foreach ($this->guessPolicyName($class) as $guessedPolicy) {
if (class_exists($guessedPolicy)) {
return $this->resolvePolicy($guessedPolicy);
}
}
foreach ($this->policies as $expected => $policy) {
if (is_subclass_of($class, $expected)) {
return $this->resolvePolicy($policy);
@ -387,11 +566,43 @@ class Gate implements GateContract
}
}
/**
* Guess the policy name for the given class.
*
* @param string $class
* @return array
*/
protected function guessPolicyName($class)
{
if ($this->guessPolicyNamesUsingCallback) {
return Arr::wrap(call_user_func($this->guessPolicyNamesUsingCallback, $class));
}
$classDirname = str_replace('/', '\\', dirname(str_replace('\\', '/', $class)));
return [$classDirname.'\\Policies\\'.class_basename($class).'Policy'];
}
/**
* Specify a callback to be used to guess policy names.
*
* @param callable $callback
* @return $this
*/
public function guessPolicyNamesUsing(callable $callback)
{
$this->guessPolicyNamesUsingCallback = $callback;
return $this;
}
/**
* Build a policy class instance of the given type.
*
* @param object|string $class
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function resolvePolicy($class)
{
@ -405,10 +616,14 @@ class Gate implements GateContract
* @param string $ability
* @param array $arguments
* @param mixed $policy
* @return callable
* @return bool|callable
*/
protected function resolvePolicyCallback($user, $ability, array $arguments, $policy)
{
if (! is_callable([$policy, $this->formatAbilityToMethod($ability)])) {
return false;
}
return function () use ($user, $ability, $arguments, $policy) {
// This callback will be responsible for calling the policy's before method and
// running this policy method if necessary. This is used to when objects are
@ -424,18 +639,9 @@ class Gate implements GateContract
return $result;
}
$ability = $this->formatAbilityToMethod($ability);
$method = $this->formatAbilityToMethod($ability);
// If this first argument is a string, that means they are passing a class name
// to the policy. We will remove the first argument from this argument array
// because this policy already knows what type of models it can authorize.
if (isset($arguments[0]) && is_string($arguments[0])) {
array_shift($arguments);
}
return is_callable([$policy, $ability])
? $policy->{$ability}($user, ...$arguments)
: false;
return $this->callPolicyMethod($policy, $method, $user, $arguments);
};
}
@ -450,11 +656,42 @@ class Gate implements GateContract
*/
protected function callPolicyBefore($policy, $user, $ability, $arguments)
{
if (method_exists($policy, 'before')) {
if (! method_exists($policy, 'before')) {
return;
}
if ($this->canBeCalledWithUser($user, $policy, 'before')) {
return $policy->before($user, $ability, ...$arguments);
}
}
/**
* Call the appropriate method on the given policy.
*
* @param mixed $policy
* @param string $method
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param array $arguments
* @return mixed
*/
protected function callPolicyMethod($policy, $method, $user, array $arguments)
{
// If this first argument is a string, that means they are passing a class name
// to the policy. We will remove the first argument from this argument array
// because this policy already knows what type of models it can authorize.
if (isset($arguments[0]) && is_string($arguments[0])) {
array_shift($arguments);
}
if (! is_callable([$policy, $method])) {
return;
}
if ($this->canBeCalledWithUser($user, $policy, $method)) {
return $policy->{$method}($user, ...$arguments);
}
}
/**
* Format the policy ability into a method name.
*
@ -480,7 +717,8 @@ class Gate implements GateContract
return new static(
$this->container, $callback, $this->abilities,
$this->policies, $this->beforeCallbacks, $this->afterCallbacks
$this->policies, $this->beforeCallbacks, $this->afterCallbacks,
$this->guessPolicyNamesUsingCallback
);
}
@ -503,4 +741,14 @@ class Gate implements GateContract
{
return $this->abilities;
}
/**
* Get all of the defined policies.
*
* @return array
*/
public function policies()
{
return $this->policies;
}
}

View file

@ -15,6 +15,7 @@ class Response
* Create a new response.
*
* @param string|null $message
* @return void
*/
public function __construct($message = null)
{
@ -38,6 +39,6 @@ class Response
*/
public function __toString()
{
return $this->message();
return (string) $this->message();
}
}

View file

@ -13,7 +13,7 @@ class AuthManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
@ -43,7 +43,7 @@ class AuthManager implements FactoryContract
/**
* Create a new Auth manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
@ -58,16 +58,14 @@ class AuthManager implements FactoryContract
/**
* Attempt to get the guard from the local cache.
*
* @param string $name
* @param string|null $name
* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
*/
public function guard($name = null)
{
$name = $name ?: $this->getDefaultDriver();
return isset($this->guards[$name])
? $this->guards[$name]
: $this->guards[$name] = $this->resolve($name);
return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
}
/**
@ -96,7 +94,9 @@ class AuthManager implements FactoryContract
return $this->{$driverMethod}($name, $config);
}
throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
throw new InvalidArgumentException(
"Auth driver [{$config['driver']}] for guard [{$name}] is not defined."
);
}
/**
@ -120,7 +120,7 @@ class AuthManager implements FactoryContract
*/
public function createSessionDriver($name, $config)
{
$provider = $this->createUserProvider($config['provider']);
$provider = $this->createUserProvider($config['provider'] ?? null);
$guard = new SessionGuard($name, $provider, $this->app['session.store']);
@ -155,8 +155,11 @@ class AuthManager implements FactoryContract
// that takes an API token field from the request and matches it to the
// user in the database or another persistence layer where users are.
$guard = new TokenGuard(
$this->createUserProvider($config['provider']),
$this->app['request']
$this->createUserProvider($config['provider'] ?? null),
$this->app['request'],
$config['input_key'] ?? 'api_token',
$config['storage_key'] ?? 'api_token',
$config['hash'] ?? false
);
$this->app->refresh('request', $guard, 'setRequest');
@ -223,7 +226,7 @@ class AuthManager implements FactoryContract
public function viaRequest($driver, callable $callback)
{
return $this->extend($driver, function () use ($callback) {
$guard = new RequestGuard($callback, $this->app['request']);
$guard = new RequestGuard($callback, $this->app['request'], $this->createUserProvider());
$this->app->refresh('request', $guard, 'setRequest');

View file

@ -17,12 +17,10 @@ class AuthServiceProvider extends ServiceProvider
public function register()
{
$this->registerAuthenticator();
$this->registerUserResolver();
$this->registerAccessGate();
$this->registerRequestRebindHandler();
$this->registerEventRebindHandler();
}
/**
@ -75,7 +73,7 @@ class AuthServiceProvider extends ServiceProvider
}
/**
* Register a resolver for the authenticated user.
* Handle the re-binding of the request binding.
*
* @return void
*/
@ -87,4 +85,22 @@ class AuthServiceProvider extends ServiceProvider
});
});
}
/**
* Handle the re-binding of the event dispatcher binding.
*
* @return void
*/
protected function registerEventRebindHandler()
{
$this->app->rebinding('events', function ($app, $dispatcher) {
if (! $app->resolved('auth')) {
return;
}
if (method_exists($guard = $app['auth']->guard(), 'setDispatcher')) {
$guard->setDispatcher($dispatcher);
}
});
}
}

View file

@ -44,12 +44,12 @@ trait Authenticatable
/**
* Get the token value for the "remember me" session.
*
* @return string
* @return string|null
*/
public function getRememberToken()
{
if (! empty($this->getRememberTokenName())) {
return $this->{$this->getRememberTokenName()};
return (string) $this->{$this->getRememberTokenName()};
}
}

View file

@ -13,18 +13,27 @@ class AuthenticationException extends Exception
*/
protected $guards;
/**
* The path the user should be redirected to.
*
* @var string
*/
protected $redirectTo;
/**
* Create a new authentication exception.
*
* @param string $message
* @param array $guards
* @param string|null $redirectTo
* @return void
*/
public function __construct($message = 'Unauthenticated.', array $guards = [])
public function __construct($message = 'Unauthenticated.', array $guards = [], $redirectTo = null)
{
parent::__construct($message);
$this->guards = $guards;
$this->redirectTo = $redirectTo;
}
/**
@ -36,4 +45,14 @@ class AuthenticationException extends Exception
{
return $this->guards;
}
/**
* Get the path the user should be redirected to.
*
* @return string
*/
public function redirectTo()
{
return $this->redirectTo;
}
}

View file

@ -25,7 +25,7 @@ class ClearResetsCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
$this->laravel['auth.password']->broker($this->argument('name'))->getRepository()->deleteExpired();

View file

@ -19,7 +19,7 @@ class HomeController extends Controller
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{

View file

@ -2,62 +2,66 @@
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Login</div>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Login') }}</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="{{ route('login') }}">
{{ csrf_field() }}
<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required autofocus>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label for="password" class="col-md-4 control-label">Password</label>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password" required>
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="checkbox">
<label>
<input type="checkbox" name="remember" {{ old('remember') ? 'checked' : '' }}> Remember Me
<div class="form-group row">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-8 col-md-offset-4">
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
Login
{{ __('Login') }}
</button>
<a class="btn btn-link" href="{{ route('password.request') }}">
Forgot Your Password?
</a>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>

View file

@ -2,39 +2,39 @@
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Reset Password</div>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="panel-body">
<div class="card-body">
@if (session('status'))
<div class="alert alert-success">
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<form class="form-horizontal" method="POST" action="{{ route('password.email') }}">
{{ csrf_field() }}
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
Send Password Reset Link
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>

View file

@ -2,62 +2,57 @@
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Reset Password</div>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="{{ route('password.request') }}">
{{ csrf_field() }}
<div class="card-body">
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control" name="email" value="{{ $email or old('email') }}" required autofocus>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label for="password" class="col-md-4 control-label">Password</label>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password" required>
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
<div class="form-group{{ $errors->has('password_confirmation') ? ' has-error' : '' }}">
<label for="password-confirm" class="col-md-4 control-label">Confirm Password</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
@if ($errors->has('password_confirmation'))
<span class="help-block">
<strong>{{ $errors->first('password_confirmation') }}</strong>
</span>
@endif
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
Reset Password
{{ __('Reset Password') }}
</button>
</div>
</div>

View file

@ -2,69 +2,69 @@
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Register</div>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Register') }}</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="{{ route('register') }}">
{{ csrf_field() }}
<div class="card-body">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
<label for="name" class="col-md-4 control-label">Name</label>
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control" name="name" value="{{ old('name') }}" required autofocus>
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('name') }}</strong>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label for="password" class="col-md-4 control-label">Password</label>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password" required>
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
<div class="form-group">
<label for="password-confirm" class="col-md-4 control-label">Confirm Password</label>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
Register
{{ __('Register') }}
</button>
</div>
</div>

View file

@ -2,14 +2,14 @@
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Dashboard</div>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Dashboard</div>
<div class="panel-body">
<div class="card-body">
@if (session('status'))
<div class="alert alert-success">
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif

View file

@ -1,8 +1,7 @@
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
@ -10,71 +9,72 @@
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Scripts -->
<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">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default navbar-static-top">
<nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Collapsed Hamburger -->
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- Branding Image -->
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
</div>
<div class="collapse navbar-collapse" id="app-navbar-collapse">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="nav navbar-nav">
&nbsp;
<ul class="navbar-nav mr-auto">
</ul>
<!-- Right Side Of Navbar -->
<ul class="nav navbar-nav navbar-right">
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@if (Auth::guest())
<li><a href="{{ route('login') }}">Login</a></li>
<li><a href="{{ route('register') }}">Register</a></li>
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li>
<a href="{{ route('logout') }}"
onclick="event.preventDefault();
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
Logout
</a>
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
</li>
</ul>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
</li>
@endif
@endguest
</ul>
</div>
</div>
</nav>
@yield('content')
<main class="py-4">
@yield('content')
</main>
</div>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>

View file

@ -16,28 +16,45 @@ trait CreatesUserProviders
/**
* Create the user provider implementation for the driver.
*
* @param string $provider
* @return \Illuminate\Contracts\Auth\UserProvider
* @param string|null $provider
* @return \Illuminate\Contracts\Auth\UserProvider|null
*
* @throws \InvalidArgumentException
*/
public function createUserProvider($provider)
public function createUserProvider($provider = null)
{
$config = $this->app['config']['auth.providers.'.$provider];
if (is_null($config = $this->getProviderConfiguration($provider))) {
return;
}
if (isset($this->customProviderCreators[$config['driver']])) {
if (isset($this->customProviderCreators[$driver = ($config['driver'] ?? null)])) {
return call_user_func(
$this->customProviderCreators[$config['driver']], $this->app, $config
$this->customProviderCreators[$driver], $this->app, $config
);
}
switch ($config['driver']) {
switch ($driver) {
case 'database':
return $this->createDatabaseProvider($config);
case 'eloquent':
return $this->createEloquentProvider($config);
default:
throw new InvalidArgumentException("Authentication user provider [{$config['driver']}] is not defined.");
throw new InvalidArgumentException(
"Authentication user provider [{$driver}] is not defined."
);
}
}
/**
* Get the user provider configuration.
*
* @param string|null $provider
* @return array|null
*/
protected function getProviderConfiguration($provider)
{
if ($provider = $provider ?: $this->getDefaultUserProvider()) {
return $this->app['config']['auth.providers.'.$provider];
}
}
@ -64,4 +81,14 @@ trait CreatesUserProviders
{
return new EloquentUserProvider($this->app['hash'], $config['model']);
}
/**
* Get the default user provider name.
*
* @return string
*/
public function getDefaultUserProvider()
{
return $this->app['config']['auth.defaults.provider'];
}
}

View file

@ -4,6 +4,7 @@ namespace Illuminate\Auth;
use Illuminate\Support\Str;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
@ -68,12 +69,12 @@ class DatabaseUserProvider implements UserProvider
*/
public function retrieveByToken($identifier, $token)
{
$user = $this->conn->table($this->table)
->where('id', $identifier)
->where('remember_token', $token)
->first();
$user = $this->getGenericUser(
$this->conn->table($this->table)->find($identifier)
);
return $this->getGenericUser($user);
return $user && $user->getRememberToken() && hash_equals($user->getRememberToken(), $token)
? $user : null;
}
/**
@ -86,8 +87,8 @@ class DatabaseUserProvider implements UserProvider
public function updateRememberToken(UserContract $user, $token)
{
$this->conn->table($this->table)
->where('id', $user->getAuthIdentifier())
->update(['remember_token' => $token]);
->where($user->getAuthIdentifierName(), $user->getAuthIdentifier())
->update([$user->getRememberTokenName() => $token]);
}
/**
@ -98,13 +99,25 @@ class DatabaseUserProvider implements UserProvider
*/
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// generic "user" object that will be utilized by the Guard instances.
$query = $this->conn->table($this->table);
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
if (Str::contains($key, 'password')) {
continue;
}
if (is_array($value) || $value instanceof Arrayable) {
$query->whereIn($key, $value);
} else {
$query->where($key, $value);
}
}

View file

@ -4,6 +4,7 @@ namespace Illuminate\Auth;
use Illuminate\Support\Str;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
@ -46,9 +47,9 @@ class EloquentUserProvider implements UserProvider
{
$model = $this->createModel();
return $model->newQuery()
->where($model->getAuthIdentifierName(), $identifier)
->first();
return $this->newModelQuery($model)
->where($model->getAuthIdentifierName(), $identifier)
->first();
}
/**
@ -62,16 +63,24 @@ class EloquentUserProvider implements UserProvider
{
$model = $this->createModel();
return $model->newQuery()
->where($model->getAuthIdentifierName(), $identifier)
->where($model->getRememberTokenName(), $token)
->first();
$retrievedModel = $this->newModelQuery($model)->where(
$model->getAuthIdentifierName(), $identifier
)->first();
if (! $retrievedModel) {
return;
}
$rememberToken = $retrievedModel->getRememberToken();
return $rememberToken && hash_equals($rememberToken, $token)
? $retrievedModel : null;
}
/**
* Update the "remember me" token for the given user in storage.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param \Illuminate\Contracts\Auth\Authenticatable|\Illuminate\Database\Eloquent\Model $user
* @param string $token
* @return void
*/
@ -96,17 +105,25 @@ class EloquentUserProvider implements UserProvider
*/
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials)) {
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
$query = $this->newModelQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
if (Str::contains($key, 'password')) {
continue;
}
if (is_array($value) || $value instanceof Arrayable) {
$query->whereIn($key, $value);
} else {
$query->where($key, $value);
}
}
@ -128,6 +145,19 @@ class EloquentUserProvider implements UserProvider
return $this->hasher->check($plain, $user->getAuthPassword());
}
/**
* Get a new query builder for the model instance.
*
* @param \Illuminate\Database\Eloquent\Model|null $model
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function newModelQuery($model = null)
{
return is_null($model)
? $this->createModel()->newQuery()
: $model->newQuery();
}
/**
* Create a new instance of the model.
*

View file

@ -4,6 +4,13 @@ namespace Illuminate\Auth\Events;
class Attempting
{
/**
* The authentication guard name.
*
* @var string
*/
public $guard;
/**
* The credentials for the user.
*
@ -21,11 +28,14 @@ class Attempting
/**
* Create a new event instance.
*
* @param string $guard
* @param array $credentials
* @param bool $remember
* @return void
*/
public function __construct($credentials, $remember)
public function __construct($guard, $credentials, $remember)
{
$this->guard = $guard;
$this->remember = $remember;
$this->credentials = $credentials;
}

View file

@ -8,6 +8,13 @@ class Authenticated
{
use SerializesModels;
/**
* The authentication guard name.
*
* @var string
*/
public $guard;
/**
* The authenticated user.
*
@ -18,11 +25,13 @@ class Authenticated
/**
* Create a new event instance.
*
* @param string $guard
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function __construct($user)
public function __construct($guard, $user)
{
$this->user = $user;
$this->guard = $guard;
}
}

View file

@ -4,6 +4,13 @@ namespace Illuminate\Auth\Events;
class Failed
{
/**
* The authentication guard name.
*
* @var string
*/
public $guard;
/**
* The user the attempter was trying to authenticate as.
*
@ -21,12 +28,15 @@ class Failed
/**
* Create a new event instance.
*
* @param string $guard
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param array $credentials
* @return void
*/
public function __construct($user, $credentials)
public function __construct($guard, $user, $credentials)
{
$this->user = $user;
$this->guard = $guard;
$this->credentials = $credentials;
}
}

View file

@ -8,6 +8,13 @@ class Login
{
use SerializesModels;
/**
* The authentication guard name.
*
* @var string
*/
public $guard;
/**
* The authenticated user.
*
@ -25,13 +32,15 @@ class Login
/**
* Create a new event instance.
*
* @param string $guard
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param bool $remember
* @return void
*/
public function __construct($user, $remember)
public function __construct($guard, $user, $remember)
{
$this->user = $user;
$this->guard = $guard;
$this->remember = $remember;
}
}

View file

@ -8,6 +8,13 @@ class Logout
{
use SerializesModels;
/**
* The authentication guard name.
*
* @var string
*/
public $guard;
/**
* The authenticated user.
*
@ -18,11 +25,13 @@ class Logout
/**
* Create a new event instance.
*
* @param string $guard
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function __construct($user)
public function __construct($guard, $user)
{
$this->user = $user;
$this->guard = $guard;
}
}

View file

@ -2,6 +2,7 @@
namespace Illuminate\Auth;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
/**
@ -24,7 +25,7 @@ trait GuardHelpers
protected $provider;
/**
* Determine if the current user is authenticated.
* Determine if current user is authenticated. If not, throw an exception.
*
* @return \Illuminate\Contracts\Auth\Authenticatable
*
@ -39,6 +40,16 @@ trait GuardHelpers
throw new AuthenticationException;
}
/**
* Determine if the guard has a user instance.
*
* @return bool
*/
public function hasUser()
{
return ! is_null($this->user);
}
/**
* Determine if the current user is authenticated.
*
@ -83,4 +94,25 @@ trait GuardHelpers
return $this;
}
/**
* Get the user provider used by the guard.
*
* @return \Illuminate\Contracts\Auth\UserProvider
*/
public function getProvider()
{
return $this->provider;
}
/**
* Set the user provider used by the guard.
*
* @param \Illuminate\Contracts\Auth\UserProvider $provider
* @return void
*/
public function setProvider(UserProvider $provider)
{
$this->provider = $provider;
}
}

View file

@ -38,7 +38,7 @@ class Authenticate
*/
public function handle($request, Closure $next, ...$guards)
{
$this->authenticate($guards);
$this->authenticate($request, $guards);
return $next($request);
}
@ -46,15 +46,16 @@ class Authenticate
/**
* Determine if the user is logged in to any of the given guards.
*
* @param \Illuminate\Http\Request $request
* @param array $guards
* @return void
*
* @throws \Illuminate\Auth\AuthenticationException
*/
protected function authenticate(array $guards)
protected function authenticate($request, array $guards)
{
if (empty($guards)) {
return $this->auth->authenticate();
$guards = [null];
}
foreach ($guards as $guard) {
@ -63,6 +64,19 @@ class Authenticate
}
}
throw new AuthenticationException('Unauthenticated.', $guards);
throw new AuthenticationException(
'Unauthenticated.', $guards, $this->redirectTo($request)
);
}
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string
*/
protected function redirectTo($request)
{
//
}
}

View file

@ -31,10 +31,15 @@ class AuthenticateWithBasicAuth
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @param string|null $field
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
*/
public function handle($request, Closure $next, $guard = null)
public function handle($request, Closure $next, $guard = null, $field = null)
{
return $this->auth->guard($guard)->basic() ?: $next($request);
$this->auth->guard($guard)->basic($field ?: 'email');
return $next($request);
}
}

View file

@ -5,17 +5,9 @@ namespace Illuminate\Auth\Middleware;
use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authorize
{
/**
* The authentication factory instance.
*
* @var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* The gate instance.
*
@ -26,13 +18,11 @@ class Authorize
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Auth\Factory $auth
* @param \Illuminate\Contracts\Auth\Access\Gate $gate
* @return void
*/
public function __construct(Auth $auth, Gate $gate)
public function __construct(Gate $gate)
{
$this->auth = $auth;
$this->gate = $gate;
}
@ -42,7 +32,7 @@ class Authorize
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $ability
* @param array|null $models
* @param array|null ...$models
* @return mixed
*
* @throws \Illuminate\Auth\AuthenticationException
@ -50,8 +40,6 @@ class Authorize
*/
public function handle($request, Closure $next, $ability, ...$models)
{
$this->auth->authenticate();
$this->gate->authorize($ability, $this->getGateArguments($request, $models));
return $next($request);
@ -80,11 +68,16 @@ class Authorize
*
* @param \Illuminate\Http\Request $request
* @param string $model
* @return string|\Illuminate\Database\Eloquent\Model
* @return \Illuminate\Database\Eloquent\Model|string
*/
protected function getModel($request, $model)
{
return $this->isClassName($model) ? $model : $request->route($model);
if ($this->isClassName($model)) {
return trim($model);
} else {
return $request->route($model, null) ?:
((preg_match("/^['\"](.*)['\"]$/", trim($model), $matches)) ? $matches[1] : null);
}
}
/**

View file

@ -2,6 +2,7 @@
namespace Illuminate\Auth\Notifications;
use Illuminate\Support\Facades\Lang;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
@ -14,6 +15,13 @@ class ResetPassword extends Notification
*/
public $token;
/**
* The callback that should be used to build the mail message.
*
* @var \Closure|null
*/
public static $toMailCallback;
/**
* Create a notification instance.
*
@ -44,9 +52,26 @@ class ResetPassword extends Notification
*/
public function toMail($notifiable)
{
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $this->token);
}
return (new MailMessage)
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url(config('app.url').route('password.reset', $this->token, false)))
->line('If you did not request a password reset, no further action is required.');
->subject(Lang::getFromJson('Reset Password Notification'))
->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.'))
->action(Lang::getFromJson('Reset Password'), url(config('app.url').route('password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->line(Lang::getFromJson('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
->line(Lang::getFromJson('If you did not request a password reset, no further action is required.'));
}
/**
* Set a callback that should be used when building the notification mail message.
*
* @param \Closure $callback
* @return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}

View file

@ -2,8 +2,8 @@
namespace Illuminate\Auth\Passwords;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Illuminate\Support\Carbon;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

View file

@ -107,7 +107,7 @@ class PasswordBroker implements PasswordBrokerContract
* Validate a password reset for the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\CanResetPassword
* @return \Illuminate\Contracts\Auth\CanResetPassword|string
*/
protected function validateReset(array $credentials)
{
@ -146,7 +146,7 @@ class PasswordBroker implements PasswordBrokerContract
public function validateNewPassword(array $credentials)
{
if (isset($this->passwordValidator)) {
list($password, $confirm) = [
[$password, $confirm] = [
$credentials['password'],
$credentials['password_confirmation'],
];
@ -167,19 +167,19 @@ class PasswordBroker implements PasswordBrokerContract
*/
protected function validatePasswordWithDefaults(array $credentials)
{
list($password, $confirm) = [
[$password, $confirm] = [
$credentials['password'],
$credentials['password_confirmation'],
];
return $password === $confirm && mb_strlen($password) >= 6;
return $password === $confirm && mb_strlen($password) >= 8;
}
/**
* Get the user for the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\CanResetPassword
* @return \Illuminate\Contracts\Auth\CanResetPassword|null
*
* @throws \UnexpectedValueException
*/
@ -199,7 +199,7 @@ class PasswordBroker implements PasswordBrokerContract
/**
* Create a new password reset token for the given user.
*
* @param CanResetPasswordContract $user
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return string
*/
public function createToken(CanResetPasswordContract $user)
@ -221,7 +221,7 @@ class PasswordBroker implements PasswordBrokerContract
/**
* Validate the given password reset token.
*
* @param CanResetPasswordContract $user
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $token
* @return bool
*/

View file

@ -14,7 +14,7 @@ class PasswordBrokerManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
@ -28,7 +28,7 @@ class PasswordBrokerManager implements FactoryContract
/**
* Create a new PasswordBroker manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
@ -39,16 +39,14 @@ class PasswordBrokerManager implements FactoryContract
/**
* Attempt to get the broker from the local cache.
*
* @param string $name
* @param string|null $name
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/
public function broker($name = null)
{
$name = $name ?: $this->getDefaultDriver();
return isset($this->brokers[$name])
? $this->brokers[$name]
: $this->brokers[$name] = $this->resolve($name);
return $this->brokers[$name] ?? ($this->brokers[$name] = $this->resolve($name));
}
/**
@ -72,7 +70,7 @@ class PasswordBrokerManager implements FactoryContract
// aggregate service of sorts providing a convenient interface for resets.
return new PasswordBroker(
$this->createTokenRepository($config),
$this->app['auth']->createUserProvider($config['provider'])
$this->app['auth']->createUserProvider($config['provider'] ?? null)
);
}
@ -90,7 +88,7 @@ class PasswordBrokerManager implements FactoryContract
$key = base64_decode(substr($key, 7));
}
$connection = isset($config['connection']) ? $config['connection'] : null;
$connection = $config['connection'] ?? null;
return new DatabaseTokenRepository(
$this->app['db']->connection($connection),

View file

@ -3,16 +3,10 @@
namespace Illuminate\Auth\Passwords;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
class PasswordResetServiceProvider extends ServiceProvider
class PasswordResetServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*

View file

@ -21,7 +21,7 @@ class Recaller
*/
public function __construct($recaller)
{
$this->recaller = $recaller;
$this->recaller = @unserialize($recaller, ['allowed_classes' => false]) ?: $recaller;
}
/**
@ -31,7 +31,7 @@ class Recaller
*/
public function id()
{
return explode('|', $this->recaller, 2)[0];
return explode('|', $this->recaller, 3)[0];
}
/**
@ -41,7 +41,17 @@ class Recaller
*/
public function token()
{
return explode('|', $this->recaller, 2)[1];
return explode('|', $this->recaller, 3)[1];
}
/**
* Get the password from the recaller.
*
* @return string
*/
public function hash()
{
return explode('|', $this->recaller, 3)[2];
}
/**
@ -51,7 +61,7 @@ class Recaller
*/
public function valid()
{
return $this->properString() && $this->hasBothSegments();
return $this->properString() && $this->hasAllSegments();
}
/**
@ -65,14 +75,14 @@ class Recaller
}
/**
* Determine if the recaller has both segments.
* Determine if the recaller has all segments.
*
* @return bool
*/
protected function hasBothSegments()
protected function hasAllSegments()
{
$segments = explode('|', $this->recaller);
return count($segments) == 2 && trim($segments[0]) !== '' && trim($segments[1]) !== '';
return count($segments) === 3 && trim($segments[0]) !== '' && trim($segments[1]) !== '';
}
}

View file

@ -5,6 +5,7 @@ namespace Illuminate\Auth;
use Illuminate\Http\Request;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Auth\UserProvider;
class RequestGuard implements Guard
{
@ -29,12 +30,14 @@ class RequestGuard implements Guard
*
* @param callable $callback
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Contracts\Auth\UserProvider|null $provider
* @return void
*/
public function __construct(callable $callback, Request $request)
public function __construct(callable $callback, Request $request, UserProvider $provider = null)
{
$this->request = $request;
$this->callback = $callback;
$this->provider = $provider;
}
/**
@ -52,7 +55,7 @@ class RequestGuard implements Guard
}
return $this->user = call_user_func(
$this->callback, $this->request
$this->callback, $this->request, $this->getProvider()
);
}
@ -65,7 +68,7 @@ class RequestGuard implements Guard
public function validate(array $credentials = [])
{
return ! is_null((new static(
$this->callback, $credentials['request']
$this->callback, $credentials['request'], $this->getProvider()
))->user());
}

View file

@ -4,7 +4,7 @@ namespace Illuminate\Auth;
use RuntimeException;
use Illuminate\Support\Str;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Session\Session;
use Illuminate\Contracts\Auth\UserProvider;
@ -13,6 +13,7 @@ use Illuminate\Contracts\Auth\StatefulGuard;
use Symfony\Component\HttpFoundation\Request;
use Illuminate\Contracts\Auth\SupportsBasicAuth;
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
class SessionGuard implements StatefulGuard, SupportsBasicAuth
@ -90,7 +91,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
* @param string $name
* @param \Illuminate\Contracts\Auth\UserProvider $provider
* @param \Illuminate\Contracts\Session\Session $session
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Symfony\Component\HttpFoundation\Request|null $request
* @return void
*/
public function __construct($name,
@ -127,30 +128,24 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
$user = null;
if (! is_null($id)) {
if ($user = $this->provider->retrieveById($id)) {
$this->fireAuthenticatedEvent($user);
}
if (! is_null($id) && $this->user = $this->provider->retrieveById($id)) {
$this->fireAuthenticatedEvent($this->user);
}
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
$recaller = $this->recaller();
if (is_null($this->user) && ! is_null($recaller = $this->recaller())) {
$this->user = $this->userFromRecaller($recaller);
if (is_null($user) && ! is_null($recaller)) {
$user = $this->userFromRecaller($recaller);
if ($this->user) {
$this->updateSession($this->user->getAuthIdentifier());
if ($user) {
$this->updateSession($user->getAuthIdentifier());
$this->fireLoginEvent($user, true);
$this->fireLoginEvent($this->user, true);
}
}
return $this->user = $user;
return $this->user;
}
/**
@ -331,11 +326,13 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
/**
* Get the response for basic authentication.
*
* @return \Symfony\Component\HttpFoundation\Response
* @return void
*
* @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
*/
protected function failedBasicResponse()
{
return new Response('Invalid credentials.', 401, ['WWW-Authenticate' => 'Basic']);
throw new UnauthorizedHttpException('Basic', 'Invalid credentials.');
}
/**
@ -461,7 +458,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function queueRecallerCookie(AuthenticatableContract $user)
{
$this->getCookieJar()->queue($this->createRecaller(
$user->getAuthIdentifier().'|'.$user->getRememberToken()
$user->getAuthIdentifier().'|'.$user->getRememberToken().'|'.$user->getAuthPassword()
));
}
@ -490,12 +487,12 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
// listening for anytime a user signs out of this application manually.
$this->clearUserDataFromStorage();
if (! is_null($this->user)) {
if (! is_null($this->user) && ! empty($user->getRememberToken())) {
$this->cycleRememberToken($user);
}
if (isset($this->events)) {
$this->events->dispatch(new Events\Logout($user));
$this->events->dispatch(new Events\Logout($this->name, $user));
}
// Once we have fired the logout event we will clear the users out of memory
@ -534,6 +531,35 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
$this->provider->updateRememberToken($user, $token);
}
/**
* Invalidate other sessions for the current user.
*
* The application must be using the AuthenticateSession middleware.
*
* @param string $password
* @param string $attribute
* @return bool|null
*/
public function logoutOtherDevices($password, $attribute = 'password')
{
if (! $this->user()) {
return;
}
$result = tap($this->user()->forceFill([
$attribute => Hash::make($password),
]))->save();
if ($this->recaller() ||
$this->getCookieJar()->hasQueued($this->getRecallerName())) {
$this->queueRecallerCookie($this->user());
}
$this->fireOtherDeviceLogoutEvent($this->user());
return $result;
}
/**
* Register an authentication attempt event listener.
*
@ -558,7 +584,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Attempting(
$credentials, $remember
$this->name, $credentials, $remember
));
}
}
@ -573,7 +599,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireLoginEvent($user, $remember = false)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Login($user, $remember));
$this->events->dispatch(new Events\Login(
$this->name, $user, $remember
));
}
}
@ -586,7 +614,24 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireAuthenticatedEvent($user)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Authenticated($user));
$this->events->dispatch(new Events\Authenticated(
$this->name, $user
));
}
}
/**
* Fire the other device logout event if the dispatcher is set.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
protected function fireOtherDeviceLogoutEvent($user)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\OtherDeviceLogout(
$this->name, $user
));
}
}
@ -600,7 +645,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireFailedEvent($user, array $credentials)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Failed($user, $credentials));
$this->events->dispatch(new Events\Failed(
$this->name, $user, $credentials
));
}
}
@ -695,34 +742,13 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
/**
* Get the session store used by the guard.
*
* @return \Illuminate\Session\Store
* @return \Illuminate\Contracts\Session\Session
*/
public function getSession()
{
return $this->session;
}
/**
* Get the user provider used by the guard.
*
* @return \Illuminate\Contracts\Auth\UserProvider
*/
public function getProvider()
{
return $this->provider;
}
/**
* Set the user provider used by the guard.
*
* @param \Illuminate\Contracts\Auth\UserProvider $provider
* @return void
*/
public function setProvider(UserProvider $provider)
{
$this->provider = $provider;
}
/**
* Return the currently cached user.
*

View file

@ -31,19 +31,35 @@ class TokenGuard implements Guard
*/
protected $storageKey;
/**
* Indicates if the API token is hashed in storage.
*
* @var bool
*/
protected $hash = false;
/**
* Create a new authentication guard.
*
* @param \Illuminate\Contracts\Auth\UserProvider $provider
* @param \Illuminate\Http\Request $request
* @param string $inputKey
* @param string $storageKey
* @param bool $hash
* @return void
*/
public function __construct(UserProvider $provider, Request $request)
public function __construct(
UserProvider $provider,
Request $request,
$inputKey = 'api_token',
$storageKey = 'api_token',
$hash = false)
{
$this->hash = $hash;
$this->request = $request;
$this->provider = $provider;
$this->inputKey = 'api_token';
$this->storageKey = 'api_token';
$this->inputKey = $inputKey;
$this->storageKey = $storageKey;
}
/**
@ -65,9 +81,9 @@ class TokenGuard implements Guard
$token = $this->getTokenForRequest();
if (! empty($token)) {
$user = $this->provider->retrieveByCredentials(
[$this->storageKey => $token]
);
$user = $this->provider->retrieveByCredentials([
$this->storageKey => $this->hash ? hash('sha256', $token) : $token,
]);
}
return $this->user = $user;

View file

@ -14,12 +14,11 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/contracts": "5.4.*",
"illuminate/http": "5.4.*",
"illuminate/queue": "5.4.*",
"illuminate/support": "5.4.*",
"nesbot/carbon": "~1.20"
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/http": "5.8.*",
"illuminate/queue": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@ -28,13 +27,13 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "5.8-dev"
}
},
"suggest": {
"illuminate/console": "Required to use the auth:clear-resets command (5.4.*).",
"illuminate/queue": "Required to fire login / logout events (5.4.*).",
"illuminate/session": "Required to use the session based guard (5.4.*)."
"illuminate/console": "Required to use the auth:clear-resets command (5.8.*).",
"illuminate/queue": "Required to fire login / logout events (5.8.*).",
"illuminate/session": "Required to use the session based guard (5.8.*)."
},
"config": {
"sort-packages": true

View file

@ -16,6 +16,10 @@ class BroadcastController extends Controller
*/
public function authenticate(Request $request)
{
if ($request->hasSession()) {
$request->session()->reflash();
}
return Broadcast::auth($request);
}
}

View file

@ -4,8 +4,8 @@ namespace Illuminate\Broadcasting;
use ReflectionClass;
use ReflectionProperty;
use Illuminate\Support\Arr;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\Job;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Broadcasting\Broadcaster;
@ -44,7 +44,7 @@ class BroadcastEvent implements ShouldQueue
? $this->event->broadcastAs() : get_class($this->event);
$broadcaster->broadcast(
array_wrap($this->event->broadcastOn()), $name,
Arr::wrap($this->event->broadcastOn()), $name,
$this->getPayloadFromEvent($this->event)
);
}
@ -98,4 +98,14 @@ class BroadcastEvent implements ShouldQueue
{
return get_class($this->event);
}
/**
* Prepare the instance for cloning.
*
* @return void
*/
public function __clone()
{
$this->event = clone $this->event;
}
}

View file

@ -2,9 +2,8 @@
namespace Illuminate\Broadcasting;
use Pusher;
use Closure;
use Illuminate\Support\Arr;
use Pusher\Pusher;
use Psr\Log\LoggerInterface;
use InvalidArgumentException;
use Illuminate\Broadcasting\Broadcasters\LogBroadcaster;
@ -22,7 +21,7 @@ class BroadcastManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
@ -43,7 +42,7 @@ class BroadcastManager implements FactoryContract
/**
* Create a new manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
@ -66,7 +65,10 @@ class BroadcastManager implements FactoryContract
$attributes = $attributes ?: ['middleware' => ['web']];
$this->app['router']->group($attributes, function ($router) {
$router->post('/broadcasting/auth', BroadcastController::class.'@authenticate');
$router->match(
['get', 'post'], '/broadcasting/auth',
'\\'.BroadcastController::class.'@authenticate'
);
});
}
@ -84,9 +86,7 @@ class BroadcastManager implements FactoryContract
$request = $request ?: $this->app['request'];
if ($request->hasHeader('X-Socket-ID')) {
return $request->header('X-Socket-ID');
}
return $request->header('X-Socket-ID');
}
/**
@ -132,7 +132,7 @@ class BroadcastManager implements FactoryContract
/**
* Get a driver instance.
*
* @param string $driver
* @param string|null $driver
* @return mixed
*/
public function connection($driver = null)
@ -143,7 +143,7 @@ class BroadcastManager implements FactoryContract
/**
* Get a driver instance.
*
* @param string $name
* @param string|null $name
* @return mixed
*/
public function driver($name = null)
@ -161,11 +161,11 @@ class BroadcastManager implements FactoryContract
*/
protected function get($name)
{
return isset($this->drivers[$name]) ? $this->drivers[$name] : $this->resolve($name);
return $this->drivers[$name] ?? $this->resolve($name);
}
/**
* Resolve the given store.
* Resolve the given broadcaster.
*
* @param string $name
* @return \Illuminate\Contracts\Broadcasting\Broadcaster
@ -176,10 +176,6 @@ class BroadcastManager implements FactoryContract
{
$config = $this->getConfig($name);
if (is_null($config)) {
throw new InvalidArgumentException("Broadcaster [{$name}] is not defined.");
}
if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($config);
}
@ -212,10 +208,16 @@ class BroadcastManager implements FactoryContract
*/
protected function createPusherDriver(array $config)
{
return new PusherBroadcaster(
new Pusher($config['key'], $config['secret'],
$config['app_id'], Arr::get($config, 'options', []))
$pusher = new Pusher(
$config['key'], $config['secret'],
$config['app_id'], $config['options'] ?? []
);
if ($config['log'] ?? false) {
$pusher->setLogger($this->app->make(LoggerInterface::class));
}
return new PusherBroadcaster($pusher);
}
/**
@ -227,7 +229,7 @@ class BroadcastManager implements FactoryContract
protected function createRedisDriver(array $config)
{
return new RedisBroadcaster(
$this->app->make('redis'), Arr::get($config, 'connection')
$this->app->make('redis'), $config['connection'] ?? null
);
}
@ -263,7 +265,11 @@ class BroadcastManager implements FactoryContract
*/
protected function getConfig($name)
{
return $this->app['config']["broadcasting.connections.{$name}"];
if (! is_null($name) && $name !== 'null') {
return $this->app['config']["broadcasting.connections.{$name}"];
}
return ['driver' => 'null'];
}
/**

View file

@ -3,18 +3,12 @@
namespace Illuminate\Broadcasting;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Contracts\Broadcasting\Factory as BroadcastingFactory;
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
class BroadcastServiceProvider extends ServiceProvider
class BroadcastServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*

View file

@ -2,13 +2,15 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Exception;
use ReflectionClass;
use ReflectionFunction;
use ReflectionParameter;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Routing\UrlRoutable;
use Illuminate\Contracts\Routing\BindingRegistrar;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
abstract class Broadcaster implements BroadcasterContract
@ -20,10 +22,17 @@ abstract class Broadcaster implements BroadcasterContract
*/
protected $channels = [];
/**
* The registered channel options.
*
* @var array
*/
protected $channelOptions = [];
/**
* The binding registrar instance.
*
* @var BindingRegistrar
* @var \Illuminate\Contracts\Routing\BindingRegistrar
*/
protected $bindingRegistrar;
@ -31,13 +40,16 @@ abstract class Broadcaster implements BroadcasterContract
* Register a channel authenticator.
*
* @param string $channel
* @param callable $callback
* @param callable|string $callback
* @param array $options
* @return $this
*/
public function channel($channel, callable $callback)
public function channel($channel, $callback, $options = [])
{
$this->channels[$channel] = $callback;
$this->channelOptions[$channel] = $options;
return $this;
}
@ -47,22 +59,26 @@ abstract class Broadcaster implements BroadcasterContract
* @param \Illuminate\Http\Request $request
* @param string $channel
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
protected function verifyUserCanAccessChannel($request, $channel)
{
foreach ($this->channels as $pattern => $callback) {
if (! Str::is(preg_replace('/\{(.*?)\}/', '*', $pattern), $channel)) {
if (! $this->channelNameMatchesPattern($channel, $pattern)) {
continue;
}
$parameters = $this->extractAuthParameters($pattern, $channel, $callback);
if ($result = $callback($request->user(), ...$parameters)) {
$handler = $this->normalizeChannelHandlerToCallable($callback);
if ($result = $handler($this->retrieveUser($request, $channel), ...$parameters)) {
return $this->validAuthenticationResponse($request, $result);
}
}
throw new HttpException(403);
throw new AccessDeniedHttpException;
}
/**
@ -70,12 +86,12 @@ abstract class Broadcaster implements BroadcasterContract
*
* @param string $pattern
* @param string $channel
* @param callable $callback
* @param callable|string $callback
* @return array
*/
protected function extractAuthParameters($pattern, $channel, $callback)
{
$callbackParameters = (new ReflectionFunction($callback))->getParameters();
$callbackParameters = $this->extractParameters($callback);
return collect($this->extractChannelKeys($pattern, $channel))->reject(function ($value, $key) {
return is_numeric($key);
@ -84,6 +100,44 @@ abstract class Broadcaster implements BroadcasterContract
})->values()->all();
}
/**
* Extracts the parameters out of what the user passed to handle the channel authentication.
*
* @param callable|string $callback
* @return \ReflectionParameter[]
*
* @throws \Exception
*/
protected function extractParameters($callback)
{
if (is_callable($callback)) {
return (new ReflectionFunction($callback))->getParameters();
} elseif (is_string($callback)) {
return $this->extractParametersFromClass($callback);
}
throw new Exception('Given channel handler is an unknown type.');
}
/**
* Extracts the parameters out of a class channel's "join" method.
*
* @param string $callback
* @return \ReflectionParameter[]
*
* @throws \Exception
*/
protected function extractParametersFromClass($callback)
{
$reflection = new ReflectionClass($callback);
if (! $reflection->hasMethod('join')) {
throw new Exception('Class based channel must define a "join" method.');
}
return $reflection->getMethod('join')->getParameters();
}
/**
* Extract the channel keys from the incoming channel name.
*
@ -140,6 +194,8 @@ abstract class Broadcaster implements BroadcasterContract
* @param mixed $value
* @param array $callbackParameters
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
protected function resolveImplicitBindingIfPossible($key, $value, $callbackParameters)
{
@ -148,11 +204,13 @@ abstract class Broadcaster implements BroadcasterContract
continue;
}
$model = $parameter->getClass()->newInstance();
$instance = $parameter->getClass()->newInstance();
return $model->where($model->getRouteKeyName(), $value)->firstOr(function () {
throw new HttpException(403);
});
if (! $model = $instance->resolveRouteBinding($value)) {
throw new AccessDeniedHttpException;
}
return $model;
}
return $value;
@ -162,13 +220,13 @@ abstract class Broadcaster implements BroadcasterContract
* Determine if a given key and parameter is implicitly bindable.
*
* @param string $key
* @param ReflectionParameter $parameter
* @param \ReflectionParameter $parameter
* @return bool
*/
protected function isImplicitlyBindable($key, $parameter)
{
return $parameter->name === $key && $parameter->getClass() &&
$parameter->getClass()->isSubclassOf(Model::class);
$parameter->getClass()->isSubclassOf(UrlRoutable::class);
}
/**
@ -187,7 +245,7 @@ abstract class Broadcaster implements BroadcasterContract
/**
* Get the model binding registrar instance.
*
* @return BindingRegistrar
* @return \Illuminate\Contracts\Routing\BindingRegistrar
*/
protected function binder()
{
@ -198,4 +256,74 @@ abstract class Broadcaster implements BroadcasterContract
return $this->bindingRegistrar;
}
/**
* Normalize the given callback into a callable.
*
* @param mixed $callback
* @return callable|\Closure
*/
protected function normalizeChannelHandlerToCallable($callback)
{
return is_callable($callback) ? $callback : function (...$args) use ($callback) {
return Container::getInstance()
->make($callback)
->join(...$args);
};
}
/**
* Retrieve the authenticated user using the configured guard (if any).
*
* @param \Illuminate\Http\Request $request
* @param string $channel
* @return mixed
*/
protected function retrieveUser($request, $channel)
{
$options = $this->retrieveChannelOptions($channel);
$guards = $options['guards'] ?? null;
if (is_null($guards)) {
return $request->user();
}
foreach (Arr::wrap($guards) as $guard) {
if ($user = $request->user($guard)) {
return $user;
}
}
}
/**
* Retrieve options for a certain channel.
*
* @param string $channel
* @return array
*/
protected function retrieveChannelOptions($channel)
{
foreach ($this->channelOptions as $pattern => $options) {
if (! $this->channelNameMatchesPattern($channel, $pattern)) {
continue;
}
return $options;
}
return [];
}
/**
* Check if channel name from request match a pattern from registered channels.
*
* @param string $channel
* @param string $pattern
* @return bool
*/
protected function channelNameMatchesPattern($channel, $pattern)
{
return Str::is(preg_replace('/\{(.*?)\}/', '*', $pattern), $channel);
}
}

View file

@ -2,25 +2,27 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Pusher;
use Pusher\Pusher;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Broadcasting\BroadcastException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class PusherBroadcaster extends Broadcaster
{
use UsePusherChannelConventions;
/**
* The Pusher SDK instance.
*
* @var \Pusher
* @var \Pusher\Pusher
*/
protected $pusher;
/**
* Create a new broadcaster instance.
*
* @param \Pusher $pusher
* @param \Pusher\Pusher $pusher
* @return void
*/
public function __construct(Pusher $pusher)
@ -33,17 +35,17 @@ class PusherBroadcaster extends Broadcaster
*
* @param \Illuminate\Http\Request $request
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function auth($request)
{
if (Str::startsWith($request->channel_name, ['private-', 'presence-']) &&
! $request->user()) {
throw new HttpException(403);
}
$channelName = $this->normalizeChannelName($request->channel_name);
$channelName = Str::startsWith($request->channel_name, 'private-')
? Str::replaceFirst('private-', '', $request->channel_name)
: Str::replaceFirst('presence-', '', $request->channel_name);
if ($this->isGuardedChannel($request->channel_name) &&
! $this->retrieveUser($request, $channelName)) {
throw new AccessDeniedHttpException;
}
return parent::verifyUserCanAccessChannel(
$request, $channelName
@ -61,25 +63,36 @@ class PusherBroadcaster extends Broadcaster
{
if (Str::startsWith($request->channel_name, 'private')) {
return $this->decodePusherResponse(
$this->pusher->socket_auth($request->channel_name, $request->socket_id)
$request, $this->pusher->socket_auth($request->channel_name, $request->socket_id)
);
}
$channelName = $this->normalizeChannelName($request->channel_name);
return $this->decodePusherResponse(
$request,
$this->pusher->presence_auth(
$request->channel_name, $request->socket_id, $request->user()->getAuthIdentifier(), $result)
$request->channel_name, $request->socket_id,
$this->retrieveUser($request, $channelName)->getAuthIdentifier(), $result
)
);
}
/**
* Decode the given Pusher response.
*
* @param \Illuminate\Http\Request $request
* @param mixed $response
* @return array
*/
protected function decodePusherResponse($response)
protected function decodePusherResponse($request, $response)
{
return json_decode($response, true);
if (! $request->input('callback', false)) {
return json_decode($response, true);
}
return response()->json(json_decode($response, true))
->withCallback($request->callback);
}
/**
@ -111,7 +124,7 @@ class PusherBroadcaster extends Broadcaster
/**
* Get the Pusher SDK instance.
*
* @return \Pusher
* @return \Pusher\Pusher
*/
public function getPusher()
{

View file

@ -3,12 +3,13 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Contracts\Redis\Factory as Redis;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class RedisBroadcaster extends Broadcaster
{
use UsePusherChannelConventions;
/**
* The Redis instance.
*
@ -27,7 +28,7 @@ class RedisBroadcaster extends Broadcaster
* Create a new broadcaster instance.
*
* @param \Illuminate\Contracts\Redis\Factory $redis
* @param string $connection
* @param string|null $connection
* @return void
*/
public function __construct(Redis $redis, $connection = null)
@ -41,17 +42,17 @@ class RedisBroadcaster extends Broadcaster
*
* @param \Illuminate\Http\Request $request
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function auth($request)
{
if (Str::startsWith($request->channel_name, ['private-', 'presence-']) &&
! $request->user()) {
throw new HttpException(403);
}
$channelName = $this->normalizeChannelName($request->channel_name);
$channelName = Str::startsWith($request->channel_name, 'private-')
? Str::replaceFirst('private-', '', $request->channel_name)
: Str::replaceFirst('presence-', '', $request->channel_name);
if ($this->isGuardedChannel($request->channel_name) &&
! $this->retrieveUser($request, $channelName)) {
throw new AccessDeniedHttpException;
}
return parent::verifyUserCanAccessChannel(
$request, $channelName
@ -71,8 +72,10 @@ class RedisBroadcaster extends Broadcaster
return json_encode($result);
}
$channelName = $this->normalizeChannelName($request->channel_name);
return json_encode(['channel_data' => [
'user_id' => $request->user()->getAuthIdentifier(),
'user_id' => $this->retrieveUser($request, $channelName)->getAuthIdentifier(),
'user_info' => $result,
]]);
}

View file

@ -14,11 +14,13 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/bus": "5.4.*",
"illuminate/contracts": "5.4.*",
"illuminate/queue": "5.4.*",
"illuminate/support": "5.4.*"
"php": "^7.1.3",
"ext-json": "*",
"psr/log": "^1.0",
"illuminate/bus": "5.8.*",
"illuminate/contracts": "5.8.*",
"illuminate/queue": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@ -27,11 +29,11 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "5.8-dev"
}
},
"suggest": {
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0)."
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0)."
},
"config": {
"sort-packages": true

View file

@ -3,19 +3,13 @@
namespace Illuminate\Bus;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Contracts\Bus\Dispatcher as DispatcherContract;
use Illuminate\Contracts\Queue\Factory as QueueFactoryContract;
use Illuminate\Contracts\Bus\QueueingDispatcher as QueueingDispatcherContract;
class BusServiceProvider extends ServiceProvider
class BusServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*

View file

@ -71,9 +71,9 @@ class Dispatcher implements QueueingDispatcher
{
if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
return $this->dispatchToQueue($command);
} else {
return $this->dispatchNow($command);
}
return $this->dispatchNow($command);
}
/**
@ -145,7 +145,7 @@ class Dispatcher implements QueueingDispatcher
*/
public function dispatchToQueue($command)
{
$connection = isset($command->connection) ? $command->connection : null;
$connection = $command->connection ?? null;
$queue = call_user_func($this->queueResolver, $connection);
@ -155,9 +155,9 @@ class Dispatcher implements QueueingDispatcher
if (method_exists($command, 'queue')) {
return $command->queue($queue, $command);
} else {
return $this->pushCommandToQueue($queue, $command);
}
return $this->pushCommandToQueue($queue, $command);
}
/**

View file

@ -18,13 +18,34 @@ trait Queueable
*/
public $queue;
/**
* The name of the connection the chain should be sent to.
*
* @var string|null
*/
public $chainConnection;
/**
* The name of the queue the chain should be sent to.
*
* @var string|null
*/
public $chainQueue;
/**
* The number of seconds before the job should be made available.
*
* @var \DateTime|int|null
* @var \DateTimeInterface|\DateInterval|int|null
*/
public $delay;
/**
* The jobs that should run if this job is successful.
*
* @var array
*/
public $chained = [];
/**
* Set the desired connection for the job.
*
@ -51,10 +72,38 @@ trait Queueable
return $this;
}
/**
* Set the desired connection for the chain.
*
* @param string|null $connection
* @return $this
*/
public function allOnConnection($connection)
{
$this->chainConnection = $connection;
$this->connection = $connection;
return $this;
}
/**
* Set the desired queue for the chain.
*
* @param string|null $queue
* @return $this
*/
public function allOnQueue($queue)
{
$this->chainQueue = $queue;
$this->queue = $queue;
return $this;
}
/**
* Set the desired delay for the job.
*
* @param \DateTime|int|null $delay
* @param \DateTimeInterface|\DateInterval|int|null $delay
* @return $this
*/
public function delay($delay)
@ -63,4 +112,39 @@ trait Queueable
return $this;
}
/**
* Set the jobs that should run if this job is successful.
*
* @param array $chain
* @return $this
*/
public function chain($chain)
{
$this->chained = collect($chain)->map(function ($job) {
return serialize($job);
})->all();
return $this;
}
/**
* Dispatch the next job on the chain.
*
* @return void
*/
public function dispatchNextJobInChain()
{
if (! empty($this->chained)) {
dispatch(tap(unserialize(array_shift($this->chained)), function ($next) {
$next->chained = $this->chained;
$next->onConnection($next->connection ?: $this->chainConnection);
$next->onQueue($next->queue ?: $this->chainQueue);
$next->chainConnection = $this->chainConnection;
$next->chainQueue = $this->chainQueue;
}));
}
}
}

View file

@ -14,10 +14,10 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/contracts": "5.4.*",
"illuminate/pipeline": "5.4.*",
"illuminate/support": "5.4.*"
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/pipeline": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@ -26,7 +26,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "5.8-dev"
}
},
"config": {

View file

@ -2,9 +2,7 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Store;
class ApcStore extends TaggableStore implements Store
class ApcStore extends TaggableStore
{
use RetrievesMultipleKeys;
@ -51,16 +49,16 @@ class ApcStore extends TaggableStore implements Store
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$this->apc->put($this->prefix.$key, $value, (int) ($minutes * 60));
return $this->apc->put($this->prefix.$key, $value, $seconds);
}
/**
@ -92,11 +90,11 @@ class ApcStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->put($key, $value, 0);
return $this->put($key, $value, 0);
}
/**

View file

@ -2,11 +2,11 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Support\InteractsWithTime;
class ArrayStore extends TaggableStore implements Store
class ArrayStore extends TaggableStore
{
use RetrievesMultipleKeys;
use InteractsWithTime, RetrievesMultipleKeys;
/**
* The array of stored values.
@ -23,22 +23,39 @@ class ArrayStore extends TaggableStore implements Store
*/
public function get($key)
{
if (array_key_exists($key, $this->storage)) {
return $this->storage[$key];
if (! isset($this->storage[$key])) {
return;
}
$item = $this->storage[$key];
$expiresAt = $item['expiresAt'] ?? 0;
if ($expiresAt !== 0 && $this->currentTime() > $expiresAt) {
$this->forget($key);
return;
}
return $item['value'];
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$this->storage[$key] = $value;
$this->storage[$key] = [
'value' => $value,
'expiresAt' => $this->calculateExpiration($seconds),
];
return true;
}
/**
@ -50,10 +67,15 @@ class ArrayStore extends TaggableStore implements Store
*/
public function increment($key, $value = 1)
{
$this->storage[$key] = ! isset($this->storage[$key])
? $value : ((int) $this->storage[$key]) + $value;
if (! isset($this->storage[$key])) {
$this->forever($key, $value);
return $this->storage[$key];
return $this->storage[$key]['value'];
}
$this->storage[$key]['value'] = ((int) $this->storage[$key]['value']) + $value;
return $this->storage[$key]['value'];
}
/**
@ -73,11 +95,11 @@ class ArrayStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->put($key, $value, 0);
return $this->put($key, $value, 0);
}
/**
@ -88,9 +110,13 @@ class ArrayStore extends TaggableStore implements Store
*/
public function forget($key)
{
unset($this->storage[$key]);
if (array_key_exists($key, $this->storage)) {
unset($this->storage[$key]);
return true;
return true;
}
return false;
}
/**
@ -114,4 +140,26 @@ class ArrayStore extends TaggableStore implements Store
{
return '';
}
/**
* Get the expiration time of the key.
*
* @param int $seconds
* @return int
*/
protected function calculateExpiration($seconds)
{
return $this->toTimestamp($seconds);
}
/**
* Get the UNIX timestamp for the given number of seconds.
*
* @param int $seconds
* @return int
*/
protected function toTimestamp($seconds)
{
return $seconds > 0 ? $this->availableAt($seconds) : 0;
}
}

View file

@ -5,6 +5,7 @@ namespace Illuminate\Cache;
use Closure;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use Aws\DynamoDb\DynamoDbClient;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Cache\Factory as FactoryContract;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
@ -17,7 +18,7 @@ class CacheManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
@ -38,7 +39,7 @@ class CacheManager implements FactoryContract
/**
* Create a new Cache manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
@ -47,10 +48,10 @@ class CacheManager implements FactoryContract
}
/**
* Get a cache store instance by name.
* Get a cache store instance by name, wrapped in a repository.
*
* @param string|null $name
* @return mixed
* @return \Illuminate\Contracts\Cache\Repository
*/
public function store($name = null)
{
@ -62,8 +63,8 @@ class CacheManager implements FactoryContract
/**
* Get a cache driver instance.
*
* @param string $driver
* @return mixed
* @param string|null $driver
* @return \Illuminate\Contracts\Cache\Repository
*/
public function driver($driver = null)
{
@ -78,7 +79,7 @@ class CacheManager implements FactoryContract
*/
protected function get($name)
{
return isset($this->stores[$name]) ? $this->stores[$name] : $this->resolve($name);
return $this->stores[$name] ?? $this->resolve($name);
}
/**
@ -125,7 +126,7 @@ class CacheManager implements FactoryContract
* Create an instance of the APC cache driver.
*
* @param array $config
* @return \Illuminate\Cache\ApcStore
* @return \Illuminate\Cache\Repository
*/
protected function createApcDriver(array $config)
{
@ -137,7 +138,7 @@ class CacheManager implements FactoryContract
/**
* Create an instance of the array cache driver.
*
* @return \Illuminate\Cache\ArrayStore
* @return \Illuminate\Cache\Repository
*/
protected function createArrayDriver()
{
@ -148,7 +149,7 @@ class CacheManager implements FactoryContract
* Create an instance of the file cache driver.
*
* @param array $config
* @return \Illuminate\Cache\FileStore
* @return \Illuminate\Cache\Repository
*/
protected function createFileDriver(array $config)
{
@ -159,7 +160,7 @@ class CacheManager implements FactoryContract
* Create an instance of the Memcached cache driver.
*
* @param array $config
* @return \Illuminate\Cache\MemcachedStore
* @return \Illuminate\Cache\Repository
*/
protected function createMemcachedDriver(array $config)
{
@ -167,9 +168,9 @@ class CacheManager implements FactoryContract
$memcached = $this->app['memcached.connector']->connect(
$config['servers'],
array_get($config, 'persistent_id'),
array_get($config, 'options', []),
array_filter(array_get($config, 'sasl', []))
$config['persistent_id'] ?? null,
$config['options'] ?? [],
array_filter($config['sasl'] ?? [])
);
return $this->repository(new MemcachedStore($memcached, $prefix));
@ -178,7 +179,7 @@ class CacheManager implements FactoryContract
/**
* Create an instance of the Null cache driver.
*
* @return \Illuminate\Cache\NullStore
* @return \Illuminate\Cache\Repository
*/
protected function createNullDriver()
{
@ -189,13 +190,13 @@ class CacheManager implements FactoryContract
* Create an instance of the Redis cache driver.
*
* @param array $config
* @return \Illuminate\Cache\RedisStore
* @return \Illuminate\Cache\Repository
*/
protected function createRedisDriver(array $config)
{
$redis = $this->app['redis'];
$connection = Arr::get($config, 'connection', 'default');
$connection = $config['connection'] ?? 'default';
return $this->repository(new RedisStore($redis, $this->getPrefix($config), $connection));
}
@ -204,15 +205,47 @@ class CacheManager implements FactoryContract
* Create an instance of the database cache driver.
*
* @param array $config
* @return \Illuminate\Cache\DatabaseStore
* @return \Illuminate\Cache\Repository
*/
protected function createDatabaseDriver(array $config)
{
$connection = $this->app['db']->connection(Arr::get($config, 'connection'));
$connection = $this->app['db']->connection($config['connection'] ?? null);
return $this->repository(
new DatabaseStore(
$connection, $this->app['encrypter'], $config['table'], $this->getPrefix($config)
$connection, $config['table'], $this->getPrefix($config)
)
);
}
/**
* Create an instance of the DynamoDB cache driver.
*
* @param array $config
* @return \Illuminate\Cache\Repository
*/
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']
);
}
return $this->repository(
new DynamoDbStore(
new DynamoDbClient($dynamoConfig),
$config['table'],
$config['attributes']['key'] ?? 'key',
$config['attributes']['value'] ?? 'value',
$config['attributes']['expiration'] ?? 'expires_at',
$this->getPrefix($config)
)
);
}
@ -244,7 +277,7 @@ class CacheManager implements FactoryContract
*/
protected function getPrefix(array $config)
{
return Arr::get($config, 'prefix') ?: $this->app['config']['cache.prefix'];
return $config['prefix'] ?? $this->app['config']['cache.prefix'];
}
/**
@ -279,10 +312,29 @@ class CacheManager implements FactoryContract
$this->app['config']['cache.default'] = $name;
}
/**
* Unset the given driver instances.
*
* @param array|string|null $name
* @return $this
*/
public function forgetDriver($name = null)
{
$name = $name ?? $this->getDefaultDriver();
foreach ((array) $name as $cacheName) {
if (isset($this->stores[$cacheName])) {
unset($this->stores[$cacheName]);
}
}
return $this;
}
/**
* Register a custom driver creator Closure.
*
* @param string $driver
* @param string $driver
* @param \Closure $callback
* @return $this
*/
@ -297,7 +349,7 @@ class CacheManager implements FactoryContract
* Dynamically call the default driver instance.
*
* @param string $method
* @param array $parameters
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)

View file

@ -3,16 +3,10 @@
namespace Illuminate\Cache;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
class CacheServiceProvider extends ServiceProvider
class CacheServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*

View file

@ -54,7 +54,7 @@ class CacheTableCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
$fullPath = $this->createBaseMigration();

View file

@ -4,6 +4,7 @@ namespace Illuminate\Cache\Console;
use Illuminate\Console\Command;
use Illuminate\Cache\CacheManager;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
@ -30,17 +31,26 @@ class ClearCommand extends Command
*/
protected $cache;
/**
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;
/**
* Create a new cache clear command instance.
*
* @param \Illuminate\Cache\CacheManager $cache
* @param \Illuminate\Filesystem\Filesystem $files
* @return void
*/
public function __construct(CacheManager $cache)
public function __construct(CacheManager $cache, Filesystem $files)
{
parent::__construct();
$this->cache = $cache;
$this->files = $files;
}
/**
@ -50,13 +60,41 @@ class ClearCommand extends Command
*/
public function handle()
{
$this->laravel['events']->fire('cache:clearing', [$this->argument('store'), $this->tags()]);
$this->laravel['events']->dispatch(
'cache:clearing', [$this->argument('store'), $this->tags()]
);
$this->cache()->flush();
$successful = $this->cache()->flush();
$this->laravel['events']->fire('cache:cleared', [$this->argument('store'), $this->tags()]);
$this->flushFacades();
$this->info('Cache cleared successfully.');
if (! $successful) {
return $this->error('Failed to clear cache. Make sure you have the appropriate permissions.');
}
$this->laravel['events']->dispatch(
'cache:cleared', [$this->argument('store'), $this->tags()]
);
$this->info('Application cache cleared!');
}
/**
* Flush the real-time facades stored in the cache directory.
*
* @return void
*/
public function flushFacades()
{
if (! $this->files->exists($storagePath = storage_path('framework/cache'))) {
return;
}
foreach ($this->files->files($storagePath) as $file) {
if (preg_match('/facade-.*\.php$/', $file)) {
$this->files->delete($file);
}
}
}
/**
@ -89,7 +127,7 @@ class ClearCommand extends Command
protected function getArguments()
{
return [
['store', InputArgument::OPTIONAL, 'The name of the store you would like to clear.'],
['store', InputArgument::OPTIONAL, 'The name of the store you would like to clear'],
];
}
@ -101,7 +139,7 @@ class ClearCommand extends Command
protected function getOptions()
{
return [
['tags', null, InputOption::VALUE_OPTIONAL, 'The cache tags you would like to clear.', null],
['tags', null, InputOption::VALUE_OPTIONAL, 'The cache tags you would like to clear', null],
];
}
}

View file

@ -15,7 +15,7 @@ class CreateCacheTable extends Migration
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->unique();
$table->text('value');
$table->mediumText('value');
$table->integer('expiration');
});
}

View file

@ -4,14 +4,15 @@ namespace Illuminate\Cache;
use Closure;
use Exception;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Database\PostgresConnection;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract;
class DatabaseStore implements Store
{
use RetrievesMultipleKeys;
use InteractsWithTime, RetrievesMultipleKeys;
/**
* The database connection instance.
@ -20,13 +21,6 @@ class DatabaseStore implements Store
*/
protected $connection;
/**
* The encrypter instance.
*
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
protected $encrypter;
/**
* The name of the cache table.
*
@ -45,17 +39,14 @@ class DatabaseStore implements Store
* Create a new database store.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* @param string $table
* @param string $prefix
* @return void
*/
public function __construct(ConnectionInterface $connection, EncrypterContract $encrypter,
$table, $prefix = '')
public function __construct(ConnectionInterface $connection, $table, $prefix = '')
{
$this->table = $table;
$this->prefix = $prefix;
$this->encrypter = $encrypter;
$this->connection = $connection;
}
@ -83,38 +74,37 @@ class DatabaseStore implements Store
// If this cache expiration date is past the current time, we will remove this
// item from the cache. Then we will return a null value since the cache is
// expired. We will use "Carbon" to make this comparison with the column.
if (Carbon::now()->getTimestamp() >= $cache->expiration) {
if ($this->currentTime() >= $cache->expiration) {
$this->forget($key);
return;
}
return $this->encrypter->decrypt($cache->value);
return $this->unserialize($cache->value);
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$key = $this->prefix.$key;
// All of the cached values in the database are encrypted in case this is used
// as a session data store by the consumer. We'll also calculate the expire
// time and place that on the table so we will check it on our retrieval.
$value = $this->encrypter->encrypt($value);
$value = $this->serialize($value);
$expiration = $this->getTime() + (int) ($minutes * 60);
$expiration = $this->getTime() + $seconds;
try {
$this->table()->insert(compact('key', 'value', 'expiration'));
return $this->table()->insert(compact('key', 'value', 'expiration'));
} catch (Exception $e) {
$this->table()->where('key', $key)->update(compact('value', 'expiration'));
$result = $this->table()->where('key', $key)->update(compact('value', 'expiration'));
return $result > 0;
}
}
@ -171,7 +161,7 @@ class DatabaseStore implements Store
$cache = is_array($cache) ? (object) $cache : $cache;
$current = $this->encrypter->decrypt($cache->value);
$current = $this->unserialize($cache->value);
// Here we'll call this callback function that was given to the function which
// is used to either increment or decrement the function. We use a callback
@ -186,7 +176,7 @@ class DatabaseStore implements Store
// since database cache values are encrypted by default with secure storage
// that can't be easily read. We will return the new value after storing.
$this->table()->where('key', $prefixed)->update([
'value' => $this->encrypter->encrypt($new),
'value' => $this->serialize($new),
]);
return $new;
@ -200,7 +190,7 @@ class DatabaseStore implements Store
*/
protected function getTime()
{
return Carbon::now()->getTimestamp();
return $this->currentTime();
}
/**
@ -208,11 +198,11 @@ class DatabaseStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->put($key, $value, 5256000);
return $this->put($key, $value, 315360000);
}
/**
@ -235,7 +225,9 @@ class DatabaseStore implements Store
*/
public function flush()
{
return (bool) $this->table()->delete();
$this->table()->delete();
return true;
}
/**
@ -258,16 +250,6 @@ class DatabaseStore implements Store
return $this->connection;
}
/**
* Get the encrypter instance.
*
* @return \Illuminate\Contracts\Encryption\Encrypter
*/
public function getEncrypter()
{
return $this->encrypter;
}
/**
* Get the cache key prefix.
*
@ -277,4 +259,36 @@ class DatabaseStore implements Store
{
return $this->prefix;
}
/**
* Serialize the given value.
*
* @param mixed $value
* @return string
*/
protected function serialize($value)
{
$result = serialize($value);
if ($this->connection instanceof PostgresConnection && Str::contains($result, "\0")) {
$result = base64_encode($result);
}
return $result;
}
/**
* Unserialize the given value.
*
* @param string $value
* @return mixed
*/
protected function unserialize($value)
{
if ($this->connection instanceof PostgresConnection && ! Str::contains($value, [':', ';'])) {
$value = base64_decode($value);
}
return unserialize($value);
}
}

View file

@ -12,26 +12,26 @@ class KeyWritten extends CacheEvent
public $value;
/**
* The number of minutes the key should be valid.
* The number of seconds the key should be valid.
*
* @var int
* @var int|null
*/
public $minutes;
public $seconds;
/**
* Create a new event instance.
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param int|null $seconds
* @param array $tags
* @return void
*/
public function __construct($key, $value, $minutes, $tags = [])
public function __construct($key, $value, $seconds = null, $tags = [])
{
parent::__construct($key, $tags);
$this->value = $value;
$this->minutes = $minutes;
$this->seconds = $seconds;
}
}

View file

@ -3,14 +3,13 @@
namespace Illuminate\Cache;
use Exception;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\InteractsWithTime;
class FileStore implements Store
{
use RetrievesMultipleKeys;
use InteractsWithTime, RetrievesMultipleKeys;
/**
* The Illuminate Filesystem instance.
@ -47,24 +46,26 @@ class FileStore implements Store
*/
public function get($key)
{
return Arr::get($this->getPayload($key), 'data');
return $this->getPayload($key)['data'] ?? null;
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$this->ensureCacheDirectoryExists($path = $this->path($key));
$this->files->put(
$path, $this->expiration($minutes).serialize($value), true
$result = $this->files->put(
$path, $this->expiration($seconds).serialize($value), true
);
return $result !== false && $result > 0;
}
/**
@ -92,7 +93,7 @@ class FileStore implements Store
$raw = $this->getPayload($key);
return tap(((int) $raw['data']) + $value, function ($newValue) use ($key, $raw) {
$this->put($key, $newValue, $raw['time']);
$this->put($key, $newValue, $raw['time'] ?? 0);
});
}
@ -113,11 +114,11 @@ class FileStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->put($key, $value, 0);
return $this->put($key, $value, 0);
}
/**
@ -165,7 +166,7 @@ class FileStore implements Store
{
$path = $this->path($key);
// If the file doesn't exists, we obviously can't return the cache so we will
// If the file doesn't exist, we obviously cannot return the cache so we will
// just return null. Otherwise, we'll get the contents of the file and get
// the expiration UNIX timestamps from the start of the file's contents.
try {
@ -179,18 +180,24 @@ class FileStore implements Store
// If the current time is greater than expiration timestamps we will delete
// the file and return null. This helps clean up the old files and keeps
// this directory much cleaner for us as old files aren't hanging out.
if (Carbon::now()->getTimestamp() >= $expire) {
if ($this->currentTime() >= $expire) {
$this->forget($key);
return $this->emptyPayload();
}
$data = unserialize(substr($contents, 10));
try {
$data = unserialize(substr($contents, 10));
} catch (Exception $e) {
$this->forget($key);
// Next, we'll extract the number of minutes that are remaining for a cache
return $this->emptyPayload();
}
// Next, we'll extract the number of seconds that are remaining for a cache
// so that we can properly retain the time for things like the increment
// operation that may be performed on this cache on a later operation.
$time = ($expire - Carbon::now()->getTimestamp()) / 60;
$time = $expire - $this->currentTime();
return compact('data', 'time');
}
@ -219,16 +226,16 @@ class FileStore implements Store
}
/**
* Get the expiration time based on the given minutes.
* Get the expiration time based on the given seconds.
*
* @param float|int $minutes
* @param int $seconds
* @return int
*/
protected function expiration($minutes)
protected function expiration($seconds)
{
$time = Carbon::now()->getTimestamp() + (int) ($minutes * 60);
$time = $this->availableAt($seconds);
return $minutes === 0 || $time > 9999999999 ? 9999999999 : (int) $time;
return $seconds === 0 || $time > 9999999999 ? 9999999999 : $time;
}
/**

View file

@ -47,7 +47,7 @@ class MemcachedConnector
{
$memcached = $this->createMemcachedInstance($connectionId);
if (count($credentials) == 2) {
if (count($credentials) === 2) {
$this->setCredentials($memcached, $credentials);
}
@ -78,7 +78,7 @@ class MemcachedConnector
*/
protected function setCredentials($memcached, $credentials)
{
list($username, $password) = $credentials;
[$username, $password] = $credentials;
$memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true);

View file

@ -3,12 +3,14 @@
namespace Illuminate\Cache;
use Memcached;
use Carbon\Carbon;
use ReflectionMethod;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\LockProvider;
class MemcachedStore extends TaggableStore implements Store
class MemcachedStore extends TaggableStore implements LockProvider
{
use InteractsWithTime;
/**
* The Memcached instance.
*
@ -91,26 +93,28 @@ class MemcachedStore extends TaggableStore implements Store
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$this->memcached->set($this->prefix.$key, $value, $this->toTimestamp($minutes));
return $this->memcached->set(
$this->prefix.$key, $value, $this->calculateExpiration($seconds)
);
}
/**
* Store multiple items in the cache for a given number of minutes.
* Store multiple items in the cache for a given number of seconds.
*
* @param array $values
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function putMany(array $values, $minutes)
public function putMany(array $values, $seconds)
{
$prefixedValues = [];
@ -118,7 +122,9 @@ class MemcachedStore extends TaggableStore implements Store
$prefixedValues[$this->prefix.$key] = $value;
}
$this->memcached->setMulti($prefixedValues, $this->toTimestamp($minutes));
return $this->memcached->setMulti(
$prefixedValues, $this->calculateExpiration($seconds)
);
}
/**
@ -126,12 +132,14 @@ class MemcachedStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @param int $seconds
* @return bool
*/
public function add($key, $value, $minutes)
public function add($key, $value, $seconds)
{
return $this->memcached->add($this->prefix.$key, $value, $this->toTimestamp($minutes));
return $this->memcached->add(
$this->prefix.$key, $value, $this->calculateExpiration($seconds)
);
}
/**
@ -163,11 +171,36 @@ class MemcachedStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->put($key, $value, 0);
return $this->put($key, $value, 0);
}
/**
* 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 MemcachedLock($this->memcached, $this->prefix.$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);
}
/**
@ -192,14 +225,25 @@ class MemcachedStore extends TaggableStore implements Store
}
/**
* Get the UNIX timestamp for the given number of minutes.
* Get the expiration time of the key.
*
* @param int $minutes
* @param int $seconds
* @return int
*/
protected function toTimestamp($minutes)
protected function calculateExpiration($seconds)
{
return $minutes > 0 ? Carbon::now()->addSeconds($minutes * 60)->getTimestamp() : 0;
return $this->toTimestamp($seconds);
}
/**
* Get the UNIX timestamp for the given number of seconds.
*
* @param int $seconds
* @return int
*/
protected function toTimestamp($seconds)
{
return $seconds > 0 ? $this->availableAt($seconds) : 0;
}
/**

View file

@ -2,9 +2,7 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Store;
class NullStore extends TaggableStore implements Store
class NullStore extends TaggableStore
{
use RetrievesMultipleKeys;
@ -23,20 +21,19 @@ class NullStore extends TaggableStore implements Store
*/
public function get($key)
{
//
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
//
return false;
}
/**
@ -44,11 +41,11 @@ class NullStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @return int
* @return int|bool
*/
public function increment($key, $value = 1)
{
//
return false;
}
/**
@ -56,11 +53,11 @@ class NullStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @return int
* @return int|bool
*/
public function decrement($key, $value = 1)
{
//
return false;
}
/**
@ -68,22 +65,22 @@ class NullStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
//
return false;
}
/**
* Remove an item from the cache.
*
* @param string $key
* @return void
* @return bool
*/
public function forget($key)
{
//
return true;
}
/**

View file

@ -2,11 +2,13 @@
namespace Illuminate\Cache;
use Carbon\Carbon;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\Repository as Cache;
class RateLimiter
{
use InteractsWithTime;
/**
* The cache store implementation.
*
@ -30,55 +32,40 @@ class RateLimiter
*
* @param string $key
* @param int $maxAttempts
* @param float|int $decayMinutes
* @return bool
*/
public function tooManyAttempts($key, $maxAttempts, $decayMinutes = 1)
public function tooManyAttempts($key, $maxAttempts)
{
if ($this->cache->has($key.':lockout')) {
return true;
}
if ($this->attempts($key) >= $maxAttempts) {
$this->lockout($key, $decayMinutes);
if ($this->cache->has($key.':timer')) {
return true;
}
$this->resetAttempts($key);
return true;
}
return false;
}
/**
* Add the lockout key to the cache.
*
* @param string $key
* @param int $decayMinutes
* @return void
*/
protected function lockout($key, $decayMinutes)
{
$this->cache->add(
$key.':lockout', Carbon::now()->getTimestamp() + ($decayMinutes * 60), $decayMinutes
);
}
/**
* Increment the counter for a given key for a given decay time.
*
* @param string $key
* @param float|int $decayMinutes
* @param int $decaySeconds
* @return int
*/
public function hit($key, $decayMinutes = 1)
public function hit($key, $decaySeconds = 60)
{
$added = $this->cache->add($key, 0, $decayMinutes);
$this->cache->add(
$key.':timer', $this->availableAt($decaySeconds), $decaySeconds
);
$added = $this->cache->add($key, 0, $decaySeconds);
$hits = (int) $this->cache->increment($key);
if (! $added && $hits == 1) {
$this->cache->put($key, 1, $decayMinutes);
$this->cache->put($key, 1, $decaySeconds);
}
return $hits;
@ -121,7 +108,7 @@ class RateLimiter
}
/**
* Clear the hits and lockout for the given key.
* Clear the hits and lockout timer for the given key.
*
* @param string $key
* @return void
@ -130,7 +117,7 @@ class RateLimiter
{
$this->resetAttempts($key);
$this->cache->forget($key.':lockout');
$this->cache->forget($key.':timer');
}
/**
@ -141,6 +128,6 @@ class RateLimiter
*/
public function availableIn($key)
{
return $this->cache->get($key.':lockout') - Carbon::now()->getTimestamp();
return $this->cache->get($key.':timer') - $this->currentTime();
}
}

View file

@ -2,10 +2,10 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Redis\Factory as Redis;
class RedisStore extends TaggableStore implements Store
class RedisStore extends TaggableStore implements LockProvider
{
/**
* The Redis factory implementation.
@ -80,36 +80,42 @@ class RedisStore extends TaggableStore implements Store
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$this->connection()->setex(
$this->prefix.$key, (int) max(1, $minutes * 60), $this->serialize($value)
return (bool) $this->connection()->setex(
$this->prefix.$key, (int) max(1, $seconds), $this->serialize($value)
);
}
/**
* Store multiple items in the cache for a given number of minutes.
* Store multiple items in the cache for a given number of seconds.
*
* @param array $values
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function putMany(array $values, $minutes)
public function putMany(array $values, $seconds)
{
$this->connection()->multi();
$manyResult = null;
foreach ($values as $key => $value) {
$this->put($key, $value, $minutes);
$result = $this->put($key, $value, $seconds);
$manyResult = is_null($manyResult) ? $result : $result && $manyResult;
}
$this->connection()->exec();
return $manyResult ?: false;
}
/**
@ -117,15 +123,15 @@ class RedisStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @param int $seconds
* @return bool
*/
public function add($key, $value, $minutes)
public function add($key, $value, $seconds)
{
$lua = "return redis.call('exists',KEYS[1])<1 and redis.call('setex',KEYS[1],ARGV[2],ARGV[1])";
return (bool) $this->connection()->eval(
$lua, 1, $this->prefix.$key, $this->serialize($value), (int) max(1, $minutes * 60)
$lua, 1, $this->prefix.$key, $this->serialize($value), (int) max(1, $seconds)
);
}
@ -158,11 +164,36 @@ class RedisStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->connection()->set($this->prefix.$key, $this->serialize($value));
return (bool) $this->connection()->set($this->prefix.$key, $this->serialize($value));
}
/**
* 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 RedisLock($this->connection(), $this->prefix.$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);
}
/**

View file

@ -22,41 +22,73 @@ class RedisTaggedCache extends TaggedCache
*
* @param string $key
* @param mixed $value
* @param \DateTime|float|int $minutes
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function put($key, $value, $ttl = null)
{
if ($ttl === null) {
return $this->forever($key, $value);
}
$this->pushStandardKeys($this->tags->getNamespace(), $key);
return parent::put($key, $value, $ttl);
}
/**
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function put($key, $value, $minutes = null)
public function increment($key, $value = 1)
{
$this->pushStandardKeys($this->tags->getNamespace(), $key);
parent::put($key, $value, $minutes);
parent::increment($key, $value);
}
/**
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function decrement($key, $value = 1)
{
$this->pushStandardKeys($this->tags->getNamespace(), $key);
parent::decrement($key, $value);
}
/**
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @return void
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
{
$this->pushForeverKeys($this->tags->getNamespace(), $key);
parent::forever($key, $value);
return parent::forever($key, $value);
}
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
$this->deleteForeverKeys();
$this->deleteStandardKeys();
parent::flush();
return parent::flush();
}
/**

View file

@ -3,16 +3,17 @@
namespace Illuminate\Cache;
use Closure;
use DateTime;
use ArrayAccess;
use Carbon\Carbon;
use DateTimeInterface;
use BadMethodCallException;
use Illuminate\Support\Carbon;
use Illuminate\Cache\Events\CacheHit;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Cache\Events\KeyWritten;
use Illuminate\Cache\Events\CacheMissed;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Cache\Events\KeyForgotten;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Cache\Repository as CacheContract;
@ -21,6 +22,7 @@ use Illuminate\Contracts\Cache\Repository as CacheContract;
*/
class Repository implements CacheContract, ArrayAccess
{
use InteractsWithTime;
use Macroable {
__call as macroCall;
}
@ -40,11 +42,11 @@ class Repository implements CacheContract, ArrayAccess
protected $events;
/**
* The default number of minutes to store items.
* The default number of seconds to store items.
*
* @var float|int
* @var int|null
*/
protected $default = 60;
protected $default = 3600;
/**
* Create a new cache repository instance.
@ -68,11 +70,22 @@ class Repository implements CacheContract, ArrayAccess
return ! is_null($this->get($key));
}
/**
* Determine if an item doesn't exist in the cache.
*
* @param string $key
* @return bool
*/
public function missing($key)
{
return ! $this->has($key);
}
/**
* Retrieve an item from the cache by key.
*
* @param string $key
* @param mixed $default
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null)
@ -116,6 +129,20 @@ class Repository implements CacheContract, ArrayAccess
})->all();
}
/**
* {@inheritdoc}
*/
public function getMultiple($keys, $default = null)
{
$defaults = [];
foreach ($keys as $key) {
$defaults[$key] = $default;
}
return $this->many($defaults);
}
/**
* Handle a result for the "many" method.
*
@ -147,12 +174,12 @@ class Repository implements CacheContract, ArrayAccess
* Retrieve an item from the cache and delete it.
*
* @param string $key
* @param mixed $default
* @param mixed $default
* @return mixed
*/
public function pull($key, $default = null)
{
return tap($this->get($key, $default), function ($value) use ($key) {
return tap($this->get($key, $default), function () use ($key) {
$this->forget($key);
});
}
@ -161,71 +188,132 @@ class Repository implements CacheContract, ArrayAccess
* Store an item in the cache.
*
* @param string $key
* @param mixed $value
* @param \DateTime|float|int $minutes
* @return void
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function put($key, $value, $minutes = null)
public function put($key, $value, $ttl = null)
{
if (is_array($key)) {
return $this->putMany($key, $value);
}
if (! is_null($minutes = $this->getMinutes($minutes))) {
$this->store->put($this->itemKey($key), $value, $minutes);
$this->event(new KeyWritten($key, $value, $minutes));
if ($ttl === null) {
return $this->forever($key, $value);
}
$seconds = $this->getSeconds($ttl);
if ($seconds <= 0) {
return $this->forget($key);
}
$result = $this->store->put($this->itemKey($key), $value, $seconds);
if ($result) {
$this->event(new KeyWritten($key, $value, $seconds));
}
return $result;
}
/**
* Store multiple items in the cache for a given number of minutes.
* {@inheritdoc}
*/
public function set($key, $value, $ttl = null)
{
return $this->put($key, $value, $ttl);
}
/**
* Store multiple items in the cache for a given number of seconds.
*
* @param array $values
* @param float|int $minutes
* @return void
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function putMany(array $values, $minutes)
public function putMany(array $values, $ttl = null)
{
if (! is_null($minutes = $this->getMinutes($minutes))) {
$this->store->putMany($values, $minutes);
if ($ttl === null) {
return $this->putManyForever($values);
}
$seconds = $this->getSeconds($ttl);
if ($seconds <= 0) {
return $this->deleteMultiple(array_keys($values));
}
$result = $this->store->putMany($values, $seconds);
if ($result) {
foreach ($values as $key => $value) {
$this->event(new KeyWritten($key, $value, $minutes));
$this->event(new KeyWritten($key, $value, $seconds));
}
}
return $result;
}
/**
* Store multiple items in the cache indefinitely.
*
* @param array $values
* @return bool
*/
protected function putManyForever(array $values)
{
$result = true;
foreach ($values as $key => $value) {
if (! $this->forever($key, $value)) {
$result = false;
}
}
return $result;
}
/**
* {@inheritdoc}
*/
public function setMultiple($values, $ttl = null)
{
return $this->putMany(is_array($values) ? $values : iterator_to_array($values), $ttl);
}
/**
* Store an item in the cache if the key does not exist.
*
* @param string $key
* @param mixed $value
* @param \DateTime|float|int $minutes
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function add($key, $value, $minutes)
public function add($key, $value, $ttl = null)
{
if (is_null($minutes = $this->getMinutes($minutes))) {
return false;
}
if ($ttl !== null) {
if ($this->getSeconds($ttl) <= 0) {
return false;
}
// If the store has an "add" method we will call the method on the store so it
// 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')) {
return $this->store->add(
$this->itemKey($key), $value, $minutes
);
// If the store has an "add" method we will call the method on the store so it
// 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
);
}
}
// If the value did not exist in the cache, we will put the value in the cache
// 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))) {
$this->put($key, $value, $minutes);
return true;
return $this->put($key, $value, $ttl);
}
return false;
@ -259,44 +347,48 @@ class Repository implements CacheContract, ArrayAccess
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @return void
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
{
$this->store->forever($this->itemKey($key), $value);
$result = $this->store->forever($this->itemKey($key), $value);
$this->event(new KeyWritten($key, $value, 0));
if ($result) {
$this->event(new KeyWritten($key, $value));
}
return $result;
}
/**
* Get an item from the cache, or store the default value.
* Get an item from the cache, or execute the given Closure and store the result.
*
* @param string $key
* @param \DateTime|float|int $minutes
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @param \Closure $callback
* @return mixed
*/
public function remember($key, $minutes, Closure $callback)
public function remember($key, $ttl, Closure $callback)
{
$value = $this->get($key);
// If the item exists in the cache we will just return this immediately and if
// not we will execute the given Closure and cache the result of that for a
// given number of minutes so it's available for all subsequent requests.
// given number of seconds so it's available for all subsequent requests.
if (! is_null($value)) {
return $value;
}
$this->put($key, $value = $callback(), $minutes);
$this->put($key, $value = $callback(), $ttl);
return $value;
}
/**
* Get an item from the cache, or store the default value forever.
* Get an item from the cache, or execute the given Closure and store the result forever.
*
* @param string $key
* @param string $key
* @param \Closure $callback
* @return mixed
*/
@ -306,9 +398,9 @@ class Repository implements CacheContract, ArrayAccess
}
/**
* Get an item from the cache, or store the default value forever.
* Get an item from the cache, or execute the given Closure and store the result forever.
*
* @param string $key
* @param string $key
* @param \Closure $callback
* @return mixed
*/
@ -316,9 +408,9 @@ class Repository implements CacheContract, ArrayAccess
{
$value = $this->get($key);
// If the item exists in the cache we will just return this immediately and if
// not we will execute the given Closure and cache the result of that for a
// given number of minutes so it's available for all subsequent requests.
// If the item exists in the cache we will just return this immediately
// and if not we will execute the given Closure and cache the result
// of that forever so it is available for all subsequent requests.
if (! is_null($value)) {
return $value;
}
@ -336,11 +428,45 @@ class Repository implements CacheContract, ArrayAccess
*/
public function forget($key)
{
return tap($this->store->forget($this->itemKey($key)), function () use ($key) {
$this->event(new KeyForgotten($key));
return tap($this->store->forget($this->itemKey($key)), function ($result) use ($key) {
if ($result) {
$this->event(new KeyForgotten($key));
}
});
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return $this->forget($key);
}
/**
* {@inheritdoc}
*/
public function deleteMultiple($keys)
{
$result = true;
foreach ($keys as $key) {
if (! $this->forget($key)) {
$result = false;
}
}
return $result;
}
/**
* {@inheritdoc}
*/
public function clear()
{
return $this->store->flush();
}
/**
* Begin executing a new tags operation if the store supports it.
*
@ -355,7 +481,7 @@ class Repository implements CacheContract, ArrayAccess
throw new BadMethodCallException('This cache store does not support tagging.');
}
$cache = $this->store->tags($names);
$cache = $this->store->tags(is_array($names) ? $names : func_get_args());
if (! is_null($this->events)) {
$cache->setEventDispatcher($this->events);
@ -378,7 +504,7 @@ class Repository implements CacheContract, ArrayAccess
/**
* Get the default cache time.
*
* @return float|int
* @return int
*/
public function getDefaultCacheTime()
{
@ -386,14 +512,14 @@ class Repository implements CacheContract, ArrayAccess
}
/**
* Set the default cache time in minutes.
* Set the default cache time in seconds.
*
* @param float|int $minutes
* @param int|null $seconds
* @return $this
*/
public function setDefaultCacheTime($minutes)
public function setDefaultCacheTime($seconds)
{
$this->default = $minutes;
$this->default = $seconds;
return $this;
}
@ -478,25 +604,27 @@ class Repository implements CacheContract, ArrayAccess
}
/**
* Calculate the number of minutes with the given duration.
* Calculate the number of seconds for the given TTL.
*
* @param \DateTime|float|int $duration
* @return float|int|null
* @param \DateTimeInterface|\DateInterval|int $ttl
* @return int
*/
protected function getMinutes($duration)
protected function getSeconds($ttl)
{
if ($duration instanceof DateTime) {
$duration = Carbon::now()->diffInSeconds(Carbon::instance($duration), false) / 60;
$duration = $this->parseDateInterval($ttl);
if ($duration instanceof DateTimeInterface) {
$duration = Carbon::now()->diffInRealSeconds($duration, false);
}
return (int) ($duration * 60) > 0 ? $duration : null;
return (int) $duration > 0 ? $duration : 0;
}
/**
* Handle dynamic calls into macros or pass missing methods to the store.
*
* @param string $method
* @param array $parameters
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)

View file

@ -24,16 +24,22 @@ trait RetrievesMultipleKeys
}
/**
* Store multiple items in the cache for a given number of minutes.
* Store multiple items in the cache for a given number of seconds.
*
* @param array $values
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function putMany(array $values, $minutes)
public function putMany(array $values, $seconds)
{
$manyResult = null;
foreach ($values as $key => $value) {
$this->put($key, $value, $minutes);
$result = $this->put($key, $value, $seconds);
$manyResult = is_null($manyResult) ? $result : $result && $manyResult;
}
return $manyResult ?: false;
}
}

View file

@ -2,7 +2,9 @@
namespace Illuminate\Cache;
abstract class TaggableStore
use Illuminate\Contracts\Cache\Store;
abstract class TaggableStore implements Store
{
/**
* Begin executing a new tags operation.

View file

@ -6,7 +6,9 @@ use Illuminate\Contracts\Cache\Store;
class TaggedCache extends Repository
{
use RetrievesMultipleKeys;
use RetrievesMultipleKeys {
putMany as putManyAlias;
}
/**
* The tag set instance.
@ -29,11 +31,27 @@ class TaggedCache extends Repository
$this->tags = $tags;
}
/**
* Store multiple items in the cache for a given number of seconds.
*
* @param array $values
* @param int|null $ttl
* @return bool
*/
public function putMany(array $values, $ttl = null)
{
if ($ttl === null) {
return $this->putManyForever($values);
}
return $this->putManyAlias($values, $ttl);
}
/**
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return void
*/
public function increment($key, $value = 1)
@ -42,10 +60,10 @@ class TaggedCache extends Repository
}
/**
* Increment the value of an item in the cache.
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return void
*/
public function decrement($key, $value = 1)
@ -56,11 +74,13 @@ class TaggedCache extends Repository
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
$this->tags->reset();
return true;
}
/**
@ -92,4 +112,14 @@ class TaggedCache extends Repository
{
parent::event($event->setTags($this->tags->getNames()));
}
/**
* Get the tag set instance.
*
* @return \Illuminate\Cache\TagSet
*/
public function getTags()
{
return $this->tags;
}
}

View file

@ -14,10 +14,9 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/contracts": "5.4.*",
"illuminate/support": "5.4.*",
"nesbot/carbon": "~1.20"
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@ -26,13 +25,13 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "5.8-dev"
}
},
"suggest": {
"illuminate/database": "Required to use the database cache driver (5.4.*).",
"illuminate/filesystem": "Required to use the file cache driver (5.4.*).",
"illuminate/redis": "Required to use the redis cache driver (5.4.*)."
"illuminate/database": "Required to use the database cache driver (5.8.*).",
"illuminate/filesystem": "Required to use the file cache driver (5.8.*).",
"illuminate/redis": "Required to use the redis cache driver (5.8.*)."
},
"config": {
"sort-packages": true

View file

@ -40,15 +40,40 @@ class Repository implements ArrayAccess, ConfigContract
/**
* Get the specified configuration value.
*
* @param string $key
* @param array|string $key
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null)
{
if (is_array($key)) {
return $this->getMany($key);
}
return Arr::get($this->items, $key, $default);
}
/**
* Get many configuration values.
*
* @param array $keys
* @return array
*/
public function getMany($keys)
{
$config = [];
foreach ($keys as $key => $default) {
if (is_numeric($key)) {
[$key, $default] = [$default, null];
}
$config[$key] = Arr::get($this->items, $key, $default);
}
return $config;
}
/**
* Set a given configuration value.
*

View file

@ -14,9 +14,9 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/contracts": "5.4.*",
"illuminate/support": "5.4.*"
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@ -25,7 +25,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "5.8-dev"
}
},
"config": {

View file

@ -3,15 +3,21 @@
namespace Illuminate\Console;
use Closure;
use Illuminate\Support\ProcessUtils;
use Illuminate\Contracts\Events\Dispatcher;
use Symfony\Component\Process\ProcessUtils;
use Illuminate\Contracts\Container\Container;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Illuminate\Contracts\Console\Application as ApplicationContract;
class Application extends SymfonyApplication implements ApplicationContract
@ -37,6 +43,13 @@ class Application extends SymfonyApplication implements ApplicationContract
*/
protected static $bootstrappers = [];
/**
* The Event Dispatcher.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events;
/**
* Create a new Artisan console application.
*
@ -50,14 +63,39 @@ class Application extends SymfonyApplication implements ApplicationContract
parent::__construct('Laravel Framework', $version);
$this->laravel = $laravel;
$this->events = $events;
$this->setAutoExit(false);
$this->setCatchExceptions(false);
$events->dispatch(new Events\ArtisanStarting($this));
$this->events->dispatch(new Events\ArtisanStarting($this));
$this->bootstrap();
}
/**
* {@inheritdoc}
*/
public function run(InputInterface $input = null, OutputInterface $output = null)
{
$commandName = $this->getCommandName(
$input = $input ?: new ArgvInput
);
$this->events->dispatch(
new Events\CommandStarting(
$commandName, $input, $output = $output ?: new ConsoleOutput
)
);
$exitCode = parent::run($input, $output);
$this->events->dispatch(
new Events\CommandFinished($commandName, $input, $output, $exitCode)
);
return $exitCode;
}
/**
* Determine the proper PHP executable.
*
@ -127,22 +165,48 @@ class Application extends SymfonyApplication implements ApplicationContract
*
* @param string $command
* @param array $parameters
* @param \Symfony\Component\Console\Output\OutputInterface $outputBuffer
* @param \Symfony\Component\Console\Output\OutputInterface|null $outputBuffer
* @return int
*
* @throws \Symfony\Component\Console\Exception\CommandNotFoundException
*/
public function call($command, array $parameters = [], $outputBuffer = null)
{
$parameters = collect($parameters)->prepend($command);
[$command, $input] = $this->parseCommand($command, $parameters);
$this->lastOutput = $outputBuffer ?: new BufferedOutput;
if (! $this->has($command)) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $command));
}
$this->setCatchExceptions(false);
return $this->run(
$input, $this->lastOutput = $outputBuffer ?: new BufferedOutput
);
}
$result = $this->run(new ArrayInput($parameters->toArray()), $this->lastOutput);
/**
* Parse the incoming Artisan command and its input.
*
* @param string $command
* @param array $parameters
* @return array
*/
protected function parseCommand($command, $parameters)
{
if (is_subclass_of($command, SymfonyCommand::class)) {
$callingClass = true;
$this->setCatchExceptions(true);
$command = $this->laravel->make($command)->getName();
}
return $result;
if (! isset($callingClass) && empty($parameters)) {
$command = $this->getCommandName($input = new StringInput($command));
} else {
array_unshift($parameters, $command);
$input = new ArrayInput($parameters);
}
return [$command, $input ?? null];
}
/**
@ -152,7 +216,9 @@ class Application extends SymfonyApplication implements ApplicationContract
*/
public function output()
{
return $this->lastOutput ? $this->lastOutput->fetch() : '';
return $this->lastOutput && method_exists($this->lastOutput, 'fetch')
? $this->lastOutput->fetch()
: '';
}
/**
@ -210,7 +276,7 @@ class Application extends SymfonyApplication implements ApplicationContract
}
/**
* Get the default input definitions for the applications.
* Get the default input definition for the application.
*
* This is used to add the --env option to every available command.
*

View file

@ -2,6 +2,8 @@
namespace Illuminate\Console;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Support\Arrayable;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\ArrayInput;
@ -15,6 +17,8 @@ use Symfony\Component\Console\Command\Command as SymfonyCommand;
class Command extends SymfonyCommand
{
use Macroable;
/**
* The Laravel application instance.
*
@ -105,7 +109,7 @@ class Command extends SymfonyCommand
// the command we'll set the arguments and the options on this command.
$this->setDescription($this->description);
$this->setHidden($this->hidden);
$this->setHidden($this->isHidden());
if (! isset($this->signature)) {
$this->specifyParameters();
@ -119,20 +123,15 @@ class Command extends SymfonyCommand
*/
protected function configureUsingFluentDefinition()
{
list($name, $arguments, $options) = Parser::parse($this->signature);
[$name, $arguments, $options] = Parser::parse($this->signature);
parent::__construct($this->name = $name);
// After parsing the signature we will spin through the arguments and options
// and set them on this command. These will already be changed into proper
// instances of these "InputArgument" and "InputOption" Symfony classes.
foreach ($arguments as $argument) {
$this->getDefinition()->addArgument($argument);
}
foreach ($options as $option) {
$this->getDefinition()->addOption($option);
}
$this->getDefinition()->addArguments($arguments);
$this->getDefinition()->addOptions($options);
}
/**
@ -163,8 +162,12 @@ class Command extends SymfonyCommand
*/
public function run(InputInterface $input, OutputInterface $output)
{
$this->output = $this->laravel->make(
OutputStyle::class, ['input' => $input, 'output' => $output]
);
return parent::run(
$this->input = $input, $this->output = new OutputStyle($input, $output)
$this->input = $input, $this->output
);
}
@ -177,9 +180,7 @@ class Command extends SymfonyCommand
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$method = method_exists($this, 'handle') ? 'handle' : 'fire';
return $this->laravel->call([$this, $method]);
return $this->laravel->call([$this, 'handle']);
}
/**
@ -194,7 +195,7 @@ class Command extends SymfonyCommand
$arguments['command'] = $command;
return $this->getApplication()->find($command)->run(
new ArrayInput($arguments), $this->output
$this->createInputFromArguments($arguments), $this->output
);
}
@ -210,10 +211,43 @@ class Command extends SymfonyCommand
$arguments['command'] = $command;
return $this->getApplication()->find($command)->run(
new ArrayInput($arguments), new NullOutput
$this->createInputFromArguments($arguments), new NullOutput
);
}
/**
* Create an input instance from the given arguments.
*
* @param array $arguments
* @return \Symfony\Component\Console\Input\ArrayInput
*/
protected function createInputFromArguments(array $arguments)
{
return tap(new ArrayInput(array_merge($this->context(), $arguments)), function ($input) {
if ($input->hasParameterOption(['--no-interaction'], true)) {
$input->setInteractive(false);
}
});
}
/**
* Get all of the context passed to the command.
*
* @return array
*/
protected function context()
{
return collect($this->option())->only([
'ansi',
'no-ansi',
'no-interaction',
'quiet',
'verbose',
])->filter()->mapWithKeys(function ($value, $key) {
return ["--{$key}" => $value];
})->all();
}
/**
* Determine if the given argument is present.
*
@ -229,7 +263,7 @@ class Command extends SymfonyCommand
* Get the value of a command argument.
*
* @param string|null $key
* @return string|array
* @return string|array|null
*/
public function argument($key = null)
{
@ -264,8 +298,8 @@ class Command extends SymfonyCommand
/**
* Get the value of a command option.
*
* @param string $key
* @return string|array
* @param string|null $key
* @return string|array|bool|null
*/
public function option($key = null)
{
@ -302,8 +336,8 @@ class Command extends SymfonyCommand
* Prompt the user for input.
*
* @param string $question
* @param string $default
* @return string
* @param string|null $default
* @return mixed
*/
public function ask($question, $default = null)
{
@ -315,8 +349,8 @@ class Command extends SymfonyCommand
*
* @param string $question
* @param array $choices
* @param string $default
* @return string
* @param string|null $default
* @return mixed
*/
public function anticipate($question, array $choices, $default = null)
{
@ -328,8 +362,8 @@ class Command extends SymfonyCommand
*
* @param string $question
* @param array $choices
* @param string $default
* @return string
* @param string|null $default
* @return mixed
*/
public function askWithCompletion($question, array $choices, $default = null)
{
@ -345,7 +379,7 @@ class Command extends SymfonyCommand
*
* @param string $question
* @param bool $fallback
* @return string
* @return mixed
*/
public function secret($question, $fallback = true)
{
@ -361,9 +395,9 @@ class Command extends SymfonyCommand
*
* @param string $question
* @param array $choices
* @param string $default
* @param mixed $attempts
* @param bool $multiple
* @param string|null $default
* @param mixed|null $attempts
* @param bool|null $multiple
* @return string
*/
public function choice($question, array $choices, $default = null, $attempts = null, $multiple = null)
@ -380,10 +414,11 @@ class Command extends SymfonyCommand
*
* @param array $headers
* @param \Illuminate\Contracts\Support\Arrayable|array $rows
* @param string $style
* @param string $tableStyle
* @param array $columnStyles
* @return void
*/
public function table(array $headers, $rows, $style = 'default')
public function table($headers, $rows, $tableStyle = 'default', array $columnStyles = [])
{
$table = new Table($this->output);
@ -391,14 +426,20 @@ class Command extends SymfonyCommand
$rows = $rows->toArray();
}
$table->setHeaders($headers)->setRows($rows)->setStyle($style)->render();
$table->setHeaders((array) $headers)->setRows($rows)->setStyle($tableStyle);
foreach ($columnStyles as $columnIndex => $columnStyle) {
$table->setColumnStyle($columnIndex, $columnStyle);
}
$table->render();
}
/**
* Write a string as information output.
*
* @param string $string
* @param null|int|string $verbosity
* @param int|string|null $verbosity
* @return void
*/
public function info($string, $verbosity = null)
@ -410,8 +451,8 @@ class Command extends SymfonyCommand
* Write a string as standard output.
*
* @param string $string
* @param string $style
* @param null|int|string $verbosity
* @param string|null $style
* @param int|string|null $verbosity
* @return void
*/
public function line($string, $style = null, $verbosity = null)
@ -425,7 +466,7 @@ class Command extends SymfonyCommand
* Write a string as comment output.
*
* @param string $string
* @param null|int|string $verbosity
* @param int|string|null $verbosity
* @return void
*/
public function comment($string, $verbosity = null)
@ -437,7 +478,7 @@ class Command extends SymfonyCommand
* Write a string as question output.
*
* @param string $string
* @param null|int|string $verbosity
* @param int|string|null $verbosity
* @return void
*/
public function question($string, $verbosity = null)
@ -449,7 +490,7 @@ class Command extends SymfonyCommand
* Write a string as error output.
*
* @param string $string
* @param null|int|string $verbosity
* @param int|string|null $verbosity
* @return void
*/
public function error($string, $verbosity = null)
@ -461,7 +502,7 @@ class Command extends SymfonyCommand
* Write a string as warning output.
*
* @param string $string
* @param null|int|string $verbosity
* @param int|string|null $verbosity
* @return void
*/
public function warn($string, $verbosity = null)
@ -483,11 +524,13 @@ class Command extends SymfonyCommand
*/
public function alert($string)
{
$this->comment(str_repeat('*', strlen($string) + 12));
$this->comment('* '.$string.' *');
$this->comment(str_repeat('*', strlen($string) + 12));
$length = Str::length(strip_tags($string)) + 12;
$this->output->writeln('');
$this->comment(str_repeat('*', $length));
$this->comment('* '.$string.' *');
$this->comment(str_repeat('*', $length));
$this->output->newLine();
}
/**
@ -504,7 +547,7 @@ class Command extends SymfonyCommand
/**
* Get the verbosity level in terms of Symfony's OutputInterface level.
*
* @param string|int $level
* @param string|int|null $level
* @return int
*/
protected function parseVerbosity($level = null)
@ -518,6 +561,24 @@ class Command extends SymfonyCommand
return $level;
}
/**
* {@inheritdoc}
*/
public function isHidden()
{
return $this->hidden;
}
/**
* {@inheritdoc}
*/
public function setHidden($hidden)
{
parent::setHidden($this->hidden = $hidden);
return $this;
}
/**
* Get the console command arguments.
*
@ -541,7 +602,7 @@ class Command extends SymfonyCommand
/**
* Get the output implementation.
*
* @return \Symfony\Component\Console\Output\OutputInterface
* @return \Illuminate\Console\OutputStyle
*/
public function getOutput()
{

View file

@ -22,7 +22,7 @@ trait ConfirmableTrait
$shouldConfirm = $callback instanceof Closure ? call_user_func($callback) : $callback;
if ($shouldConfirm) {
if ($this->option('force')) {
if ($this->hasOption('force') && $this->option('force')) {
return true;
}
@ -48,7 +48,7 @@ trait ConfirmableTrait
protected function getDefaultConfirmCallback()
{
return function () {
return $this->getLaravel()->environment() == 'production';
return $this->getLaravel()->environment() === 'production';
};
}
}

View file

@ -46,8 +46,9 @@ abstract class GeneratorCommand extends Command
* Execute the console command.
*
* @return bool|null
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function fire()
public function handle()
{
$name = $this->qualifyClass($this->getNameInput());
@ -56,7 +57,9 @@ abstract class GeneratorCommand extends Command
// First we will check to see if the class already exists. If it does, we don't want
// to create the class and overwrite the user's code. So, we will bail out so the
// code is untouched. Otherwise, we will continue generating this class' files.
if ($this->alreadyExists($this->getNameInput())) {
if ((! $this->hasOption('force') ||
! $this->option('force')) &&
$this->alreadyExists($this->getNameInput())) {
$this->error($this->type.' already exists!');
return false;
@ -80,6 +83,8 @@ abstract class GeneratorCommand extends Command
*/
protected function qualifyClass($name)
{
$name = ltrim($name, '\\/');
$rootNamespace = $this->rootNamespace();
if (Str::startsWith($name, $rootNamespace)) {
@ -123,7 +128,7 @@ abstract class GeneratorCommand extends Command
*/
protected function getPath($name)
{
$name = str_replace_first($this->rootNamespace(), '', $name);
$name = Str::replaceFirst($this->rootNamespace(), '', $name);
return $this->laravel['path'].'/'.str_replace('\\', '/', $name).'.php';
}
@ -148,6 +153,7 @@ abstract class GeneratorCommand extends Command
*
* @param string $name
* @return string
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function buildClass($name)
{
@ -166,8 +172,8 @@ abstract class GeneratorCommand extends Command
protected function replaceNamespace(&$stub, $name)
{
$stub = str_replace(
['DummyNamespace', 'DummyRootNamespace'],
[$this->getNamespace($name), $this->rootNamespace()],
['DummyNamespace', 'DummyRootNamespace', 'NamespacedDummyUserModel'],
[$this->getNamespace($name), $this->rootNamespace(), $this->userProviderModel()],
$stub
);
@ -219,6 +225,20 @@ abstract class GeneratorCommand extends Command
return $this->laravel->getNamespace();
}
/**
* Get the model for the default guard's user provider.
*
* @return string|null
*/
protected function userProviderModel()
{
$guard = config('auth.defaults.guard');
$provider = config("auth.guards.{$guard}.provider");
return config("auth.providers.{$provider}.model");
}
/**
* Get the console command arguments.
*

View file

@ -35,6 +35,8 @@ class Parser
*
* @param string $expression
* @return string
*
* @throws \InvalidArgumentException
*/
protected static function name($expression)
{
@ -80,7 +82,7 @@ class Parser
*/
protected static function parseArgument($token)
{
list($token, $description) = static::extractDescription($token);
[$token, $description] = static::extractDescription($token);
switch (true) {
case Str::endsWith($token, '?*'):
@ -89,6 +91,8 @@ class Parser
return new InputArgument(trim($token, '*'), InputArgument::IS_ARRAY | InputArgument::REQUIRED, $description);
case Str::endsWith($token, '?'):
return new InputArgument(trim($token, '?'), InputArgument::OPTIONAL, $description);
case preg_match('/(.+)\=\*(.+)/', $token, $matches):
return new InputArgument($matches[1], InputArgument::IS_ARRAY, $description, preg_split('/,\s?/', $matches[2]));
case preg_match('/(.+)\=(.+)/', $token, $matches):
return new InputArgument($matches[1], InputArgument::OPTIONAL, $description, $matches[2]);
default:
@ -104,7 +108,7 @@ class Parser
*/
protected static function parseOption($token)
{
list($token, $description) = static::extractDescription($token);
[$token, $description] = static::extractDescription($token);
$matches = preg_split('/\s*\|\s*/', $token, 2);
@ -120,6 +124,8 @@ class Parser
return new InputOption(trim($token, '='), $shortcut, InputOption::VALUE_OPTIONAL, $description);
case Str::endsWith($token, '=*'):
return new InputOption(trim($token, '=*'), $shortcut, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, $description);
case preg_match('/(.+)\=\*(.+)/', $token, $matches):
return new InputOption($matches[1], $shortcut, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, $description, preg_split('/,\s?/', $matches[2]));
case preg_match('/(.+)\=(.+)/', $token, $matches):
return new InputOption($matches[1], $shortcut, InputOption::VALUE_OPTIONAL, $description, $matches[2]);
default:
@ -137,6 +143,6 @@ class Parser
{
$parts = preg_split('/\s+:\s+/', trim($token), 2);
return count($parts) === 2 ? $parts : [$token, null];
return count($parts) === 2 ? $parts : [$token, ''];
}
}

View file

@ -1,61 +0,0 @@
<?php
namespace Illuminate\Console\Scheduling;
use Illuminate\Contracts\Cache\Repository as Cache;
class CacheMutex implements Mutex
{
/**
* The cache repository implementation.
*
* @var \Illuminate\Contracts\Cache\Repository
*/
public $cache;
/**
* Create a new overlapping strategy.
*
* @param \Illuminate\Contracts\Cache\Repository $cache
* @return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
/**
* Attempt to obtain a mutex for the given event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return bool
*/
public function create(Event $event)
{
return $this->cache->add(
$event->mutexName(), true, $event->expiresAt
);
}
/**
* Determine if a mutex exists for the given event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return bool
*/
public function exists(Event $event)
{
return $this->cache->has($event->mutexName());
}
/**
* Clear the mutex for the given event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return void
*/
public function forget(Event $event)
{
$this->cache->forget($event->mutexName());
}
}

View file

@ -25,14 +25,14 @@ class CallbackEvent extends Event
/**
* Create a new event instance.
*
* @param \Illuminate\Console\Scheduling\Mutex $mutex
* @param \Illuminate\Console\Scheduling\EventMutex $mutex
* @param string $callback
* @param array $parameters
* @return void
*
* @throws \InvalidArgumentException
*/
public function __construct(Mutex $mutex, $callback, array $parameters = [])
public function __construct(EventMutex $mutex, $callback, array $parameters = [])
{
if (! is_string($callback) && ! is_callable($callback)) {
throw new InvalidArgumentException(
@ -60,17 +60,25 @@ class CallbackEvent extends Event
return;
}
register_shutdown_function(function () {
$this->removeMutex();
$pid = getmypid();
register_shutdown_function(function () use ($pid) {
if ($pid === getmypid()) {
$this->removeMutex();
}
});
parent::callBeforeCallbacks($container);
try {
$response = $container->call($this->callback, $this->parameters);
$response = is_object($this->callback)
? $container->call([$this->callback, '__invoke'], $this->parameters)
: $container->call($this->callback, $this->parameters);
} finally {
$this->removeMutex();
}
parent::callAfterCallbacks($container);
parent::callAfterCallbacks($container);
}
return $response;
}
@ -82,7 +90,7 @@ class CallbackEvent extends Event
*/
protected function removeMutex()
{
if ($this->description) {
if ($this->description && $this->withoutOverlapping) {
$this->mutex->forget($this);
}
}
@ -92,6 +100,8 @@ class CallbackEvent extends Event
*
* @param int $expiresAt
* @return $this
*
* @throws \LogicException
*/
public function withoutOverlapping($expiresAt = 1440)
{
@ -110,6 +120,26 @@ class CallbackEvent extends Event
});
}
/**
* Allow the event to only run on one server for each cron expression.
*
* @return $this
*
* @throws \LogicException
*/
public function onOneServer()
{
if (! isset($this->description)) {
throw new LogicException(
"A scheduled event name is required to only run on one server. Use the 'name' method before 'onOneServer'."
);
}
$this->onOneServer = true;
return $this;
}
/**
* Get the mutex name for the scheduled command.
*

View file

@ -3,7 +3,7 @@
namespace Illuminate\Console\Scheduling;
use Illuminate\Console\Application;
use Symfony\Component\Process\ProcessUtils;
use Illuminate\Support\ProcessUtils;
class CommandBuilder
{
@ -17,9 +17,9 @@ class CommandBuilder
{
if ($event->runInBackground) {
return $this->buildBackgroundCommand($event);
} else {
return $this->buildForegroundCommand($event);
}
return $this->buildForegroundCommand($event);
}
/**

View file

@ -3,9 +3,11 @@
namespace Illuminate\Console\Scheduling;
use Closure;
use Carbon\Carbon;
use Cron\CronExpression;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use GuzzleHttp\Client as HttpClient;
use Illuminate\Support\Facades\Date;
use Illuminate\Contracts\Mail\Mailer;
use Symfony\Component\Process\Process;
use Illuminate\Support\Traits\Macroable;
@ -27,7 +29,7 @@ class Event
*
* @var string
*/
public $expression = '* * * * * *';
public $expression = '* * * * *';
/**
* The timezone the date should be evaluated on.
@ -64,6 +66,13 @@ class Event
*/
public $withoutOverlapping = false;
/**
* Indicates if the command should only be allowed to run on one server for each cron expression.
*
* @var bool
*/
public $onOneServer = false;
/**
* The amount of time the mutex should be valid.
*
@ -128,23 +137,33 @@ class Event
public $description;
/**
* The mutex implementation.
* The event mutex implementation.
*
* @var \Illuminate\Console\Scheduling\Mutex
* @var \Illuminate\Console\Scheduling\EventMutex
*/
public $mutex;
/**
* The exit status code of the command.
*
* @var int|null
*/
public $exitCode;
/**
* Create a new event instance.
*
* @param \Illuminate\Console\Scheduling\Mutex $mutex
* @param \Illuminate\Console\Scheduling\EventMutex $mutex
* @param string $command
* @param \DateTimeZone|string|null $timezone
* @return void
*/
public function __construct(Mutex $mutex, $command)
public function __construct(EventMutex $mutex, $command, $timezone = null)
{
$this->mutex = $mutex;
$this->command = $command;
$this->timezone = $timezone;
$this->output = $this->getDefaultOutput();
}
@ -155,7 +174,7 @@ class Event
*/
public function getDefaultOutput()
{
return (DIRECTORY_SEPARATOR == '\\') ? 'NUL' : '/dev/null';
return (DIRECTORY_SEPARATOR === '\\') ? 'NUL' : '/dev/null';
}
/**
@ -196,9 +215,7 @@ class Event
{
$this->callBeforeCallbacks($container);
(new Process(
$this->buildCommand(), base_path(), null, null, null
))->run();
$this->exitCode = Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run();
$this->callAfterCallbacks($container);
}
@ -213,9 +230,7 @@ class Event
{
$this->callBeforeCallbacks($container);
(new Process(
$this->buildCommand(), base_path(), null, null, null
))->run();
Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run();
}
/**
@ -330,6 +345,18 @@ class Event
return true;
}
/**
* Ensure that the output is stored on disk in a log file.
*
* @return $this
*/
public function storeOutput()
{
$this->ensureOutputIsBeingCaptured();
return $this;
}
/**
* Send the output of the command to a given location.
*
@ -368,9 +395,9 @@ class Event
*/
public function emailOutputTo($addresses, $onlyIfOutputExists = false)
{
$this->ensureOutputIsBeingCapturedForEmail();
$this->ensureOutputIsBeingCaptured();
$addresses = is_array($addresses) ? $addresses : [$addresses];
$addresses = Arr::wrap($addresses);
return $this->then(function (Mailer $mailer) use ($addresses, $onlyIfOutputExists) {
$this->emailOutput($mailer, $addresses, $onlyIfOutputExists);
@ -391,11 +418,28 @@ class Event
}
/**
* Ensure that output is being captured for email.
* E-mail the results of the scheduled operation if it fails.
*
* @param array|mixed $addresses
* @return $this
*/
public function emailOutputOnFailure($addresses)
{
$this->ensureOutputIsBeingCaptured();
$addresses = Arr::wrap($addresses);
return $this->onFailure(function (Mailer $mailer) use ($addresses) {
$this->emailOutput($mailer, $addresses, false);
});
}
/**
* Ensure that the command output is being captured.
*
* @return void
*/
protected function ensureOutputIsBeingCapturedForEmail()
protected function ensureOutputIsBeingCaptured()
{
if (is_null($this->output) || $this->output == $this->getDefaultOutput()) {
$this->sendOutputTo(storage_path('logs/schedule-'.sha1($this->mutexName()).'.log'));
@ -434,7 +478,7 @@ class Event
return $this->description;
}
return 'Scheduled Job Output';
return "Scheduled Job Output For [{$this->command}]";
}
/**
@ -450,6 +494,18 @@ class Event
});
}
/**
* Register a callback to ping a given URL before the job runs if the given condition is true.
*
* @param bool $value
* @param string $url
* @return $this
*/
public function pingBeforeIf($value, $url)
{
return $value ? $this->pingBefore($url) : $this;
}
/**
* Register a callback to ping a given URL after the job runs.
*
@ -463,6 +519,44 @@ class Event
});
}
/**
* Register a callback to ping a given URL after the job runs if the given condition is true.
*
* @param bool $value
* @param string $url
* @return $this
*/
public function thenPingIf($value, $url)
{
return $value ? $this->thenPing($url) : $this;
}
/**
* Register a callback to ping a given URL if the operation succeeds.
*
* @param string $url
* @return $this
*/
public function pingOnSuccess($url)
{
return $this->onSuccess(function () use ($url) {
(new HttpClient)->get($url);
});
}
/**
* Register a callback to ping a given URL if the operation fails.
*
* @param string $url
* @return $this
*/
public function pingOnFailure($url)
{
return $this->onFailure(function () use ($url) {
(new HttpClient)->get($url);
});
}
/**
* State that the command should run in background.
*
@ -533,14 +627,13 @@ class Event
}
/**
* Register a callback to further filter the schedule.
* Allow the event to only run on one server for each cron expression.
*
* @param \Closure $callback
* @return $this
*/
public function when(Closure $callback)
public function onOneServer()
{
$this->filters[] = $callback;
$this->onOneServer = true;
return $this;
}
@ -548,12 +641,29 @@ class Event
/**
* Register a callback to further filter the schedule.
*
* @param \Closure $callback
* @param \Closure|bool $callback
* @return $this
*/
public function skip(Closure $callback)
public function when($callback)
{
$this->rejects[] = $callback;
$this->filters[] = is_callable($callback) ? $callback : function () use ($callback) {
return $callback;
};
return $this;
}
/**
* Register a callback to further filter the schedule.
*
* @param \Closure|bool $callback
* @return $this
*/
public function skip($callback)
{
$this->rejects[] = is_callable($callback) ? $callback : function () use ($callback) {
return $callback;
};
return $this;
}
@ -595,6 +705,36 @@ class Event
return $this;
}
/**
* Register a callback to be called if the operation succeeds.
*
* @param \Closure $callback
* @return $this
*/
public function onSuccess(Closure $callback)
{
return $this->then(function (Container $container) use ($callback) {
if (0 === $this->exitCode) {
$container->call($callback);
}
});
}
/**
* Register a callback to be called if the operation fails.
*
* @param \Closure $callback
* @return $this
*/
public function onFailure(Closure $callback)
{
return $this->then(function (Container $container) use ($callback) {
if (0 !== $this->exitCode) {
$container->call($callback);
}
});
}
/**
* Set the human-friendly description of the event.
*
@ -636,16 +776,16 @@ class Event
/**
* Determine the next due date for an event.
*
* @param \DateTime|string $currentTime
* @param \DateTimeInterface|string $currentTime
* @param int $nth
* @param bool $allowCurrentDate
* @return \Carbon\Carbon
* @return \Illuminate\Support\Carbon
*/
public function nextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
{
return Carbon::instance($nextDue = CronExpression::factory(
return Date::instance(CronExpression::factory(
$this->getExpression()
)->getNextRunDate($currentTime, $nth, $allowCurrentDate));
)->getNextRunDate($currentTime, $nth, $allowCurrentDate, $this->timezone));
}
/**
@ -659,12 +799,12 @@ class Event
}
/**
* Set the mutex implementation to be used.
* Set the event mutex implementation to be used.
*
* @param \Illuminate\Console\Scheduling\Mutex $mutex
* @param \Illuminate\Console\Scheduling\EventMutex $mutex
* @return $this
*/
public function preventOverlapsUsing(Mutex $mutex)
public function preventOverlapsUsing(EventMutex $mutex)
{
$this->mutex = $mutex;

View file

@ -2,7 +2,7 @@
namespace Illuminate\Console\Scheduling;
use Carbon\Carbon;
use Illuminate\Support\Carbon;
trait ManagesFrequencies
{
@ -61,6 +61,56 @@ trait ManagesFrequencies
};
}
/**
* Schedule the event to run every minute.
*
* @return $this
*/
public function everyMinute()
{
return $this->spliceIntoPosition(1, '*');
}
/**
* Schedule the event to run every five minutes.
*
* @return $this
*/
public function everyFiveMinutes()
{
return $this->spliceIntoPosition(1, '*/5');
}
/**
* Schedule the event to run every ten minutes.
*
* @return $this
*/
public function everyTenMinutes()
{
return $this->spliceIntoPosition(1, '*/10');
}
/**
* Schedule the event to run every fifteen minutes.
*
* @return $this
*/
public function everyFifteenMinutes()
{
return $this->spliceIntoPosition(1, '*/15');
}
/**
* Schedule the event to run every thirty minutes.
*
* @return $this
*/
public function everyThirtyMinutes()
{
return $this->spliceIntoPosition(1, '0,30');
}
/**
* Schedule the event to run hourly.
*
@ -74,11 +124,13 @@ trait ManagesFrequencies
/**
* Schedule the event to run hourly at a given offset in the hour.
*
* @param int $offset
* @param array|int $offset
* @return $this
*/
public function hourlyAt($offset)
{
$offset = is_array($offset) ? implode(',', $offset) : $offset;
return $this->spliceIntoPosition(1, $offset);
}
@ -115,7 +167,7 @@ trait ManagesFrequencies
$segments = explode(':', $time);
return $this->spliceIntoPosition(2, (int) $segments[0])
->spliceIntoPosition(1, count($segments) == 2 ? (int) $segments[1] : '0');
->spliceIntoPosition(1, count($segments) === 2 ? (int) $segments[1] : '0');
}
/**
@ -301,7 +353,7 @@ trait ManagesFrequencies
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, 0)
->spliceIntoPosition(3, 1)
->spliceIntoPosition(4, '*/3');
->spliceIntoPosition(4, '1-12/3');
}
/**
@ -317,46 +369,6 @@ trait ManagesFrequencies
->spliceIntoPosition(4, 1);
}
/**
* Schedule the event to run every minute.
*
* @return $this
*/
public function everyMinute()
{
return $this->spliceIntoPosition(1, '*');
}
/**
* Schedule the event to run every five minutes.
*
* @return $this
*/
public function everyFiveMinutes()
{
return $this->spliceIntoPosition(1, '*/5');
}
/**
* Schedule the event to run every ten minutes.
*
* @return $this
*/
public function everyTenMinutes()
{
return $this->spliceIntoPosition(1, '*/10');
}
/**
* Schedule the event to run every thirty minutes.
*
* @return $this
*/
public function everyThirtyMinutes()
{
return $this->spliceIntoPosition(1, '0,30');
}
/**
* Set the days of the week the command should run on.
*

View file

@ -1,30 +0,0 @@
<?php
namespace Illuminate\Console\Scheduling;
interface Mutex
{
/**
* Attempt to obtain a mutex for the given event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return bool
*/
public function create(Event $event);
/**
* Determine if a mutex exists for the given event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return bool
*/
public function exists(Event $event);
/**
* Clear the mutex for the given event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return void
*/
public function forget(Event $event);
}

View file

@ -2,9 +2,11 @@
namespace Illuminate\Console\Scheduling;
use DateTimeInterface;
use Illuminate\Console\Application;
use Illuminate\Container\Container;
use Symfony\Component\Process\ProcessUtils;
use Illuminate\Support\ProcessUtils;
use Illuminate\Contracts\Queue\ShouldQueue;
class Schedule
{
@ -16,37 +18,58 @@ class Schedule
protected $events = [];
/**
* The mutex implementation.
* The event mutex implementation.
*
* @var \Illuminate\Console\Scheduling\Mutex
* @var \Illuminate\Console\Scheduling\EventMutex
*/
protected $mutex;
protected $eventMutex;
/**
* The scheduling mutex implementation.
*
* @var \Illuminate\Console\Scheduling\SchedulingMutex
*/
protected $schedulingMutex;
/**
* The timezone the date should be evaluated on.
*
* @var \DateTimeZone|string
*/
protected $timezone;
/**
* Create a new schedule instance.
*
* @param \DateTimeZone|string|null $timezone
* @return void
*/
public function __construct()
public function __construct($timezone = null)
{
$this->timezone = $timezone;
$container = Container::getInstance();
$this->mutex = $container->bound(Mutex::class)
? $container->make(Mutex::class)
: $container->make(CacheMutex::class);
$this->eventMutex = $container->bound(EventMutex::class)
? $container->make(EventMutex::class)
: $container->make(CacheEventMutex::class);
$this->schedulingMutex = $container->bound(SchedulingMutex::class)
? $container->make(SchedulingMutex::class)
: $container->make(CacheSchedulingMutex::class);
}
/**
* Add a new callback event to the schedule.
*
* @param string|callable $callback
* @param array $parameters
* @param array $parameters
* @return \Illuminate\Console\Scheduling\CallbackEvent
*/
public function call($callback, array $parameters = [])
{
$this->events[] = $event = new CallbackEvent(
$this->mutex, $callback, $parameters
$this->eventMutex, $callback, $parameters
);
return $event;
@ -74,12 +97,22 @@ class Schedule
* Add a new job callback event to the schedule.
*
* @param object|string $job
* @param string|null $queue
* @param string|null $connection
* @return \Illuminate\Console\Scheduling\CallbackEvent
*/
public function job($job)
public function job($job, $queue = null, $connection = null)
{
return $this->call(function () use ($job) {
dispatch(is_string($job) ? resolve($job) : $job);
return $this->call(function () use ($job, $queue, $connection) {
$job = is_string($job) ? resolve($job) : $job;
if ($job instanceof ShouldQueue) {
dispatch($job)
->onConnection($connection ?? $job->connection)
->onQueue($queue ?? $job->queue);
} else {
dispatch_now($job);
}
})->name(is_string($job) ? $job : get_class($job));
}
@ -96,7 +129,7 @@ class Schedule
$command .= ' '.$this->compileParameters($parameters);
}
$this->events[] = $event = new Event($this->mutex, $command);
$this->events[] = $event = new Event($this->eventMutex, $command, $this->timezone);
return $event;
}
@ -122,11 +155,23 @@ class Schedule
})->implode(' ');
}
/**
* Determine if the server is allowed to run this event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @param \DateTimeInterface $time
* @return bool
*/
public function serverShouldRun(Event $event, DateTimeInterface $time)
{
return $this->schedulingMutex->create($event, $time);
}
/**
* Get all of the events on the schedule that are due.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return array
* @return \Illuminate\Support\Collection
*/
public function dueEvents($app)
{
@ -142,4 +187,23 @@ class Schedule
{
return $this->events;
}
/**
* Specify the cache store that should be used to store mutexes.
*
* @param string $store
* @return $this
*/
public function useCache($store)
{
if ($this->eventMutex instanceof CacheEventMutex) {
$this->eventMutex->useStore($store);
}
if ($this->schedulingMutex instanceof CacheSchedulingMutex) {
$this->schedulingMutex->useStore($store);
}
return $this;
}
}

View file

@ -52,7 +52,7 @@ class ScheduleFinishCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
collect($this->schedule->events())->filter(function ($value) {
return $value->mutexName() == $this->argument('id');

View file

@ -3,6 +3,7 @@
namespace Illuminate\Console\Scheduling;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Date;
class ScheduleRunCommand extends Command
{
@ -27,6 +28,20 @@ class ScheduleRunCommand extends Command
*/
protected $schedule;
/**
* The 24 hour timestamp this scheduler command started running.
*
* @var \Illuminate\Support\Carbon;
*/
protected $startedAt;
/**
* Check if any events ran.
*
* @var bool
*/
protected $eventsRan = false;
/**
* Create a new command instance.
*
@ -37,6 +52,8 @@ class ScheduleRunCommand extends Command
{
$this->schedule = $schedule;
$this->startedAt = Date::now();
parent::__construct();
}
@ -45,24 +62,54 @@ class ScheduleRunCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
$eventsRan = false;
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
continue;
}
$this->line('<info>Running scheduled command:</info> '.$event->getSummaryForDisplay());
if ($event->onOneServer) {
$this->runSingleServerEvent($event);
} else {
$this->runEvent($event);
}
$event->run($this->laravel);
$eventsRan = true;
$this->eventsRan = true;
}
if (! $eventsRan) {
if (! $this->eventsRan) {
$this->info('No scheduled commands are ready to run.');
}
}
/**
* Run the given single server event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return void
*/
protected function runSingleServerEvent($event)
{
if ($this->schedule->serverShouldRun($event, $this->startedAt)) {
$this->runEvent($event);
} else {
$this->line('<info>Skipping command (has already run on another server):</info> '.$event->getSummaryForDisplay());
}
}
/**
* Run the given event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return void
*/
protected function runEvent($event)
{
$this->line('<info>Running scheduled command:</info> '.$event->getSummaryForDisplay());
$event->run($this->laravel);
$this->eventsRan = true;
}
}

View file

@ -14,11 +14,11 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/contracts": "5.4.*",
"illuminate/support": "5.4.*",
"nesbot/carbon": "~1.20",
"symfony/console": "~3.2"
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*",
"symfony/console": "^4.2",
"symfony/process": "^4.2"
},
"autoload": {
"psr-4": {
@ -27,13 +27,13 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "5.8-dev"
}
},
"suggest": {
"guzzlehttp/guzzle": "Required to use the ping methods on schedules (~6.0).",
"mtdowling/cron-expression": "Required to use scheduling component (~1.0).",
"symfony/process": "Required to use scheduling component (~3.2)."
"dragonmantank/cron-expression": "Required to use scheduling component (^2.0).",
"guzzlehttp/guzzle": "Required to use the ping methods on schedules (^6.0).",
"illuminate/filesystem": "Required to use the generator command (5.8.*)"
},
"config": {
"sort-packages": true

View file

@ -17,6 +17,9 @@ class BoundMethod
* @param array $parameters
* @param string|null $defaultMethod
* @return mixed
*
* @throws \ReflectionException
* @throws \InvalidArgumentException
*/
public static function call($container, $callback, array $parameters = [], $defaultMethod = null)
{
@ -49,7 +52,7 @@ class BoundMethod
// We will assume an @ sign is used to delimit the class name from the method
// name. We will split on this @ sign and then build a callable array that
// we can pass right back into the "call" method for dependency binding.
$method = count($segments) == 2
$method = count($segments) === 2
? $segments[1] : $defaultMethod;
if (is_null($method)) {
@ -103,10 +106,12 @@ class BoundMethod
/**
* Get all dependencies for a given method.
*
* @param \Illuminate\Container\Container
* @param \Illuminate\Container\Container $container
* @param callable|string $callback
* @param array $parameters
* @return array
*
* @throws \ReflectionException
*/
protected static function getMethodDependencies($container, $callback, array $parameters = [])
{
@ -122,8 +127,10 @@ class BoundMethod
/**
* Get the proper reflection instance for the given callback.
*
* @param callable|string $callback
* @param callable|string $callback
* @return \ReflectionFunctionAbstract
*
* @throws \ReflectionException
*/
protected static function getCallReflector($callback)
{
@ -143,7 +150,7 @@ class BoundMethod
* @param \ReflectionParameter $parameter
* @param array $parameters
* @param array $dependencies
* @return mixed
* @return void
*/
protected static function addDependencyForCallParameter($container, $parameter,
array &$parameters, &$dependencies)
@ -152,6 +159,10 @@ class BoundMethod
$dependencies[] = $parameters[$parameter->name];
unset($parameters[$parameter->name]);
} elseif ($parameter->getClass() && array_key_exists($parameter->getClass()->name, $parameters)) {
$dependencies[] = $parameters[$parameter->getClass()->name];
unset($parameters[$parameter->getClass()->name]);
} elseif ($parameter->getClass()) {
$dependencies[] = $container->make($parameter->getClass()->name);
} elseif ($parameter->isDefaultValueAvailable()) {

View file

@ -3,10 +3,12 @@
namespace Illuminate\Container;
use Closure;
use Exception;
use ArrayAccess;
use LogicException;
use ReflectionClass;
use ReflectionParameter;
use Illuminate\Support\Arr;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Container\Container as ContainerContract;
@ -22,124 +24,130 @@ class Container implements ArrayAccess, ContainerContract
/**
* An array of the types that have been resolved.
*
* @var array
* @var bool[]
*/
protected $resolved = [];
/**
* The container's bindings.
*
* @var array
* @var array[]
*/
protected $bindings = [];
/**
* The container's method bindings.
*
* @var array
* @var \Closure[]
*/
protected $methodBindings = [];
/**
* The container's shared instances.
*
* @var array
* @var object[]
*/
protected $instances = [];
/**
* The registered type aliases.
*
* @var array
* @var string[]
*/
protected $aliases = [];
/**
* The registered aliases keyed by the abstract name.
*
* @var array
* @var array[]
*/
protected $abstractAliases = [];
/**
* The extension closures for services.
*
* @var array
* @var array[]
*/
protected $extenders = [];
/**
* All of the registered tags.
*
* @var array
* @var array[]
*/
protected $tags = [];
/**
* The stack of concretions currently being built.
*
* @var array
* @var array[]
*/
protected $buildStack = [];
/**
* The parameter override stack.
*
* @var array
* @var array[]
*/
protected $with = [];
/**
* The contextual binding map.
*
* @var array
* @var array[]
*/
public $contextual = [];
/**
* All of the registered rebound callbacks.
*
* @var array
* @var array[]
*/
protected $reboundCallbacks = [];
/**
* All of the global resolving callbacks.
*
* @var array
* @var \Closure[]
*/
protected $globalResolvingCallbacks = [];
/**
* All of the global after resolving callbacks.
*
* @var array
* @var \Closure[]
*/
protected $globalAfterResolvingCallbacks = [];
/**
* All of the resolving callbacks by class type.
*
* @var array
* @var array[]
*/
protected $resolvingCallbacks = [];
/**
* All of the after resolving callbacks by class type.
*
* @var array
* @var array[]
*/
protected $afterResolvingCallbacks = [];
/**
* Define a contextual binding.
*
* @param string $concrete
* @param array|string $concrete
* @return \Illuminate\Contracts\Container\ContextualBindingBuilder
*/
public function when($concrete)
{
return new ContextualBindingBuilder($this, $this->getAlias($concrete));
$aliases = [];
foreach (Arr::wrap($concrete) as $c) {
$aliases[] = $this->getAlias($c);
}
return new ContextualBindingBuilder($this, $aliases);
}
/**
@ -155,6 +163,14 @@ class Container implements ArrayAccess, ContainerContract
$this->isAlias($abstract);
}
/**
* {@inheritdoc}
*/
public function has($id)
{
return $this->bound($id);
}
/**
* Determine if the given abstract type has been resolved.
*
@ -180,7 +196,7 @@ class Container implements ArrayAccess, ContainerContract
public function isShared($abstract)
{
return isset($this->instances[$abstract]) ||
(isset($this->bindings[$abstract]['shared']) &&
(isset($this->bindings[$abstract]['shared']) &&
$this->bindings[$abstract]['shared'] === true);
}
@ -198,18 +214,18 @@ class Container implements ArrayAccess, ContainerContract
/**
* Register a binding with the container.
*
* @param string|array $abstract
* @param string $abstract
* @param \Closure|string|null $concrete
* @param bool $shared
* @return void
*/
public function bind($abstract, $concrete = null, $shared = false)
{
$this->dropStaleInstances($abstract);
// If no concrete type was given, we will simply set the concrete type to the
// abstract type. After that, the concrete type to be registered as shared
// without being forced to state their classes in both of the parameters.
$this->dropStaleInstances($abstract);
if (is_null($concrete)) {
$concrete = $abstract;
}
@ -245,7 +261,9 @@ class Container implements ArrayAccess, ContainerContract
return $container->build($concrete);
}
return $container->makeWith($concrete, $parameters);
return $container->resolve(
$concrete, $parameters, $raiseEvents = false
);
};
}
@ -263,13 +281,28 @@ class Container implements ArrayAccess, ContainerContract
/**
* Bind a callback to resolve with Container::call.
*
* @param string $method
* @param array|string $method
* @param \Closure $callback
* @return void
*/
public function bindMethod($method, $callback)
{
$this->methodBindings[$method] = $callback;
$this->methodBindings[$this->parseBindMethod($method)] = $callback;
}
/**
* Get the method to be bound in class@method format.
*
* @param array|string $method
* @return string
*/
protected function parseBindMethod($method)
{
if (is_array($method)) {
return $method[0].'@'.$method[1];
}
return $method;
}
/**
@ -315,7 +348,7 @@ class Container implements ArrayAccess, ContainerContract
/**
* Register a shared binding in the container.
*
* @param string|array $abstract
* @param string $abstract
* @param \Closure|string|null $concrete
* @return void
*/
@ -355,7 +388,7 @@ class Container implements ArrayAccess, ContainerContract
*
* @param string $abstract
* @param mixed $instance
* @return void
* @return mixed
*/
public function instance($abstract, $instance)
{
@ -373,6 +406,8 @@ class Container implements ArrayAccess, ContainerContract
if ($isBound) {
$this->rebound($abstract);
}
return $instance;
}
/**
@ -422,19 +457,19 @@ class Container implements ArrayAccess, ContainerContract
* Resolve all of the bindings for a given tag.
*
* @param string $tag
* @return array
* @return iterable
*/
public function tagged($tag)
{
$results = [];
if (isset($this->tags[$tag])) {
foreach ($this->tags[$tag] as $abstract) {
$results[] = $this->make($abstract);
}
if (! isset($this->tags[$tag])) {
return [];
}
return $results;
return new RewindableGenerator(function () use ($tag) {
foreach ($this->tags[$tag] as $abstract) {
yield $this->make($abstract);
}
}, count($this->tags[$tag]));
}
/**
@ -443,9 +478,15 @@ class Container implements ArrayAccess, ContainerContract
* @param string $abstract
* @param string $alias
* @return void
*
* @throws \LogicException
*/
public function alias($abstract, $alias)
{
if ($alias === $abstract) {
throw new LogicException("[{$abstract}] is aliased to itself.");
}
$this->aliases[$alias] = $abstract;
$this->abstractAliases[$abstract][] = $alias;
@ -505,11 +546,7 @@ class Container implements ArrayAccess, ContainerContract
*/
protected function getReboundCallbacks($abstract)
{
if (isset($this->reboundCallbacks[$abstract])) {
return $this->reboundCallbacks[$abstract];
}
return [];
return $this->reboundCallbacks[$abstract] ?? [];
}
/**
@ -553,26 +590,45 @@ class Container implements ArrayAccess, ContainerContract
}
/**
* Resolve the given type with the given parameter overrides.
* An alias function name for make().
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
public function makeWith($abstract, array $parameters)
public function makeWith($abstract, array $parameters = [])
{
return $this->make($abstract, $parameters);
}
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @param array $parameters
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @return mixed
* {@inheritdoc}
*/
public function make($abstract)
public function get($id)
{
return $this->resolve($abstract);
try {
return $this->resolve($id);
} catch (Exception $e) {
if ($this->has($id)) {
throw $e;
}
throw new EntryNotFoundException($id);
}
}
/**
@ -580,9 +636,12 @@ class Container implements ArrayAccess, ContainerContract
*
* @param string $abstract
* @param array $parameters
* @param bool $raiseEvents
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function resolve($abstract, $parameters = [])
protected function resolve($abstract, $parameters = [], $raiseEvents = true)
{
$abstract = $this->getAlias($abstract);
@ -624,7 +683,9 @@ class Container implements ArrayAccess, ContainerContract
$this->instances[$abstract] = $object;
}
$this->fireResolvingCallbacks($abstract, $object);
if ($raiseEvents) {
$this->fireResolvingCallbacks($abstract, $object);
}
// Before returning, we will also set the resolved flag to "true" and pop off
// the parameter overrides for this build. After those two things are done
@ -662,7 +723,7 @@ class Container implements ArrayAccess, ContainerContract
* Get the contextual concrete binding for the given abstract.
*
* @param string $abstract
* @return string|null
* @return \Closure|string|null
*/
protected function getContextualConcrete($abstract)
{
@ -688,13 +749,11 @@ class Container implements ArrayAccess, ContainerContract
* Find the concrete binding for the given abstract in the contextual binding array.
*
* @param string $abstract
* @return string|null
* @return \Closure|string|null
*/
protected function findInContextualBindings($abstract)
{
if (isset($this->contextual[end($this->buildStack)][$abstract])) {
return $this->contextual[end($this->buildStack)][$abstract];
}
return $this->contextual[end($this->buildStack)][$abstract] ?? null;
}
/**
@ -729,7 +788,7 @@ class Container implements ArrayAccess, ContainerContract
$reflector = new ReflectionClass($concrete);
// If the type is not instantiable, the developer is attempting to resolve
// an abstract type such as an Interface of Abstract Class and there is
// an abstract type such as an Interface or Abstract Class and there is
// no binding registered for the abstractions so we need to bail out.
if (! $reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
@ -753,9 +812,13 @@ class Container implements ArrayAccess, ContainerContract
// Once we have all the constructor's parameters we can create each of the
// dependency instances and then use the reflection instances to make a
// new instance of this class, injecting the created dependencies in.
$instances = $this->resolveDependencies(
$dependencies
);
try {
$instances = $this->resolveDependencies($dependencies);
} catch (BindingResolutionException $e) {
array_pop($this->buildStack);
throw $e;
}
array_pop($this->buildStack);
@ -767,6 +830,8 @@ class Container implements ArrayAccess, ContainerContract
*
* @param array $dependencies
* @return array
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function resolveDependencies(array $dependencies)
{
@ -785,7 +850,7 @@ class Container implements ArrayAccess, ContainerContract
// If the class is null, it means the dependency is a string or some other
// primitive type which we can not resolve since it is not a class and
// we will just bomb out with an error since we have no-where to go.
$results[] = is_null($class = $dependency->getClass())
$results[] = is_null($dependency->getClass())
? $this->resolvePrimitive($dependency)
: $this->resolveClass($dependency);
}
@ -794,7 +859,7 @@ class Container implements ArrayAccess, ContainerContract
}
/**
* Determine if the given dependency has a parameter override from makeWith.
* Determine if the given dependency has a parameter override.
*
* @param \ReflectionParameter $dependency
* @return bool
@ -913,7 +978,7 @@ class Container implements ArrayAccess, ContainerContract
/**
* Register a new resolving callback.
*
* @param string $abstract
* @param \Closure|string $abstract
* @param \Closure|null $callback
* @return void
*/
@ -933,8 +998,8 @@ class Container implements ArrayAccess, ContainerContract
/**
* Register a new after resolving callback for all types.
*
* @param string $abstract
* @param \Closure|null $callback
* @param \Closure|string $abstract
* @param \Closure|null $callback
* @return void
*/
public function afterResolving($abstract, Closure $callback = null)
@ -1035,8 +1100,6 @@ class Container implements ArrayAccess, ContainerContract
*
* @param string $abstract
* @return string
*
* @throws \LogicException
*/
public function getAlias($abstract)
{
@ -1044,10 +1107,6 @@ class Container implements ArrayAccess, ContainerContract
return $abstract;
}
if ($this->aliases[$abstract] === $abstract) {
throw new LogicException("[{$abstract}] is aliased to itself.");
}
return $this->getAlias($this->aliases[$abstract]);
}
@ -1061,11 +1120,7 @@ class Container implements ArrayAccess, ContainerContract
{
$abstract = $this->getAlias($abstract);
if (isset($this->extenders[$abstract])) {
return $this->extenders[$abstract];
}
return [];
return $this->extenders[$abstract] ?? [];
}
/**
@ -1126,7 +1181,7 @@ class Container implements ArrayAccess, ContainerContract
}
/**
* Set the globally available instance of the container.
* Get the globally available instance of the container.
*
* @return static
*/
@ -1143,7 +1198,7 @@ class Container implements ArrayAccess, ContainerContract
* Set the shared instance of the container.
*
* @param \Illuminate\Contracts\Container\Container|null $container
* @return static
* @return \Illuminate\Contracts\Container\Container|static
*/
public static function setInstance(ContainerContract $container = null)
{

View file

@ -2,6 +2,8 @@
namespace Illuminate\Container;
use Illuminate\Support\Arr;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Container\ContextualBindingBuilder as ContextualBindingBuilderContract;
class ContextualBindingBuilder implements ContextualBindingBuilderContract
@ -9,14 +11,14 @@ class ContextualBindingBuilder implements ContextualBindingBuilderContract
/**
* The underlying container instance.
*
* @var \Illuminate\Container\Container
* @var \Illuminate\Contracts\Container\Container
*/
protected $container;
/**
* The concrete instance.
*
* @var string
* @var string|array
*/
protected $concrete;
@ -30,8 +32,8 @@ class ContextualBindingBuilder implements ContextualBindingBuilderContract
/**
* Create a new contextual binding builder.
*
* @param \Illuminate\Container\Container $container
* @param string $concrete
* @param \Illuminate\Contracts\Container\Container $container
* @param string|array $concrete
* @return void
*/
public function __construct(Container $container, $concrete)
@ -61,8 +63,8 @@ class ContextualBindingBuilder implements ContextualBindingBuilderContract
*/
public function give($implementation)
{
$this->container->addContextualBinding(
$this->concrete, $this->needs, $implementation
);
foreach (Arr::wrap($this->concrete) as $concrete) {
$this->container->addContextualBinding($concrete, $this->needs, $implementation);
}
}
}

View file

@ -14,8 +14,10 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/contracts": "5.4.*"
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*",
"psr/container": "^1.0"
},
"autoload": {
"psr-4": {
@ -24,7 +26,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "5.8-dev"
}
},
"config": {

View file

@ -65,13 +65,22 @@ interface Gate
public function denies($ability, $arguments = []);
/**
* Determine if the given ability should be granted.
* Determine if all of the given abilities should be granted for the current user.
*
* @param string $ability
* @param iterable|string $abilities
* @param array|mixed $arguments
* @return bool
*/
public function check($ability, $arguments = []);
public function check($abilities, $arguments = []);
/**
* Determine if any one of the given abilities should be granted for the current user.
*
* @param iterable|string $abilities
* @param array|mixed $arguments
* @return bool
*/
public function any($abilities, $arguments = []);
/**
* Determine if the given ability should be granted for the current user.
@ -84,6 +93,15 @@ interface Gate
*/
public function authorize($ability, $arguments = []);
/**
* Get the raw result from the authorization callback.
*
* @param string $ability
* @param array|mixed $arguments
* @return mixed
*/
public function raw($ability, $arguments = []);
/**
* Get a policy instance for a given class.
*
@ -101,4 +119,11 @@ interface Gate
* @return static
*/
public function forUser($user);
/**
* Get all of the defined abilities.
*
* @return array
*/
public function abilities();
}

View file

@ -28,7 +28,7 @@ interface Guard
/**
* Get the ID for the currently authenticated user.
*
* @return int|null
* @return int|string|null
*/
public function id();

View file

@ -15,7 +15,7 @@ interface UserProvider
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* @param mixed $identifier
* @param mixed $identifier
* @param string $token
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/

View file

@ -7,7 +7,7 @@ interface Factory
/**
* Get a broadcaster implementation by name.
*
* @param string $name
* @param string|null $name
* @return void
*/
public function connection($name = null);

View file

@ -7,7 +7,7 @@ interface ShouldBroadcast
/**
* Get the channels the event should broadcast on.
*
* @return array
* @return \Illuminate\Broadcasting\Channel|\Illuminate\Broadcasting\Channel[]
*/
public function broadcastOn();
}

View file

@ -21,6 +21,22 @@ interface Dispatcher
*/
public function dispatchNow($command, $handler = null);
/**
* Determine if the given command has a handler.
*
* @param mixed $command
* @return bool
*/
public function hasCommandHandler($command);
/**
* Retrieve the handler for a command.
*
* @param mixed $command
* @return bool|mixed
*/
public function getCommandHandler($command);
/**
* Set the pipes commands should be piped through before dispatching.
*
@ -28,4 +44,12 @@ interface Dispatcher
* @return $this
*/
public function pipeThrough(array $pipes);
/**
* Map a command to a handler.
*
* @param array $map
* @return $this
*/
public function map(array $map);
}

View file

@ -8,7 +8,7 @@ interface Factory
* Get a cache store instance by name.
*
* @param string|null $name
* @return mixed
* @return \Illuminate\Contracts\Cache\Repository
*/
public function store($name = null);
}

View file

@ -3,31 +3,15 @@
namespace Illuminate\Contracts\Cache;
use Closure;
use Psr\SimpleCache\CacheInterface;
interface Repository
interface Repository extends CacheInterface
{
/**
* Determine if an item exists in the cache.
*
* @param string $key
* @return bool
*/
public function has($key);
/**
* Retrieve an item from the cache by key.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null);
/**
* Retrieve an item from the cache and delete it.
*
* @param string $key
* @param mixed $default
* @param mixed $default
* @return mixed
*/
public function pull($key, $default = null);
@ -36,21 +20,21 @@ interface Repository
* Store an item in the cache.
*
* @param string $key
* @param mixed $value
* @param \DateTime|float|int $minutes
* @return void
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function put($key, $value, $minutes);
public function put($key, $value, $ttl = null);
/**
* Store an item in the cache if the key does not exist.
*
* @param string $key
* @param mixed $value
* @param \DateTime|float|int $minutes
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function add($key, $value, $minutes);
public function add($key, $value, $ttl = null);
/**
* Increment the value of an item in the cache.
@ -74,34 +58,34 @@ interface Repository
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @return void
* @param mixed $value
* @return bool
*/
public function forever($key, $value);
/**
* Get an item from the cache, or store the default value.
* Get an item from the cache, or execute the given Closure and store the result.
*
* @param string $key
* @param \DateTime|float|int $minutes
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @param \Closure $callback
* @return mixed
*/
public function remember($key, $minutes, Closure $callback);
public function remember($key, $ttl, Closure $callback);
/**
* Get an item from the cache, or store the default value forever.
* Get an item from the cache, or execute the given Closure and store the result forever.
*
* @param string $key
* @param string $key
* @param \Closure $callback
* @return mixed
*/
public function sear($key, Closure $callback);
/**
* Get an item from the cache, or store the default value forever.
* Get an item from the cache, or execute the given Closure and store the result forever.
*
* @param string $key
* @param string $key
* @param \Closure $callback
* @return mixed
*/
@ -114,4 +98,11 @@ interface Repository
* @return bool
*/
public function forget($key);
/**
* Get the cache store implementation.
*
* @return \Illuminate\Contracts\Cache\Store
*/
public function getStore();
}

Some files were not shown because too many files have changed in this diff Show more