Laravel version update

Laravel version update
This commit is contained in:
Manish Verma
2018-08-06 18:48:58 +05:30
parent d143048413
commit 126fbb0255
13678 changed files with 1031482 additions and 778530 deletions

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) <Taylor Otwell>
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -3,7 +3,7 @@
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"homepage": "http://laravel.com",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
@@ -11,33 +11,32 @@
"authors": [
{
"name": "Taylor Otwell",
"email": "taylorotwell@gmail.com"
"email": "taylor@laravel.com"
}
],
"require": {
"php": ">=5.5.9",
"php": ">=7.0",
"ext-mbstring": "*",
"ext-openssl": "*",
"classpreloader/classpreloader": "~3.0",
"doctrine/inflector": "~1.0",
"jeremeamia/superclosure": "~2.2",
"league/flysystem": "~1.0",
"monolog/monolog": "~1.11",
"doctrine/inflector": "~1.1",
"erusev/parsedown": "~1.7",
"league/flysystem": "^1.0.8",
"monolog/monolog": "~1.12",
"mtdowling/cron-expression": "~1.0",
"nesbot/carbon": "~1.20",
"paragonie/random_compat": "~1.4",
"psy/psysh": "0.7.*",
"swiftmailer/swiftmailer": "~5.1",
"symfony/console": "2.8.*|3.0.*",
"symfony/debug": "2.8.*|3.0.*",
"symfony/finder": "2.8.*|3.0.*",
"symfony/http-foundation": "2.8.*|3.0.*",
"symfony/http-kernel": "2.8.*|3.0.*",
"symfony/polyfill-php56": "~1.0",
"symfony/process": "2.8.*|3.0.*",
"symfony/routing": "2.8.*|3.0.*",
"symfony/translation": "2.8.*|3.0.*",
"symfony/var-dumper": "2.8.*|3.0.*",
"nesbot/carbon": "^1.24.1",
"psr/container": "~1.0",
"psr/simple-cache": "^1.0",
"ramsey/uuid": "~3.0",
"swiftmailer/swiftmailer": "~6.0",
"symfony/console": "~3.3",
"symfony/debug": "~3.3",
"symfony/finder": "~3.3",
"symfony/http-foundation": "~3.3",
"symfony/http-kernel": "~3.3",
"symfony/process": "~3.3",
"symfony/routing": "~3.3",
"symfony/var-dumper": "~3.3",
"tijsverkoyen/css-to-inline-styles": "~2.2",
"vlucas/phpdotenv": "~2.2"
},
"replace": {
@@ -53,12 +52,12 @@
"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",
"illuminate/log": "self.version",
"illuminate/mail": "self.version",
"illuminate/notifications": "self.version",
"illuminate/pagination": "self.version",
"illuminate/pipeline": "self.version",
"illuminate/queue": "self.version",
@@ -69,21 +68,21 @@
"illuminate/translation": "self.version",
"illuminate/validation": "self.version",
"illuminate/view": "self.version",
"tightenco/collect": "self.version"
"tightenco/collect": "<5.5.33"
},
"require-dev": {
"aws/aws-sdk-php": "~3.0",
"mockery/mockery": "~0.9.4",
"doctrine/dbal": "~2.5",
"filp/whoops": "^2.1.4",
"mockery/mockery": "~1.0",
"orchestra/testbench-core": "3.5.*",
"pda/pheanstalk": "~3.0",
"phpunit/phpunit": "~4.1",
"predis/predis": "~1.0",
"symfony/css-selector": "2.8.*|3.0.*",
"symfony/dom-crawler": "2.8.*|3.0.*"
"phpunit/phpunit": "~6.0",
"predis/predis": "^1.1.1",
"symfony/css-selector": "~3.3",
"symfony/dom-crawler": "~3.3"
},
"autoload": {
"classmap": [
"src/Illuminate/Queue/IlluminateQueueClosure.php"
],
"files": [
"src/Illuminate/Foundation/helpers.php",
"src/Illuminate/Support/helpers.php"
@@ -92,24 +91,41 @@
"Illuminate\\": "src/Illuminate/"
}
},
"autoload-dev": {
"files": [
"tests/Database/stubs/MigrationCreatorFakeMigration.php"
],
"psr-4": {
"Illuminate\\Tests\\": "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "5.2-dev"
"dev-master": "5.5-dev"
}
},
"suggest": {
"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.4).",
"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 (~5.3|~6.0).",
"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).",
"league/flysystem-cached-adapter": "Required to use Flysystem caching (~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 (2.8.*|3.0.*).",
"symfony/dom-crawler": "Required to use most of the crawler integration testing tools (2.8.*|3.0.*).",
"symfony/psr-http-message-bridge": "Required to use psr7 bridging features (0.2.*)."
"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 (~3.3).",
"symfony/dom-crawler": "Required to use most of the crawler integration testing tools (~3.3).",
"symfony/psr-http-message-bridge": "Required to psr7 bridging features (~1.0)."
},
"minimum-stability": "dev"
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -1,31 +1,44 @@
# Laravel Framework (Kernel)
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://laravel.com/assets/img/components/logo-laravel.svg"></a></p>
[![StyleCI](https://styleci.io/repos/7548986/shield?style=flat)](https://styleci.io/repos/7548986)
[![Build Status](https://travis-ci.org/laravel/framework.svg)](https://travis-ci.org/laravel/framework)
[![Total Downloads](https://poser.pugx.org/laravel/framework/d/total.svg)](https://packagist.org/packages/laravel/framework)
[![Latest Stable Version](https://poser.pugx.org/laravel/framework/v/stable.svg)](https://packagist.org/packages/laravel/framework)
[![Latest Unstable Version](https://poser.pugx.org/laravel/framework/v/unstable.svg)](https://packagist.org/packages/laravel/framework)
[![License](https://poser.pugx.org/laravel/framework/license.svg)](https://packagist.org/packages/laravel/framework)
<p align="center">
<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/d/total.svg" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/v/stable.svg" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/license.svg" alt="License"></a>
</p>
## About Laravel
> **Note:** This repository contains the core code of the Laravel framework. If you want to build an application using Laravel 5, visit the main [Laravel repository](https://github.com/laravel/laravel).
## Laravel PHP Framework
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as:
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as authentication, routing, sessions, queueing, and caching.
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, yet powerful, providing powerful tools needed for large, robust applications. A superb inversion of control container, expressive migration system, and tightly integrated unit testing support give you the 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
## Official Documentation
## Learning Laravel
Documentation for the framework can be found on the [Laravel website](http://laravel.com/docs).
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 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).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](CODE_OF_CONDUCT.md).
## 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.
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## License

View File

@@ -2,6 +2,7 @@
namespace Illuminate\Auth\Access;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Illuminate\Contracts\Container\Container;
@@ -64,7 +65,8 @@ class Gate implements GateContract
* @param array $afterCallbacks
* @return void
*/
public function __construct(Container $container, callable $userResolver, array $abilities = [], array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = [])
public function __construct(Container $container, callable $userResolver, array $abilities = [],
array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = [])
{
$this->policies = $policies;
$this->container = $container;
@@ -77,12 +79,20 @@ class Gate implements GateContract
/**
* 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,7 +109,7 @@ 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);
$this->abilities[$ability] = $this->buildAbilityCallback($ability, $callback);
} else {
throw new InvalidArgumentException("Callback must be a callable or a 'Class@method' string.");
}
@@ -107,18 +117,57 @@ class Gate implements GateContract
return $this;
}
/**
* Define abilities for a resource.
*
* @param string $name
* @param string $class
* @param array $abilities
* @return $this
*/
public function resource($name, $class, array $abilities = null)
{
$abilities = $abilities ?: [
'view' => 'view',
'create' => 'create',
'update' => 'update',
'delete' => 'delete',
];
foreach ($abilities as $ability => $method) {
$this->define($name.'.'.$ability, $class.'@'.$method);
}
return $this;
}
/**
* 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) = explode('@', $callback);
return function () use ($ability, $callback) {
list($class, $method) = Str::parseCallback($callback);
return call_user_func_array([$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 $policy->{$method}(...func_get_args());
};
}
@@ -187,21 +236,35 @@ 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 {
$result = $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;
}
});
}
return (bool) $result;
/**
* 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);
});
}
/**
@@ -225,7 +288,7 @@ class Gate implements GateContract
}
/**
* Get the raw result for the given ability for the current user.
* Get the raw result from the authorization callback.
*
* @param string $ability
* @param array|mixed $arguments
@@ -237,12 +300,22 @@ class Gate implements GateContract
return false;
}
$arguments = is_array($arguments) ? $arguments : [$arguments];
$arguments = Arr::wrap($arguments);
if (is_null($result = $this->callBeforeCallbacks($user, $ability, $arguments))) {
// 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
// to let the developers override all checks for some authorization cases.
$result = $this->callBeforeCallbacks(
$user, $ability, $arguments
);
if (is_null($result)) {
$result = $this->callAuthCallback($user, $ability, $arguments);
}
// 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(
$user, $ability, $arguments, $result
);
@@ -260,13 +333,9 @@ class Gate implements GateContract
*/
protected function callAuthCallback($user, $ability, array $arguments)
{
$callback = $this->resolveAuthCallback(
$user, $ability, $arguments
);
$callback = $this->resolveAuthCallback($user, $ability, $arguments);
return call_user_func_array(
$callback, array_merge([$user], $arguments)
);
return $callback($user, ...$arguments);
}
/**
@@ -282,7 +351,7 @@ class Gate implements GateContract
$arguments = array_merge([$user, $ability], [$arguments]);
foreach ($this->beforeCallbacks as $before) {
if (! is_null($result = call_user_func_array($before, $arguments))) {
if (! is_null($result = $before(...$arguments))) {
return $result;
}
}
@@ -302,7 +371,7 @@ class Gate implements GateContract
$arguments = array_merge([$user, $ability, $result], [$arguments]);
foreach ($this->afterCallbacks as $after) {
call_user_func_array($after, $arguments);
$after(...$arguments);
}
}
@@ -316,78 +385,18 @@ class Gate implements GateContract
*/
protected function resolveAuthCallback($user, $ability, array $arguments)
{
if ($this->firstArgumentCorrespondsToPolicy($arguments)) {
return $this->resolvePolicyCallback($user, $ability, $arguments);
} elseif (isset($this->abilities[$ability])) {
if (isset($arguments[0]) &&
! is_null($policy = $this->getPolicyFor($arguments[0])) &&
$callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) {
return $callback;
}
if (isset($this->abilities[$ability])) {
return $this->abilities[$ability];
} else {
return function () {
return false;
};
}
}
/**
* Determine if the first argument in the array corresponds to a policy.
*
* @param array $arguments
* @return bool
*/
protected function firstArgumentCorrespondsToPolicy(array $arguments)
{
if (! isset($arguments[0])) {
return function () {
return false;
}
if (is_object($arguments[0])) {
return isset($this->policies[get_class($arguments[0])]);
}
return is_string($arguments[0]) && isset($this->policies[$arguments[0]]);
}
/**
* Resolve the callback for a policy check.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param string $ability
* @param array $arguments
* @return callable
*/
protected function resolvePolicyCallback($user, $ability, array $arguments)
{
return function () use ($user, $ability, $arguments) {
$instance = $this->getPolicyFor($arguments[0]);
if (method_exists($instance, 'before')) {
// We will prepend the user and ability onto the arguments so that the before
// callback can determine which ability is being called. Then we will call
// into the policy before methods with the arguments and get the result.
$beforeArguments = array_merge([$user, $ability], $arguments);
$result = call_user_func_array(
[$instance, 'before'], $beforeArguments
);
// If we received a non-null result from the before method, we will return it
// as the result of a check. This allows developers to override the checks
// in the policy and return a result for all rules defined in the class.
if (! is_null($result)) {
return $result;
}
}
if (strpos($ability, '-') !== false) {
$ability = Str::camel($ability);
}
if (! is_callable([$instance, $ability])) {
return false;
}
return call_user_func_array(
[$instance, $ability], array_merge([$user], $arguments)
);
};
}
@@ -396,8 +405,6 @@ class Gate implements GateContract
*
* @param object|string $class
* @return mixed
*
* @throws \InvalidArgumentException
*/
public function getPolicyFor($class)
{
@@ -405,11 +412,19 @@ class Gate implements GateContract
$class = get_class($class);
}
if (! isset($this->policies[$class])) {
throw new InvalidArgumentException("Policy not defined for [{$class}].");
if (! is_string($class)) {
return;
}
return $this->resolvePolicy($this->policies[$class]);
if (isset($this->policies[$class])) {
return $this->resolvePolicy($this->policies[$class]);
}
foreach ($this->policies as $expected => $policy) {
if (is_subclass_of($class, $expected)) {
return $this->resolvePolicy($policy);
}
}
}
/**
@@ -423,6 +438,78 @@ class Gate implements GateContract
return $this->container->make($class);
}
/**
* Resolve the callback for a policy check.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param string $ability
* @param array $arguments
* @param mixed $policy
* @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
// mapped to policy objects in the user's configurations or on this class.
$result = $this->callPolicyBefore(
$policy, $user, $ability, $arguments
);
// When we receive a non-null result from this before method, we will return it
// as the "final" results. This will allow developers to override the checks
// in this policy to return the result for all rules defined in the class.
if (! is_null($result)) {
return $result;
}
$ability = $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;
};
}
/**
* Call the "before" method on the given policy, if applicable.
*
* @param mixed $policy
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param string $ability
* @param array $arguments
* @return mixed
*/
protected function callPolicyBefore($policy, $user, $ability, $arguments)
{
if (method_exists($policy, 'before')) {
return $policy->before($user, $ability, ...$arguments);
}
}
/**
* Format the policy ability into a method name.
*
* @param string $ability
* @return string
*/
protected function formatAbilityToMethod($ability)
{
return strpos($ability, '-') !== false ? Str::camel($ability) : $ability;
}
/**
* Get a gate instance for the given user.
*
@@ -450,4 +537,24 @@ class Gate implements GateContract
{
return call_user_func($this->userResolver);
}
/**
* Get all of the defined abilities.
*
* @return array
*/
public function abilities()
{
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)
{

View File

@@ -65,9 +65,7 @@ class AuthManager implements FactoryContract
{
$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);
}
/**
@@ -88,15 +86,15 @@ class AuthManager implements FactoryContract
if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($name, $config);
} else {
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
if (method_exists($this, $driverMethod)) {
return $this->{$driverMethod}($name, $config);
} else {
throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
}
}
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
if (method_exists($this, $driverMethod)) {
return $this->{$driverMethod}($name, $config);
}
throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
}
/**
@@ -120,7 +118,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,7 +153,7 @@ 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->createUserProvider($config['provider'] ?? null),
$this->app['request']
);
@@ -193,6 +191,8 @@ class AuthManager implements FactoryContract
*/
public function shouldUse($name)
{
$name = $name ?: $this->getDefaultDriver();
$this->setDefaultDriver($name);
$this->userResolver = function ($name = null) {
@@ -221,7 +221,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');
@@ -289,6 +289,6 @@ class AuthManager implements FactoryContract
*/
public function __call($method, $parameters)
{
return call_user_func_array([$this->guard(), $method], $parameters);
return $this->guard()->{$method}(...$parameters);
}
}

View File

@@ -4,6 +4,13 @@ namespace Illuminate\Auth;
trait Authenticatable
{
/**
* The column name of the "remember me" token.
*
* @var string
*/
protected $rememberTokenName = 'remember_token';
/**
* Get the name of the unique identifier for the user.
*
@@ -21,7 +28,7 @@ trait Authenticatable
*/
public function getAuthIdentifier()
{
return $this->getKey();
return $this->{$this->getAuthIdentifierName()};
}
/**
@@ -37,11 +44,13 @@ trait Authenticatable
/**
* Get the token value for the "remember me" session.
*
* @return string
* @return string|null
*/
public function getRememberToken()
{
return $this->{$this->getRememberTokenName()};
if (! empty($this->getRememberTokenName())) {
return (string) $this->{$this->getRememberTokenName()};
}
}
/**
@@ -52,7 +61,9 @@ trait Authenticatable
*/
public function setRememberToken($value)
{
$this->{$this->getRememberTokenName()} = $value;
if (! empty($this->getRememberTokenName())) {
$this->{$this->getRememberTokenName()} = $value;
}
}
/**
@@ -62,6 +73,6 @@ trait Authenticatable
*/
public function getRememberTokenName()
{
return 'remember_token';
return $this->rememberTokenName;
}
}

View File

@@ -7,31 +7,33 @@ use Exception;
class AuthenticationException extends Exception
{
/**
* The guard instance.
* All of the guards that were checked.
*
* @var \Illuminate\Contracts\Auth\Guard
* @var array
*/
protected $guard;
protected $guards;
/**
* Create a new authentication exception.
*
* @param \Illuminate\Contracts\Auth\Guard|null $guard
* @param string $message
* @param array $guards
* @return void
*/
public function __construct($guard = null)
public function __construct($message = 'Unauthenticated.', array $guards = [])
{
$this->guard = $guard;
parent::__construct($message);
parent::__construct('Unauthenticated.');
$this->guards = $guards;
}
/**
* Get the guard instance.
* Get the guards that were checked.
*
* @return \Illuminate\Contracts\Auth\Guard|null
* @return array
*/
public function guard()
public function guards()
{
return $this->guard;
return $this->guards;
}
}

View File

@@ -3,18 +3,20 @@
namespace Illuminate\Auth\Console;
use Illuminate\Console\Command;
use Illuminate\Console\AppNamespaceDetectorTrait;
use Illuminate\Console\DetectsApplicationNamespace;
class MakeAuthCommand extends Command
class AuthMakeCommand extends Command
{
use AppNamespaceDetectorTrait;
use DetectsApplicationNamespace;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'make:auth {--views : Only scaffold the authentication views}';
protected $signature = 'make:auth
{--views : Only scaffold the authentication views}
{--force : Overwrite existing views by default}';
/**
* The console command description.
@@ -33,10 +35,8 @@ class MakeAuthCommand extends Command
'auth/register.stub' => 'auth/register.blade.php',
'auth/passwords/email.stub' => 'auth/passwords/email.blade.php',
'auth/passwords/reset.stub' => 'auth/passwords/reset.blade.php',
'auth/emails/password.stub' => 'auth/emails/password.blade.php',
'layouts/app.stub' => 'layouts/app.blade.php',
'home.stub' => 'home.blade.php',
'welcome.stub' => 'welcome.blade.php',
];
/**
@@ -44,30 +44,26 @@ class MakeAuthCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
$this->createDirectories();
$this->exportViews();
if (! $this->option('views')) {
$this->info('Installed HomeController.');
file_put_contents(
app_path('Http/Controllers/HomeController.php'),
$this->compileControllerStub()
);
$this->info('Updated Routes File.');
file_put_contents(
app_path('Http/routes.php'),
base_path('routes/web.php'),
file_get_contents(__DIR__.'/stubs/make/routes.stub'),
FILE_APPEND
);
}
$this->comment('Authentication scaffolding generated successfully!');
$this->info('Authentication scaffolding generated successfully.');
}
/**
@@ -77,16 +73,12 @@ class MakeAuthCommand extends Command
*/
protected function createDirectories()
{
if (! is_dir(base_path('resources/views/layouts'))) {
mkdir(base_path('resources/views/layouts'), 0755, true);
if (! is_dir($directory = resource_path('views/layouts'))) {
mkdir($directory, 0755, true);
}
if (! is_dir(base_path('resources/views/auth/passwords'))) {
mkdir(base_path('resources/views/auth/passwords'), 0755, true);
}
if (! is_dir(base_path('resources/views/auth/emails'))) {
mkdir(base_path('resources/views/auth/emails'), 0755, true);
if (! is_dir($directory = resource_path('views/auth/passwords'))) {
mkdir($directory, 0755, true);
}
}
@@ -98,11 +90,16 @@ class MakeAuthCommand extends Command
protected function exportViews()
{
foreach ($this->views as $key => $value) {
$path = base_path('resources/views/'.$value);
if (file_exists($view = resource_path('views/'.$value)) && ! $this->option('force')) {
if (! $this->confirm("The [{$value}] view already exists. Do you want to replace it?")) {
continue;
}
}
$this->line('<info>Created View:</info> '.$path);
copy(__DIR__.'/stubs/make/views/'.$key, $path);
copy(
__DIR__.'/stubs/make/views/'.$key,
$view
);
}
}

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

@@ -2,7 +2,6 @@
namespace {{namespace}}Http\Controllers;
use {{namespace}}Http\Requests;
use Illuminate\Http\Request;
class HomeController extends Controller

View File

@@ -1,4 +1,4 @@
Route::auth();
Auth::routes();
Route::get('/home', 'HomeController@index');
Route::get('/home', 'HomeController@index')->name('home');

View File

@@ -1 +0,0 @@
Click here to reset your password: <a href="{{ $link = url('password/reset', $token).'?email='.urlencode($user->getEmailForPasswordReset()) }}"> {{ $link }} </a>

View File

@@ -6,15 +6,16 @@
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Login</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/login') }}">
<form class="form-horizontal" method="POST" action="{{ route('login') }}">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required autofocus>
@if ($errors->has('email'))
<span class="help-block">
@@ -28,7 +29,7 @@
<label for="password" class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password">
<input id="password" type="password" class="form-control" name="password" required>
@if ($errors->has('password'))
<span class="help-block">
@@ -42,19 +43,21 @@
<div class="col-md-6 col-md-offset-4">
<div class="checkbox">
<label>
<input type="checkbox" name="remember"> Remember Me
<input type="checkbox" name="remember" {{ old('remember') ? 'checked' : '' }}> Remember Me
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="col-md-8 col-md-offset-4">
<button type="submit" class="btn btn-primary">
<i class="fa fa-btn fa-sign-in"></i> Login
Login
</button>
<a class="btn btn-link" href="{{ url('/password/reset') }}">Forgot Your Password?</a>
<a class="btn btn-link" href="{{ route('password.request') }}">
Forgot Your Password?
</a>
</div>
</div>
</form>

View File

@@ -1,12 +1,12 @@
@extends('layouts.app')
<!-- Main Content -->
@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="panel-body">
@if (session('status'))
<div class="alert alert-success">
@@ -14,14 +14,14 @@
</div>
@endif
<form class="form-horizontal" role="form" method="POST" action="{{ url('/password/email') }}">
<form class="form-horizontal" method="POST" action="{{ route('password.email') }}">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required>
@if ($errors->has('email'))
<span class="help-block">
@@ -34,7 +34,7 @@
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
<i class="fa fa-btn fa-envelope"></i> Send Password Reset Link
Send Password Reset Link
</button>
</div>
</div>

View File

@@ -8,7 +8,7 @@
<div class="panel-heading">Reset Password</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/password/reset') }}">
<form class="form-horizontal" method="POST" action="{{ route('password.request') }}">
{{ csrf_field() }}
<input type="hidden" name="token" value="{{ $token }}">
@@ -17,7 +17,7 @@
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control" name="email" value="{{ $email or old('email') }}">
<input id="email" type="email" class="form-control" name="email" value="{{ $email or old('email') }}" required autofocus>
@if ($errors->has('email'))
<span class="help-block">
@@ -31,7 +31,7 @@
<label for="password" class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password">
<input id="password" type="password" class="form-control" name="password" required>
@if ($errors->has('password'))
<span class="help-block">
@@ -44,7 +44,7 @@
<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">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
@if ($errors->has('password_confirmation'))
<span class="help-block">
@@ -57,7 +57,7 @@
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
<i class="fa fa-btn fa-refresh"></i> Reset Password
Reset Password
</button>
</div>
</div>

View File

@@ -6,15 +6,16 @@
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Register</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/register') }}">
<form class="form-horizontal" method="POST" action="{{ route('register') }}">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
<label for="name" class="col-md-4 control-label">Name</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control" name="name" value="{{ old('name') }}">
<input id="name" type="text" class="form-control" name="name" value="{{ old('name') }}" required autofocus>
@if ($errors->has('name'))
<span class="help-block">
@@ -28,7 +29,7 @@
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}">
<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required>
@if ($errors->has('email'))
<span class="help-block">
@@ -42,7 +43,7 @@
<label for="password" class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password">
<input id="password" type="password" class="form-control" name="password" required>
@if ($errors->has('password'))
<span class="help-block">
@@ -52,24 +53,18 @@
</div>
</div>
<div class="form-group{{ $errors->has('password_confirmation') ? ' has-error' : '' }}">
<div class="form-group">
<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">
@if ($errors->has('password_confirmation'))
<span class="help-block">
<strong>{{ $errors->first('password_confirmation') }}</strong>
</span>
@endif
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
<i class="fa fa-btn fa-user"></i> Register
Register
</button>
</div>
</div>

View File

@@ -3,11 +3,17 @@
@section('content')
<div class="container">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Dashboard</div>
<div class="panel-body">
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
You are logged in!
</div>
</div>

View File

@@ -1,82 +1,80 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ 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">
<title>Laravel</title>
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<!-- Fonts -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css" integrity="sha384-XdYbMnZ/QjLh6iI4ogqCTaIjrFk87ip+ekIjefZch0Y+PvJ8CDYtEs1ipDmPorQ+" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
{{-- <link href="{{ elixir('css/app.css') }}" rel="stylesheet"> --}}
<style>
body {
font-family: 'Lato';
}
.fa-btn {
margin-right: 6px;
}
</style>
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body id="app-layout">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<body>
<div id="app">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<!-- 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>
<!-- Collapsed Hamburger -->
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse" aria-expanded="false">
<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('/') }}">
Laravel
</a>
<!-- Branding Image -->
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
</div>
<div class="collapse navbar-collapse" id="app-navbar-collapse">
<!-- Left Side Of Navbar -->
<ul class="nav navbar-nav">
&nbsp;
</ul>
<!-- Right Side Of Navbar -->
<ul class="nav navbar-nav navbar-right">
<!-- Authentication Links -->
@guest
<li><a href="{{ route('login') }}">Login</a></li>
<li><a href="{{ route('register') }}">Register</a></li>
@else
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false" aria-haspopup="true" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>
<a href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
Logout
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
</li>
</ul>
</li>
@endguest
</ul>
</div>
</div>
</nav>
<div class="collapse navbar-collapse" id="app-navbar-collapse">
<!-- Left Side Of Navbar -->
<ul class="nav navbar-nav">
<li><a href="{{ url('/home') }}">Home</a></li>
</ul>
@yield('content')
</div>
<!-- Right Side Of Navbar -->
<ul class="nav navbar-nav navbar-right">
<!-- Authentication Links -->
@if (Auth::guest())
<li><a href="{{ url('/login') }}">Login</a></li>
<li><a href="{{ url('/register') }}">Register</a></li>
@else
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="{{ url('/logout') }}"><i class="fa fa-btn fa-sign-out"></i>Logout</a></li>
</ul>
</li>
@endif
</ul>
</div>
</div>
</nav>
@yield('content')
<!-- JavaScripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js" integrity="sha384-I6F5OKECLVtK/BL+8iSLDEHowSAfUo76ZL9+kGAgTRdiByINKJaqTPH/QVNS1VDb" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
{{-- <script src="{{ elixir('js/app.js') }}"></script> --}}
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>

View File

@@ -1,17 +0,0 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-heading">Welcome</div>
<div class="panel-body">
Your Application's Landing Page.
</div>
</div>
</div>
</div>
</div>
@endsection

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

@@ -68,12 +68,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 +86,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,6 +98,12 @@ 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.
@@ -125,7 +131,7 @@ class DatabaseUserProvider implements UserProvider
*/
protected function getGenericUser($user)
{
if ($user !== null) {
if (! is_null($user)) {
return new GenericUser((array) $user);
}
}
@@ -139,8 +145,8 @@ class DatabaseUserProvider implements UserProvider
*/
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password'];
return $this->hasher->check($plain, $user->getAuthPassword());
return $this->hasher->check(
$credentials['password'], $user->getAuthPassword()
);
}
}

View File

@@ -44,7 +44,11 @@ class EloquentUserProvider implements UserProvider
*/
public function retrieveById($identifier)
{
return $this->createModel()->newQuery()->find($identifier);
$model = $this->createModel();
return $model->newQuery()
->where($model->getAuthIdentifierName(), $identifier)
->first();
}
/**
@@ -58,10 +62,15 @@ class EloquentUserProvider implements UserProvider
{
$model = $this->createModel();
return $model->newQuery()
->where($model->getAuthIdentifierName(), $identifier)
->where($model->getRememberTokenName(), $token)
->first();
$model = $model->where($model->getAuthIdentifierName(), $identifier)->first();
if (! $model) {
return null;
}
$rememberToken = $model->getRememberToken();
return $rememberToken && hash_equals($rememberToken, $token) ? $model : null;
}
/**
@@ -75,7 +84,13 @@ class EloquentUserProvider implements UserProvider
{
$user->setRememberToken($token);
$timestamps = $user->timestamps;
$user->timestamps = false;
$user->save();
$user->timestamps = $timestamps;
}
/**
@@ -86,7 +101,9 @@ class EloquentUserProvider implements UserProvider
*/
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials)) {
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
}

View File

@@ -18,23 +18,15 @@ class Attempting
*/
public $remember;
/**
* Indicates if the user should be authenticated if successful.
*
* @var bool
*/
public $login;
/**
* Create a new event instance.
*
* @param array $credentials
* @param bool $remember
* @param bool $login
* @return void
*/
public function __construct($credentials, $remember, $login)
public function __construct($credentials, $remember)
{
$this->login = $login;
$this->remember = $remember;
$this->credentials = $credentials;
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Illuminate\Auth\Events;
use Illuminate\Queue\SerializesModels;
class Authenticated
{
use SerializesModels;
/**
* The authenticated user.
*
* @var \Illuminate\Contracts\Auth\Authenticatable
*/
public $user;
/**
* Create a new event instance.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function __construct($user)
{
$this->user = $user;
}
}

View File

@@ -23,6 +23,7 @@ class Failed
*
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param array $credentials
* @return void
*/
public function __construct($user, $credentials)
{

View File

@@ -0,0 +1,28 @@
<?php
namespace Illuminate\Auth\Events;
use Illuminate\Queue\SerializesModels;
class PasswordReset
{
use SerializesModels;
/**
* The user.
*
* @var \Illuminate\Contracts\Auth\Authenticatable
*/
public $user;
/**
* Create a new event instance.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function __construct($user)
{
$this->user = $user;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Illuminate\Auth\Events;
use Illuminate\Queue\SerializesModels;
class Registered
{
use SerializesModels;
/**
* The authenticated user.
*
* @var \Illuminate\Contracts\Auth\Authenticatable
*/
public $user;
/**
* Create a new event instance.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function __construct($user)
{
$this->user = $user;
}
}

View File

@@ -2,6 +2,7 @@
namespace Illuminate\Auth;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
/**
@@ -36,7 +37,7 @@ trait GuardHelpers
return $user;
}
throw new AuthenticationException($this);
throw new AuthenticationException;
}
/**
@@ -83,4 +84,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

@@ -0,0 +1,68 @@
<?php
namespace Illuminate\Auth\Middleware;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authenticate
{
/**
* The authentication factory instance.
*
* @var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Auth\Factory $auth
* @return void
*/
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string[] ...$guards
* @return mixed
*
* @throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, ...$guards)
{
$this->authenticate($guards);
return $next($request);
}
/**
* Determine if the user is logged in to any of the given guards.
*
* @param array $guards
* @return void
*
* @throws \Illuminate\Auth\AuthenticationException
*/
protected function authenticate(array $guards)
{
if (empty($guards)) {
return $this->auth->authenticate();
}
foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}
}
throw new AuthenticationException('Unauthenticated.', $guards);
}
}

View File

@@ -0,0 +1,100 @@
<?php
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.
*
* @var \Illuminate\Contracts\Auth\Access\Gate
*/
protected $gate;
/**
* 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)
{
$this->auth = $auth;
$this->gate = $gate;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $ability
* @param array|null $models
* @return mixed
*
* @throws \Illuminate\Auth\AuthenticationException
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function handle($request, Closure $next, $ability, ...$models)
{
$this->auth->authenticate();
$this->gate->authorize($ability, $this->getGateArguments($request, $models));
return $next($request);
}
/**
* Get the arguments parameter for the gate.
*
* @param \Illuminate\Http\Request $request
* @param array|null $models
* @return array|string|\Illuminate\Database\Eloquent\Model
*/
protected function getGateArguments($request, $models)
{
if (is_null($models)) {
return [];
}
return collect($models)->map(function ($model) use ($request) {
return $model instanceof Model ? $model : $this->getModel($request, $model);
})->all();
}
/**
* Get the model to authorize.
*
* @param \Illuminate\Http\Request $request
* @param string $model
* @return \Illuminate\Database\Eloquent\Model|string
*/
protected function getModel($request, $model)
{
return $this->isClassName($model) ? $model : $request->route($model);
}
/**
* Checks if the given string looks like a fully qualified class name.
*
* @param string $value
* @return bool
*/
protected function isClassName($value)
{
return strpos($value, '\\') !== false;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Illuminate\Auth\Notifications;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class ResetPassword extends Notification
{
/**
* The password reset token.
*
* @var string
*/
public $token;
/**
* Create a notification instance.
*
* @param string $token
* @return void
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Get the notification's channels.
*
* @param mixed $notifiable
* @return array|string
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Build the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
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.');
}
}

View File

@@ -2,6 +2,8 @@
namespace Illuminate\Auth\Passwords;
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
trait CanResetPassword
{
/**
@@ -13,4 +15,15 @@ trait CanResetPassword
{
return $this->email;
}
/**
* Send the password reset notification.
*
* @param string $token
* @return void
*/
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPasswordNotification($token));
}
}

View File

@@ -2,9 +2,10 @@
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;
class DatabaseTokenRepository implements TokenRepositoryInterface
@@ -16,6 +17,13 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
*/
protected $connection;
/**
* The Hasher implementation.
*
* @var \Illuminate\Contracts\Hashing\Hasher
*/
protected $hasher;
/**
* The token database table.
*
@@ -41,14 +49,17 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
* Create a new token repository instance.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
* @param string $table
* @param string $hashKey
* @param int $expires
* @return void
*/
public function __construct(ConnectionInterface $connection, $table, $hashKey, $expires = 60)
public function __construct(ConnectionInterface $connection, HasherContract $hasher,
$table, $hashKey, $expires = 60)
{
$this->table = $table;
$this->hasher = $hasher;
$this->hashKey = $hashKey;
$this->expires = $expires * 60;
$this->connection = $connection;
@@ -96,7 +107,7 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
*/
protected function getPayload($email, $token)
{
return ['email' => $email, 'token' => $token, 'created_at' => new Carbon];
return ['email' => $email, 'token' => $this->hasher->make($token), 'created_at' => new Carbon];
}
/**
@@ -108,35 +119,35 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
*/
public function exists(CanResetPasswordContract $user, $token)
{
$email = $user->getEmailForPasswordReset();
$record = (array) $this->getTable()->where(
'email', $user->getEmailForPasswordReset()
)->first();
$token = (array) $this->getTable()->where('email', $email)->where('token', $token)->first();
return $token && ! $this->tokenExpired($token);
return $record &&
! $this->tokenExpired($record['created_at']) &&
$this->hasher->check($token, $record['token']);
}
/**
* Determine if the token has expired.
*
* @param array $token
* @param string $createdAt
* @return bool
*/
protected function tokenExpired($token)
protected function tokenExpired($createdAt)
{
$expiresAt = Carbon::parse($token['created_at'])->addSeconds($this->expires);
return $expiresAt->isPast();
return Carbon::parse($createdAt)->addSeconds($this->expires)->isPast();
}
/**
* Delete a token record by token.
* Delete a token record by user.
*
* @param string $token
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return void
*/
public function delete($token)
public function delete(CanResetPasswordContract $user)
{
$this->getTable()->where('token', $token)->delete();
$this->deleteExisting($user);
}
/**
@@ -161,6 +172,16 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
return hash_hmac('sha256', Str::random(40), $this->hashKey);
}
/**
* Get the database connection instance.
*
* @return \Illuminate\Database\ConnectionInterface
*/
public function getConnection()
{
return $this->connection;
}
/**
* Begin a new database query against the table.
*
@@ -172,12 +193,12 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
}
/**
* Get the database connection instance.
* Get the hasher instance.
*
* @return \Illuminate\Database\ConnectionInterface
* @return \Illuminate\Contracts\Hashing\Hasher
*/
public function getConnection()
public function getHasher()
{
return $this->connection;
return $this->hasher;
}
}

View File

@@ -6,7 +6,6 @@ use Closure;
use Illuminate\Support\Arr;
use UnexpectedValueException;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Mail\Mailer as MailerContract;
use Illuminate\Contracts\Auth\PasswordBroker as PasswordBrokerContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
@@ -26,20 +25,6 @@ class PasswordBroker implements PasswordBrokerContract
*/
protected $users;
/**
* The mailer instance.
*
* @var \Illuminate\Contracts\Mail\Mailer
*/
protected $mailer;
/**
* The view of the password reset link e-mail.
*
* @var string
*/
protected $emailView;
/**
* The custom password validator callback.
*
@@ -52,29 +37,22 @@ class PasswordBroker implements PasswordBrokerContract
*
* @param \Illuminate\Auth\Passwords\TokenRepositoryInterface $tokens
* @param \Illuminate\Contracts\Auth\UserProvider $users
* @param \Illuminate\Contracts\Mail\Mailer $mailer
* @param string $emailView
* @return void
*/
public function __construct(TokenRepositoryInterface $tokens,
UserProvider $users,
MailerContract $mailer,
$emailView)
UserProvider $users)
{
$this->users = $users;
$this->mailer = $mailer;
$this->tokens = $tokens;
$this->emailView = $emailView;
}
/**
* Send a password reset link to a user.
*
* @param array $credentials
* @param \Closure|null $callback
* @return string
*/
public function sendResetLink(array $credentials, Closure $callback = null)
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
@@ -88,37 +66,13 @@ class PasswordBroker implements PasswordBrokerContract
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
$token = $this->tokens->create($user);
$this->emailResetLink($user, $token, $callback);
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
return static::RESET_LINK_SENT;
}
/**
* Send the password reset link via e-mail.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $token
* @param \Closure|null $callback
* @return int
*/
public function emailResetLink(CanResetPasswordContract $user, $token, Closure $callback = null)
{
// We will use the reminder view that was given to the broker to display the
// password reminder e-mail. We'll pass a "token" variable into the views
// so that it may be displayed for an user to click for password reset.
$view = $this->emailView;
return $this->mailer->send($view, compact('token', 'user'), function ($m) use ($user, $token, $callback) {
$m->to($user->getEmailForPasswordReset());
if (! is_null($callback)) {
call_user_func($callback, $m, $user, $token);
}
});
}
/**
* Reset the password for the given token.
*
@@ -137,14 +91,14 @@ class PasswordBroker implements PasswordBrokerContract
return $user;
}
$pass = $credentials['password'];
$password = $credentials['password'];
// Once we have called this callback, we will remove this token row from the
// table and return the response from this callback so the user gets sent
// to the destination given by the developers from the callback return.
call_user_func($callback, $user, $pass);
// Once the reset has been validated, we'll call the given callback with the
// new password. This gives the user an opportunity to store the password
// in their persistent storage. Then we'll delete the token and return.
$callback($user, $password);
$this->tokens->delete($credentials['token']);
$this->tokens->delete($user);
return static::PASSWORD_RESET;
}
@@ -153,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)
{
@@ -191,14 +145,15 @@ class PasswordBroker implements PasswordBrokerContract
*/
public function validateNewPassword(array $credentials)
{
list($password, $confirm) = [
$credentials['password'],
$credentials['password_confirmation'],
];
if (isset($this->passwordValidator)) {
list($password, $confirm) = [
$credentials['password'],
$credentials['password_confirmation'],
];
return call_user_func(
$this->passwordValidator, $credentials) && $password === $confirm;
$this->passwordValidator, $credentials
) && $password === $confirm;
}
return $this->validatePasswordWithDefaults($credentials);
@@ -224,7 +179,7 @@ class PasswordBroker implements PasswordBrokerContract
* Get the user for the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\CanResetPassword
* @return \Illuminate\Contracts\Auth\CanResetPassword|null
*
* @throws \UnexpectedValueException
*/
@@ -244,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)
@@ -253,20 +208,20 @@ class PasswordBroker implements PasswordBrokerContract
}
/**
* Delete the given password reset token.
* Delete password reset tokens of the given user.
*
* @param string $token
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return void
*/
public function deleteToken($token)
public function deleteToken(CanResetPasswordContract $user)
{
$this->tokens->delete($token);
$this->tokens->delete($user);
}
/**
* Validate the given password reset token.
*
* @param CanResetPasswordContract $user
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $token
* @return bool
*/

View File

@@ -6,6 +6,9 @@ use Illuminate\Support\Str;
use InvalidArgumentException;
use Illuminate\Contracts\Auth\PasswordBrokerFactory as FactoryContract;
/**
* @mixin \Illuminate\Contracts\Auth\PasswordBroker
*/
class PasswordBrokerManager implements FactoryContract
{
/**
@@ -69,9 +72,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['mailer'],
$config['email']
$this->app['auth']->createUserProvider($config['provider'] ?? null)
);
}
@@ -89,10 +90,11 @@ 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),
$this->app['hash'],
$config['table'],
$key,
$config['expire']
@@ -140,6 +142,6 @@ class PasswordBrokerManager implements FactoryContract
*/
public function __call($method, $parameters)
{
return call_user_func_array([$this->broker(), $method], $parameters);
return $this->broker()->{$method}(...$parameters);
}
}

View File

@@ -26,10 +26,10 @@ interface TokenRepositoryInterface
/**
* Delete a token record.
*
* @param string $token
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return void
*/
public function delete($token);
public function delete(CanResetPasswordContract $user);
/**
* Delete expired tokens.

View File

@@ -0,0 +1,88 @@
<?php
namespace Illuminate\Auth;
use Illuminate\Support\Str;
class Recaller
{
/**
* The "recaller" / "remember me" cookie string.
*
* @var string
*/
protected $recaller;
/**
* Create a new recaller instance.
*
* @param string $recaller
* @return void
*/
public function __construct($recaller)
{
$this->recaller = $recaller;
}
/**
* Get the user ID from the recaller.
*
* @return string
*/
public function id()
{
return explode('|', $this->recaller, 3)[0];
}
/**
* Get the "remember token" token from the recaller.
*
* @return string
*/
public function token()
{
return explode('|', $this->recaller, 3)[1];
}
/**
* Get the password from the recaller.
*
* @return string
*/
public function hash()
{
return explode('|', $this->recaller, 3)[2];
}
/**
* Determine if the recaller is valid.
*
* @return bool
*/
public function valid()
{
return $this->properString() && $this->hasAllSegments();
}
/**
* Determine if the recaller is an invalid string.
*
* @return bool
*/
protected function properString()
{
return is_string($this->recaller) && Str::contains($this->recaller, '|');
}
/**
* Determine if the recaller has all segments.
*
* @return bool
*/
protected function hasAllSegments()
{
$segments = explode('|', $this->recaller);
return count($segments) == 3 && trim($segments[0]) !== '' && trim($segments[1]) !== '';
}
}

View File

@@ -4,10 +4,12 @@ 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
{
use GuardHelpers;
use GuardHelpers, Macroable;
/**
* The guard callback.
@@ -28,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;
}
/**
@@ -50,7 +54,9 @@ class RequestGuard implements Guard
return $this->user;
}
return $this->user = call_user_func($this->callback, $this->request);
return $this->user = call_user_func(
$this->callback, $this->request, $this->getProvider()
);
}
/**
@@ -62,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,24 +4,25 @@ namespace Illuminate\Auth;
use RuntimeException;
use Illuminate\Support\Str;
use Illuminate\Http\Response;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Session\Session;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Events\Dispatcher;
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\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
class SessionGuard implements StatefulGuard, SupportsBasicAuth
{
use GuardHelpers;
use GuardHelpers, Macroable;
/**
* The name of the Guard. Typically "session".
*
* Corresponds to driver name in authentication configuration.
* Corresponds to guard name in authentication configuration.
*
* @var string
*/
@@ -44,7 +45,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
/**
* The session used by the guard.
*
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
* @var \Illuminate\Contracts\Session\Session
*/
protected $session;
@@ -81,20 +82,20 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*
* @var bool
*/
protected $tokenRetrievalAttempted = false;
protected $recallAttempted = false;
/**
* Create a new authentication guard.
*
* @param string $name
* @param \Illuminate\Contracts\Auth\UserProvider $provider
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
* @param \Illuminate\Contracts\Session\Session $session
* @param \Symfony\Component\HttpFoundation\Request $request
* @return void
*/
public function __construct($name,
UserProvider $provider,
SessionInterface $session,
Session $session,
Request $request = null)
{
$this->name = $name;
@@ -126,28 +127,68 @@ 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)) {
$user = $this->provider->retrieveById($id);
if ($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->getRecaller();
$recaller = $this->recaller();
if (is_null($user) && ! is_null($recaller)) {
$user = $this->getUserByRecaller($recaller);
if (is_null($this->user) && ! is_null($recaller)) {
$this->user = $this->userFromRecaller($recaller);
if ($user) {
$this->updateSession($user->getAuthIdentifier());
if ($this->user) {
$this->updateSession($this->user->getAuthIdentifier());
$this->fireLoginEvent($user, true);
$this->fireLoginEvent($this->user, true);
}
}
return $this->user = $user;
return $this->user;
}
/**
* Pull a user from the repository by its "remember me" cookie token.
*
* @param \Illuminate\Auth\Recaller $recaller
* @return mixed
*/
protected function userFromRecaller($recaller)
{
if (! $recaller->valid() || $this->recallAttempted) {
return;
}
// 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.
$this->recallAttempted = true;
$this->viaRemember = ! is_null($user = $this->provider->retrieveByToken(
$recaller->id(), $recaller->token()
));
return $user;
}
/**
* Get the decrypted recaller cookie for the request.
*
* @return \Illuminate\Auth\Recaller|null
*/
protected function recaller()
{
if (is_null($this->request)) {
return;
}
if ($recaller = $this->request->cookies->get($this->getRecallerName())) {
return new Recaller($recaller);
}
}
/**
@@ -161,71 +202,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
return;
}
$id = $this->session->get($this->getName());
if (is_null($id) && $this->user()) {
$id = $this->user()->getAuthIdentifier();
}
return $id;
}
/**
* Pull a user from the repository by its recaller ID.
*
* @param string $recaller
* @return mixed
*/
protected function getUserByRecaller($recaller)
{
if ($this->validRecaller($recaller) && ! $this->tokenRetrievalAttempted) {
$this->tokenRetrievalAttempted = true;
list($id, $token) = explode('|', $recaller, 2);
$this->viaRemember = ! is_null($user = $this->provider->retrieveByToken($id, $token));
return $user;
}
}
/**
* Get the decrypted recaller cookie for the request.
*
* @return string|null
*/
protected function getRecaller()
{
return $this->request->cookies->get($this->getRecallerName());
}
/**
* Get the user ID from the recaller cookie.
*
* @return string|null
*/
protected function getRecallerId()
{
if ($this->validRecaller($recaller = $this->getRecaller())) {
return head(explode('|', $recaller));
}
}
/**
* Determine if the recaller cookie is in a valid format.
*
* @param mixed $recaller
* @return bool
*/
protected function validRecaller($recaller)
{
if (! is_string($recaller) || ! Str::contains($recaller, '|')) {
return false;
}
$segments = explode('|', $recaller);
return count($segments) == 2 && trim($segments[0]) !== '' && trim($segments[1]) !== '';
return $this->user()
? $this->user()->getAuthIdentifier()
: $this->session->get($this->getName());
}
/**
@@ -236,6 +215,8 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*/
public function once(array $credentials = [])
{
$this->fireAttemptEvent($credentials);
if ($this->validate($credentials)) {
$this->setUser($this->lastAttempted);
@@ -245,6 +226,23 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
return false;
}
/**
* Log the given user ID into the application without sessions or cookies.
*
* @param mixed $id
* @return \Illuminate\Contracts\Auth\Authenticatable|false
*/
public function onceUsingId($id)
{
if (! is_null($user = $this->provider->retrieveById($id))) {
$this->setUser($user);
return $user;
}
return false;
}
/**
* Validate a user's credentials.
*
@@ -253,7 +251,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*/
public function validate(array $credentials = [])
{
return $this->attempt($credentials, false, false);
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
return $this->hasValidCredentials($user, $credentials);
}
/**
@@ -276,7 +276,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
return;
}
return $this->getBasicResponse();
return $this->failedBasicResponse();
}
/**
@@ -288,10 +288,10 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*/
public function onceBasic($field = 'email', $extraConditions = [])
{
$credentials = $this->getBasicCredentials($this->getRequest(), $field);
$credentials = $this->basicCredentials($this->getRequest(), $field);
if (! $this->once(array_merge($credentials, $extraConditions))) {
return $this->getBasicResponse();
return $this->failedBasicResponse();
}
}
@@ -309,9 +309,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
return false;
}
$credentials = $this->getBasicCredentials($request, $field);
return $this->attempt(array_merge($credentials, $extraConditions));
return $this->attempt(array_merge(
$this->basicCredentials($request, $field), $extraConditions
));
}
/**
@@ -321,7 +321,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
* @param string $field
* @return array
*/
protected function getBasicCredentials(Request $request, $field)
protected function basicCredentials(Request $request, $field)
{
return [$field => $request->getUser(), 'password' => $request->getPassword()];
}
@@ -329,13 +329,12 @@ 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 getBasicResponse()
protected function failedBasicResponse()
{
$headers = ['WWW-Authenticate' => 'Basic'];
return new Response('Invalid credentials.', 401, $headers);
throw new UnauthorizedHttpException('Basic', 'Invalid credentials.');
}
/**
@@ -343,12 +342,11 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*
* @param array $credentials
* @param bool $remember
* @param bool $login
* @return bool
*/
public function attempt(array $credentials = [], $remember = false, $login = true)
public function attempt(array $credentials = [], $remember = false)
{
$this->fireAttemptEvent($credentials, $remember, $login);
$this->fireAttemptEvent($credentials, $remember);
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
@@ -356,9 +354,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
// to validate the user against the given credentials, and if they are in
// fact valid we'll log the users into the application and return true.
if ($this->hasValidCredentials($user, $credentials)) {
if ($login) {
$this->login($user, $remember);
}
$this->login($user, $remember);
return true;
}
@@ -366,9 +362,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
// If the authentication attempt fails we will fire an event so that the user
// may be notified of any suspicious attempts to access their account from
// an unrecognized user. A developer may listen to this event as needed.
if ($login) {
$this->fireFailedEvent($user, $credentials);
}
$this->fireFailedEvent($user, $credentials);
return false;
}
@@ -386,47 +380,21 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
}
/**
* Fire the attempt event with the arguments.
* Log the given user ID into the application.
*
* @param array $credentials
* @param bool $remember
* @param bool $login
* @return void
* @param mixed $id
* @param bool $remember
* @return \Illuminate\Contracts\Auth\Authenticatable|false
*/
protected function fireAttemptEvent(array $credentials, $remember, $login)
public function loginUsingId($id, $remember = false)
{
if (isset($this->events)) {
$this->events->fire(new Events\Attempting(
$credentials, $remember, $login
));
}
}
if (! is_null($user = $this->provider->retrieveById($id))) {
$this->login($user, $remember);
/**
* Fire the failed authentication attempt event with the given arguments.
*
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param array $credentials
* @return void
*/
protected function fireFailedEvent($user, array $credentials)
{
if (isset($this->events)) {
$this->events->fire(new Events\Failed($user, $credentials));
return $user;
}
}
/**
* Register an authentication attempt event listener.
*
* @param mixed $callback
* @return void
*/
public function attempting($callback)
{
if (isset($this->events)) {
$this->events->listen(Events\Attempting::class, $callback);
}
return false;
}
/**
@@ -444,7 +412,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
// queue a permanent cookie that contains the encrypted copy of the user
// identifier. We will then decrypt this later to retrieve the users.
if ($remember) {
$this->createRememberTokenIfDoesntExist($user);
$this->ensureRememberTokenIsSet($user);
$this->queueRecallerCookie($user);
}
@@ -457,20 +425,6 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
$this->setUser($user);
}
/**
* Fire the login event if the dispatcher is set.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param bool $remember
* @return void
*/
protected function fireLoginEvent($user, $remember = false)
{
if (isset($this->events)) {
$this->events->fire(new Events\Login($user, $remember));
}
}
/**
* Update the session with the given ID.
*
@@ -479,48 +433,22 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*/
protected function updateSession($id)
{
$this->session->set($this->getName(), $id);
$this->session->put($this->getName(), $id);
$this->session->migrate(true);
}
/**
* Log the given user ID into the application.
* Create a new "remember me" token for the user if one doesn't already exist.
*
* @param mixed $id
* @param bool $remember
* @return \Illuminate\Contracts\Auth\Authenticatable
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function loginUsingId($id, $remember = false)
protected function ensureRememberTokenIsSet(AuthenticatableContract $user)
{
$user = $this->provider->retrieveById($id);
if (! is_null($user)) {
$this->login($user, $remember);
return $user;
if (empty($user->getRememberToken())) {
$this->cycleRememberToken($user);
}
return false;
}
/**
* Log the given user ID into the application without sessions or cookies.
*
* @param mixed $id
* @return bool
*/
public function onceUsingId($id)
{
$user = $this->provider->retrieveById($id);
if (! is_null($user)) {
$this->setUser($user);
return true;
}
return false;
}
/**
@@ -531,9 +459,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*/
protected function queueRecallerCookie(AuthenticatableContract $user)
{
$value = $user->getAuthIdentifier().'|'.$user->getRememberToken();
$this->getCookieJar()->queue($this->createRecaller($value));
$this->getCookieJar()->queue($this->createRecaller(
$user->getAuthIdentifier().'|'.$user->getRememberToken().'|'.$user->getAuthPassword()
));
}
/**
@@ -562,11 +490,11 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
$this->clearUserDataFromStorage();
if (! is_null($this->user)) {
$this->refreshRememberToken($user);
$this->cycleRememberToken($user);
}
if (isset($this->events)) {
$this->events->fire(new Events\Logout($user));
$this->events->dispatch(new Events\Logout($user));
}
// Once we have fired the logout event we will clear the users out of memory
@@ -586,10 +514,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
{
$this->session->remove($this->getName());
if (! is_null($this->getRecaller())) {
$recaller = $this->getRecallerName();
$this->getCookieJar()->queue($this->getCookieJar()->forget($recaller));
if (! is_null($this->recaller())) {
$this->getCookieJar()->queue($this->getCookieJar()
->forget($this->getRecallerName()));
}
}
@@ -599,7 +526,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
protected function refreshRememberToken(AuthenticatableContract $user)
protected function cycleRememberToken(AuthenticatableContract $user)
{
$user->setRememberToken($token = Str::random(60));
@@ -607,18 +534,115 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
}
/**
* Create a new "remember me" token for the user if one doesn't already exist.
* Register an authentication attempt event listener.
*
* @param mixed $callback
* @return void
*/
public function attempting($callback)
{
if (isset($this->events)) {
$this->events->listen(Events\Attempting::class, $callback);
}
}
/**
* Fire the attempt event with the arguments.
*
* @param array $credentials
* @param bool $remember
* @return void
*/
protected function fireAttemptEvent(array $credentials, $remember = false)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Attempting(
$credentials, $remember
));
}
}
/**
* Fire the login event if the dispatcher is set.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param bool $remember
* @return void
*/
protected function fireLoginEvent($user, $remember = false)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Login($user, $remember));
}
}
/**
* Fire the authenticated event if the dispatcher is set.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
protected function createRememberTokenIfDoesntExist(AuthenticatableContract $user)
protected function fireAuthenticatedEvent($user)
{
if (empty($user->getRememberToken())) {
$this->refreshRememberToken($user);
if (isset($this->events)) {
$this->events->dispatch(new Events\Authenticated($user));
}
}
/**
* Fire the failed authentication attempt event with the given arguments.
*
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param array $credentials
* @return void
*/
protected function fireFailedEvent($user, array $credentials)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Failed($user, $credentials));
}
}
/**
* Get the last user we attempted to authenticate.
*
* @return \Illuminate\Contracts\Auth\Authenticatable
*/
public function getLastAttempted()
{
return $this->lastAttempted;
}
/**
* Get a unique identifier for the auth session value.
*
* @return string
*/
public function getName()
{
return 'login_'.$this->name.'_'.sha1(static::class);
}
/**
* Get the name of the cookie used to store the "recaller".
*
* @return string
*/
public function getRecallerName()
{
return 'remember_'.$this->name.'_'.sha1(static::class);
}
/**
* Determine if the user was authenticated via "remember me" cookie.
*
* @return bool
*/
public function viaRemember()
{
return $this->viaRemember;
}
/**
* Get the cookie creator instance used by the guard.
*
@@ -670,34 +694,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.
*
@@ -720,6 +723,8 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
$this->loggedOut = false;
$this->fireAuthenticatedEvent($user);
return $this;
}
@@ -745,44 +750,4 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
return $this;
}
/**
* Get the last user we attempted to authenticate.
*
* @return \Illuminate\Contracts\Auth\Authenticatable
*/
public function getLastAttempted()
{
return $this->lastAttempted;
}
/**
* Get a unique identifier for the auth session value.
*
* @return string
*/
public function getName()
{
return 'login_'.$this->name.'_'.sha1(static::class);
}
/**
* Get the name of the cookie used to store the "recaller".
*
* @return string
*/
public function getRecallerName()
{
return 'remember_'.$this->name.'_'.sha1(static::class);
}
/**
* Determine if the user was authenticated via "remember me" cookie.
*
* @return bool
*/
public function viaRemember()
{
return $this->viaRemember;
}
}

View File

@@ -18,7 +18,7 @@ class TokenGuard implements Guard
protected $request;
/**
* The name of the field on the request containing the API token.
* The name of the query string item from the request containing the API token.
*
* @var string
*/
@@ -78,9 +78,13 @@ class TokenGuard implements Guard
*
* @return string
*/
protected function getTokenForRequest()
public function getTokenForRequest()
{
$token = $this->request->input($this->inputKey);
$token = $this->request->query($this->inputKey);
if (empty($token)) {
$token = $this->request->input($this->inputKey);
}
if (empty($token)) {
$token = $this->request->bearerToken();

View File

@@ -2,7 +2,7 @@
"name": "illuminate/auth",
"description": "The Illuminate Auth package.",
"license": "MIT",
"homepage": "http://laravel.com",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
@@ -14,11 +14,11 @@
}
],
"require": {
"php": ">=5.5.9",
"illuminate/contracts": "5.2.*",
"illuminate/http": "5.2.*",
"illuminate/support": "5.2.*",
"nesbot/carbon": "~1.20"
"php": ">=7.0",
"illuminate/contracts": "5.5.*",
"illuminate/http": "5.5.*",
"illuminate/queue": "5.5.*",
"illuminate/support": "5.5.*"
},
"autoload": {
"psr-4": {
@@ -27,13 +27,16 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.2-dev"
"dev-master": "5.5-dev"
}
},
"suggest": {
"illuminate/console": "Required to use the auth:clear-resets command (5.2.*).",
"illuminate/queue": "Required to fire login / logout events (5.2.*).",
"illuminate/session": "Required to use the session based guard (5.2.*)."
"illuminate/console": "Required to use the auth:clear-resets command (5.5.*).",
"illuminate/queue": "Required to fire login / logout events (5.5.*).",
"illuminate/session": "Required to use the session based guard (5.5.*)."
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Illuminate\Broadcasting;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Broadcast;
class BroadcastController extends Controller
{
/**
* Authenticate the request for channel access.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function authenticate(Request $request)
{
return Broadcast::auth($request);
}
}

View File

@@ -4,49 +4,49 @@ namespace Illuminate\Broadcasting;
use ReflectionClass;
use ReflectionProperty;
use Illuminate\Contracts\Queue\Job;
use Illuminate\Support\Arr;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Broadcasting\Broadcaster;
class BroadcastEvent
class BroadcastEvent implements ShouldQueue
{
use Queueable;
/**
* The broadcaster implementation.
* The event instance.
*
* @var \Illuminate\Contracts\Broadcasting\Broadcaster
* @var mixed
*/
protected $broadcaster;
public $event;
/**
* Create a new job handler instance.
*
* @param \Illuminate\Contracts\Broadcasting\Broadcaster $broadcaster
* @param mixed $event
* @return void
*/
public function __construct(Broadcaster $broadcaster)
public function __construct($event)
{
$this->broadcaster = $broadcaster;
$this->event = $event;
}
/**
* Handle the queued job.
*
* @param \Illuminate\Contracts\Queue\Job $job
* @param array $data
* @param \Illuminate\Contracts\Broadcasting\Broadcaster $broadcaster
* @return void
*/
public function fire(Job $job, array $data)
public function handle(Broadcaster $broadcaster)
{
$event = unserialize($data['event']);
$name = method_exists($this->event, 'broadcastAs')
? $this->event->broadcastAs() : get_class($this->event);
$name = method_exists($event, 'broadcastAs')
? $event->broadcastAs() : get_class($event);
$this->broadcaster->broadcast(
$event->broadcastOn(), $name, $this->getPayloadFromEvent($event)
$broadcaster->broadcast(
Arr::wrap($this->event->broadcastOn()), $name,
$this->getPayloadFromEvent($this->event)
);
$job->delete();
}
/**
@@ -58,7 +58,9 @@ class BroadcastEvent
protected function getPayloadFromEvent($event)
{
if (method_exists($event, 'broadcastWith')) {
return $event->broadcastWith();
return array_merge(
$event->broadcastWith(), ['socket' => data_get($event, 'socket')]
);
}
$payload = [];
@@ -67,6 +69,8 @@ class BroadcastEvent
$payload[$property->getName()] = $this->formatProperty($property->getValue($event));
}
unset($payload['broadcastQueue']);
return $payload;
}
@@ -84,4 +88,24 @@ class BroadcastEvent
return $value;
}
/**
* Get the display name for the queued job.
*
* @return string
*/
public function displayName()
{
return get_class($this->event);
}
/**
* Prepare the instance for cloning.
*
* @return void
*/
public function __clone()
{
$this->event = clone $this->event;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Illuminate\Broadcasting;
use RuntimeException;
class BroadcastException extends RuntimeException
{
//
}

View File

@@ -2,15 +2,20 @@
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;
use Illuminate\Broadcasting\Broadcasters\NullBroadcaster;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Broadcasting\Broadcasters\RedisBroadcaster;
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
use Illuminate\Contracts\Broadcasting\Factory as FactoryContract;
/**
* @mixin \Illuminate\Contracts\Broadcasting\Broadcaster
*/
class BroadcastManager implements FactoryContract
{
/**
@@ -45,6 +50,82 @@ class BroadcastManager implements FactoryContract
$this->app = $app;
}
/**
* Register the routes for handling broadcast authentication and sockets.
*
* @param array|null $attributes
* @return void
*/
public function routes(array $attributes = null)
{
if ($this->app->routesAreCached()) {
return;
}
$attributes = $attributes ?: ['middleware' => ['web']];
$this->app['router']->group($attributes, function ($router) {
$router->post('/broadcasting/auth', '\\'.BroadcastController::class.'@authenticate');
});
}
/**
* Get the socket ID for the given request.
*
* @param \Illuminate\Http\Request|null $request
* @return string|null
*/
public function socket($request = null)
{
if (! $request && ! $this->app->bound('request')) {
return;
}
$request = $request ?: $this->app['request'];
return $request->header('X-Socket-ID');
}
/**
* Begin broadcasting an event.
*
* @param mixed|null $event
* @return \Illuminate\Broadcasting\PendingBroadcast|void
*/
public function event($event = null)
{
return new PendingBroadcast($this->app->make('events'), $event);
}
/**
* Queue the given event for broadcast.
*
* @param mixed $event
* @return void
*/
public function queue($event)
{
$connection = $event instanceof ShouldBroadcastNow ? 'sync' : null;
if (is_null($connection) && isset($event->connection)) {
$connection = $event->connection;
}
$queue = null;
if (method_exists($event, 'broadcastQueue')) {
$queue = $event->broadcastQueue();
} elseif (isset($event->broadcastQueue)) {
$queue = $event->broadcastQueue;
} elseif (isset($event->queue)) {
$queue = $event->queue;
}
$this->app->make('queue')->connection($connection)->pushOn(
$queue, new BroadcastEvent(clone $event)
);
}
/**
* Get a driver instance.
*
@@ -77,7 +158,7 @@ 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);
}
/**
@@ -98,15 +179,15 @@ class BroadcastManager implements FactoryContract
if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($config);
} else {
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
if (method_exists($this, $driverMethod)) {
return $this->{$driverMethod}($config);
} else {
throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
}
}
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
if (! method_exists($this, $driverMethod)) {
throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
}
return $this->{$driverMethod}($config);
}
/**
@@ -129,7 +210,8 @@ 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', []))
new Pusher($config['key'], $config['secret'],
$config['app_id'], $config['options'] ?? [])
);
}
@@ -142,7 +224,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
);
}
@@ -155,10 +237,21 @@ class BroadcastManager implements FactoryContract
protected function createLogDriver(array $config)
{
return new LogBroadcaster(
$this->app->make('Psr\Log\LoggerInterface')
$this->app->make(LoggerInterface::class)
);
}
/**
* Create an instance of the driver.
*
* @param array $config
* @return \Illuminate\Contracts\Broadcasting\Broadcaster
*/
protected function createNullDriver(array $config)
{
return new NullBroadcaster;
}
/**
* Get the connection configuration.
*
@@ -214,6 +307,6 @@ class BroadcastManager implements FactoryContract
*/
public function __call($method, $parameters)
{
return call_user_func_array([$this->driver(), $method], $parameters);
return $this->driver()->$method(...$parameters);
}
}

View File

@@ -3,6 +3,8 @@
namespace Illuminate\Broadcasting;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Broadcasting\Factory as BroadcastingFactory;
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
class BroadcastServiceProvider extends ServiceProvider
{
@@ -20,16 +22,16 @@ class BroadcastServiceProvider extends ServiceProvider
*/
public function register()
{
$this->app->singleton('Illuminate\Broadcasting\BroadcastManager', function ($app) {
$this->app->singleton(BroadcastManager::class, function ($app) {
return new BroadcastManager($app);
});
$this->app->singleton('Illuminate\Contracts\Broadcasting\Broadcaster', function ($app) {
return $app->make('Illuminate\Broadcasting\BroadcastManager')->connection();
$this->app->singleton(BroadcasterContract::class, function ($app) {
return $app->make(BroadcastManager::class)->connection();
});
$this->app->alias(
'Illuminate\Broadcasting\BroadcastManager', 'Illuminate\Contracts\Broadcasting\Factory'
BroadcastManager::class, BroadcastingFactory::class
);
}
@@ -41,9 +43,9 @@ class BroadcastServiceProvider extends ServiceProvider
public function provides()
{
return [
'Illuminate\Broadcasting\BroadcastManager',
'Illuminate\Contracts\Broadcasting\Factory',
'Illuminate\Contracts\Broadcasting\Broadcaster',
BroadcastManager::class,
BroadcastingFactory::class,
BroadcasterContract::class,
];
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace Illuminate\Broadcasting\Broadcasters;
use ReflectionFunction;
use Illuminate\Support\Str;
use Illuminate\Container\Container;
use Illuminate\Contracts\Routing\UrlRoutable;
use Illuminate\Contracts\Routing\BindingRegistrar;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
abstract class Broadcaster implements BroadcasterContract
{
/**
* The registered channel authenticators.
*
* @var array
*/
protected $channels = [];
/**
* The binding registrar instance.
*
* @var BindingRegistrar
*/
protected $bindingRegistrar;
/**
* Register a channel authenticator.
*
* @param string $channel
* @param callable $callback
* @return $this
*/
public function channel($channel, callable $callback)
{
$this->channels[$channel] = $callback;
return $this;
}
/**
* Authenticate the incoming request for a given channel.
*
* @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)) {
continue;
}
$parameters = $this->extractAuthParameters($pattern, $channel, $callback);
if ($result = $callback($request->user(), ...$parameters)) {
return $this->validAuthenticationResponse($request, $result);
}
}
throw new AccessDeniedHttpException;
}
/**
* Extract the parameters from the given pattern and channel.
*
* @param string $pattern
* @param string $channel
* @param callable $callback
* @return array
*/
protected function extractAuthParameters($pattern, $channel, $callback)
{
$callbackParameters = (new ReflectionFunction($callback))->getParameters();
return collect($this->extractChannelKeys($pattern, $channel))->reject(function ($value, $key) {
return is_numeric($key);
})->map(function ($value, $key) use ($callbackParameters) {
return $this->resolveBinding($key, $value, $callbackParameters);
})->values()->all();
}
/**
* Extract the channel keys from the incoming channel name.
*
* @param string $pattern
* @param string $channel
* @return array
*/
protected function extractChannelKeys($pattern, $channel)
{
preg_match('/^'.preg_replace('/\{(.*?)\}/', '(?<$1>[^\.]+)', $pattern).'/', $channel, $keys);
return $keys;
}
/**
* Resolve the given parameter binding.
*
* @param string $key
* @param string $value
* @param array $callbackParameters
* @return mixed
*/
protected function resolveBinding($key, $value, $callbackParameters)
{
$newValue = $this->resolveExplicitBindingIfPossible($key, $value);
return $newValue === $value ? $this->resolveImplicitBindingIfPossible(
$key, $value, $callbackParameters
) : $newValue;
}
/**
* Resolve an explicit parameter binding if applicable.
*
* @param string $key
* @param mixed $value
* @return mixed
*/
protected function resolveExplicitBindingIfPossible($key, $value)
{
$binder = $this->binder();
if ($binder && $binder->getBindingCallback($key)) {
return call_user_func($binder->getBindingCallback($key), $value);
}
return $value;
}
/**
* Resolve an implicit parameter binding if applicable.
*
* @param string $key
* @param mixed $value
* @param array $callbackParameters
* @return mixed
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
protected function resolveImplicitBindingIfPossible($key, $value, $callbackParameters)
{
foreach ($callbackParameters as $parameter) {
if (! $this->isImplicitlyBindable($key, $parameter)) {
continue;
}
$instance = $parameter->getClass()->newInstance();
if (! $model = $instance->resolveRouteBinding($value)) {
throw new AccessDeniedHttpException;
}
return $model;
}
return $value;
}
/**
* Determine if a given key and parameter is implicitly bindable.
*
* @param string $key
* @param \ReflectionParameter $parameter
* @return bool
*/
protected function isImplicitlyBindable($key, $parameter)
{
return $parameter->name === $key && $parameter->getClass() &&
$parameter->getClass()->isSubclassOf(UrlRoutable::class);
}
/**
* Format the channel array into an array of strings.
*
* @param array $channels
* @return array
*/
protected function formatChannels(array $channels)
{
return array_map(function ($channel) {
return (string) $channel;
}, $channels);
}
/**
* Get the model binding registrar instance.
*
* @return \Illuminate\Contracts\Routing\BindingRegistrar
*/
protected function binder()
{
if (! $this->bindingRegistrar) {
$this->bindingRegistrar = Container::getInstance()->bound(BindingRegistrar::class)
? Container::getInstance()->make(BindingRegistrar::class) : null;
}
return $this->bindingRegistrar;
}
}

View File

@@ -3,9 +3,8 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Psr\Log\LoggerInterface;
use Illuminate\Contracts\Broadcasting\Broadcaster;
class LogBroadcaster implements Broadcaster
class LogBroadcaster extends Broadcaster
{
/**
* The logger implementation.
@@ -25,12 +24,28 @@ class LogBroadcaster implements Broadcaster
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function auth($request)
{
//
}
/**
* {@inheritdoc}
*/
public function validAuthenticationResponse($request, $result)
{
//
}
/**
* {@inheritdoc}
*/
public function broadcast(array $channels, $event, array $payload = [])
{
$channels = implode(', ', $channels);
$channels = implode(', ', $this->formatChannels($channels));
$payload = json_encode($payload, JSON_PRETTY_PRINT);

View File

@@ -0,0 +1,30 @@
<?php
namespace Illuminate\Broadcasting\Broadcasters;
class NullBroadcaster extends Broadcaster
{
/**
* {@inheritdoc}
*/
public function auth($request)
{
//
}
/**
* {@inheritdoc}
*/
public function validAuthenticationResponse($request, $result)
{
//
}
/**
* {@inheritdoc}
*/
public function broadcast(array $channels, $event, array $payload = [])
{
//
}
}

View File

@@ -2,22 +2,25 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Pusher;
use Illuminate\Contracts\Broadcasting\Broadcaster;
use Pusher\Pusher;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Broadcasting\BroadcastException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class PusherBroadcaster implements Broadcaster
class PusherBroadcaster extends Broadcaster
{
/**
* 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)
@@ -26,17 +29,90 @@ class PusherBroadcaster implements Broadcaster
}
/**
* {@inheritdoc}
* Authenticate the incoming request for a given channel.
*
* @param \Illuminate\Http\Request $request
* @return mixed
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function auth($request)
{
if (Str::startsWith($request->channel_name, ['private-', 'presence-']) &&
! $request->user()) {
throw new AccessDeniedHttpException;
}
$channelName = Str::startsWith($request->channel_name, 'private-')
? Str::replaceFirst('private-', '', $request->channel_name)
: Str::replaceFirst('presence-', '', $request->channel_name);
return parent::verifyUserCanAccessChannel(
$request, $channelName
);
}
/**
* Return the valid authentication response.
*
* @param \Illuminate\Http\Request $request
* @param mixed $result
* @return mixed
*/
public function validAuthenticationResponse($request, $result)
{
if (Str::startsWith($request->channel_name, 'private')) {
return $this->decodePusherResponse(
$this->pusher->socket_auth($request->channel_name, $request->socket_id)
);
}
return $this->decodePusherResponse(
$this->pusher->presence_auth(
$request->channel_name, $request->socket_id, $request->user()->getAuthIdentifier(), $result)
);
}
/**
* Decode the given Pusher response.
*
* @param mixed $response
* @return array
*/
protected function decodePusherResponse($response)
{
return json_decode($response, true);
}
/**
* Broadcast the given event.
*
* @param array $channels
* @param string $event
* @param array $payload
* @return void
*/
public function broadcast(array $channels, $event, array $payload = [])
{
$this->pusher->trigger($channels, $event, $payload);
$socket = Arr::pull($payload, 'socket');
$response = $this->pusher->trigger(
$this->formatChannels($channels), $event, $payload, $socket, true
);
if ((is_array($response) && $response['status'] >= 200 && $response['status'] <= 299)
|| $response === true) {
return;
}
throw new BroadcastException(
is_bool($response) ? 'Failed to connect to Pusher.' : $response['body']
);
}
/**
* Get the Pusher SDK instance.
*
* @return \Pusher
* @return \Pusher\Pusher
*/
public function getPusher()
{

View File

@@ -2,15 +2,17 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Illuminate\Contracts\Broadcasting\Broadcaster;
use Illuminate\Contracts\Redis\Database as RedisDatabase;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Contracts\Redis\Factory as Redis;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class RedisBroadcaster implements Broadcaster
class RedisBroadcaster extends Broadcaster
{
/**
* The Redis instance.
*
* @var \Illuminate\Contracts\Redis\Database
* @var \Illuminate\Contracts\Redis\Factory
*/
protected $redis;
@@ -24,26 +26,77 @@ class RedisBroadcaster implements Broadcaster
/**
* Create a new broadcaster instance.
*
* @param \Illuminate\Contracts\Redis\Database $redis
* @param \Illuminate\Contracts\Redis\Factory $redis
* @param string $connection
* @return void
*/
public function __construct(RedisDatabase $redis, $connection = null)
public function __construct(Redis $redis, $connection = null)
{
$this->redis = $redis;
$this->connection = $connection;
}
/**
* {@inheritdoc}
* Authenticate the incoming request for a given channel.
*
* @param \Illuminate\Http\Request $request
* @return mixed
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function auth($request)
{
if (Str::startsWith($request->channel_name, ['private-', 'presence-']) &&
! $request->user()) {
throw new AccessDeniedHttpException;
}
$channelName = Str::startsWith($request->channel_name, 'private-')
? Str::replaceFirst('private-', '', $request->channel_name)
: Str::replaceFirst('presence-', '', $request->channel_name);
return parent::verifyUserCanAccessChannel(
$request, $channelName
);
}
/**
* Return the valid authentication response.
*
* @param \Illuminate\Http\Request $request
* @param mixed $result
* @return mixed
*/
public function validAuthenticationResponse($request, $result)
{
if (is_bool($result)) {
return json_encode($result);
}
return json_encode(['channel_data' => [
'user_id' => $request->user()->getAuthIdentifier(),
'user_info' => $result,
]]);
}
/**
* Broadcast the given event.
*
* @param array $channels
* @param string $event
* @param array $payload
* @return void
*/
public function broadcast(array $channels, $event, array $payload = [])
{
$connection = $this->redis->connection($this->connection);
$payload = json_encode(['event' => $event, 'data' => $payload]);
$payload = json_encode([
'event' => $event,
'data' => $payload,
'socket' => Arr::pull($payload, 'socket'),
]);
foreach ($channels as $channel) {
foreach ($this->formatChannels($channels) as $channel) {
$connection->publish($channel, $payload);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Illuminate\Broadcasting;
class Channel
{
/**
* The channel's name.
*
* @var string
*/
public $name;
/**
* Create a new channel instance.
*
* @param string $name
* @return void
*/
public function __construct($name)
{
$this->name = $name;
}
/**
* Convert the channel instance to a string.
*
* @return string
*/
public function __toString()
{
return $this->name;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Illuminate\Broadcasting;
use Illuminate\Support\Facades\Broadcast;
trait InteractsWithSockets
{
/**
* The socket ID for the user that raised the event.
*
* @var string|null
*/
public $socket;
/**
* Exclude the current user from receiving the broadcast.
*
* @return $this
*/
public function dontBroadcastToCurrentUser()
{
$this->socket = Broadcast::socket();
return $this;
}
/**
* Broadcast the event to everyone.
*
* @return $this
*/
public function broadcastToEveryone()
{
$this->socket = null;
return $this;
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Illuminate\Broadcasting;
use Illuminate\Contracts\Events\Dispatcher;
class PendingBroadcast
{
/**
* The event dispatcher implementation.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events;
/**
* The event instance.
*
* @var mixed
*/
protected $event;
/**
* Create a new pending broadcast instance.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @param mixed $event
* @return void
*/
public function __construct(Dispatcher $events, $event)
{
$this->event = $event;
$this->events = $events;
}
/**
* Broadcast the event to everyone except the current user.
*
* @return $this
*/
public function toOthers()
{
if (method_exists($this->event, 'dontBroadcastToCurrentUser')) {
$this->event->dontBroadcastToCurrentUser();
}
return $this;
}
/**
* Handle the object's destruction.
*
* @return void
*/
public function __destruct()
{
$this->events->dispatch($this->event);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Illuminate\Broadcasting;
class PresenceChannel extends Channel
{
/**
* Create a new channel instance.
*
* @param string $name
* @return void
*/
public function __construct($name)
{
parent::__construct('presence-'.$name);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Illuminate\Broadcasting;
class PrivateChannel extends Channel
{
/**
* Create a new channel instance.
*
* @param string $name
* @return void
*/
public function __construct($name)
{
parent::__construct('private-'.$name);
}
}

View File

@@ -2,7 +2,7 @@
"name": "illuminate/broadcasting",
"description": "The Illuminate Broadcasting package.",
"license": "MIT",
"homepage": "http://laravel.com",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
@@ -14,9 +14,12 @@
}
],
"require": {
"php": ">=5.5.9",
"illuminate/contracts": "5.2.*",
"illuminate/support": "5.2.*"
"php": ">=7.0",
"psr/log": "~1.0",
"illuminate/bus": "5.5.*",
"illuminate/contracts": "5.5.*",
"illuminate/queue": "5.5.*",
"illuminate/support": "5.5.*"
},
"autoload": {
"psr-4": {
@@ -25,11 +28,14 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.2-dev"
"dev-master": "5.5-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
},
"minimum-stability": "dev"
}

View File

@@ -3,6 +3,9 @@
namespace Illuminate\Bus;
use Illuminate\Support\ServiceProvider;
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
{
@@ -20,18 +23,18 @@ class BusServiceProvider extends ServiceProvider
*/
public function register()
{
$this->app->singleton('Illuminate\Bus\Dispatcher', function ($app) {
$this->app->singleton(Dispatcher::class, function ($app) {
return new Dispatcher($app, function ($connection = null) use ($app) {
return $app['Illuminate\Contracts\Queue\Factory']->connection($connection);
return $app[QueueFactoryContract::class]->connection($connection);
});
});
$this->app->alias(
'Illuminate\Bus\Dispatcher', 'Illuminate\Contracts\Bus\Dispatcher'
Dispatcher::class, DispatcherContract::class
);
$this->app->alias(
'Illuminate\Bus\Dispatcher', 'Illuminate\Contracts\Bus\QueueingDispatcher'
Dispatcher::class, QueueingDispatcherContract::class
);
}
@@ -43,9 +46,9 @@ class BusServiceProvider extends ServiceProvider
public function provides()
{
return [
'Illuminate\Bus\Dispatcher',
'Illuminate\Contracts\Bus\Dispatcher',
'Illuminate\Contracts\Bus\QueueingDispatcher',
Dispatcher::class,
DispatcherContract::class,
QueueingDispatcherContract::class,
];
}
}

View File

@@ -33,6 +33,13 @@ class Dispatcher implements QueueingDispatcher
*/
protected $pipes = [];
/**
* The command to handler mapping for non-self-handling events.
*
* @var array
*/
protected $handlers = [];
/**
* The queue resolver callback.
*
@@ -64,22 +71,57 @@ class Dispatcher implements QueueingDispatcher
{
if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
return $this->dispatchToQueue($command);
} else {
return $this->dispatchNow($command);
}
return $this->dispatchNow($command);
}
/**
* Dispatch a command to its appropriate handler in the current process.
*
* @param mixed $command
* @param mixed $handler
* @return mixed
*/
public function dispatchNow($command)
public function dispatchNow($command, $handler = null)
{
return $this->pipeline->send($command)->through($this->pipes)->then(function ($command) {
return $this->container->call([$command, 'handle']);
});
if ($handler || $handler = $this->getCommandHandler($command)) {
$callback = function ($command) use ($handler) {
return $handler->handle($command);
};
} else {
$callback = function ($command) {
return $this->container->call([$command, 'handle']);
};
}
return $this->pipeline->send($command)->through($this->pipes)->then($callback);
}
/**
* Determine if the given command has a handler.
*
* @param mixed $command
* @return bool
*/
public function hasCommandHandler($command)
{
return array_key_exists(get_class($command), $this->handlers);
}
/**
* Retrieve the handler for a command.
*
* @param mixed $command
* @return bool|mixed
*/
public function getCommandHandler($command)
{
if ($this->hasCommandHandler($command)) {
return $this->container->make($this->handlers[get_class($command)]);
}
return false;
}
/**
@@ -103,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);
@@ -113,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);
}
/**
@@ -154,4 +196,17 @@ class Dispatcher implements QueueingDispatcher
return $this;
}
/**
* Map a command to a handler.
*
* @param array $map
* @return $this
*/
public function map(array $map)
{
$this->handlers = array_merge($this->handlers, $map);
return $this;
}
}

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 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

@@ -2,7 +2,7 @@
"name": "illuminate/bus",
"description": "The Illuminate Bus package.",
"license": "MIT",
"homepage": "http://laravel.com",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
@@ -14,10 +14,10 @@
}
],
"require": {
"php": ">=5.5.9",
"illuminate/contracts": "5.2.*",
"illuminate/pipeline": "5.2.*",
"illuminate/support": "5.2.*"
"php": ">=7.0",
"illuminate/contracts": "5.5.*",
"illuminate/pipeline": "5.5.*",
"illuminate/support": "5.5.*"
},
"autoload": {
"psr-4": {
@@ -26,8 +26,11 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.2-dev"
"dev-master": "5.5-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}

View File

@@ -55,12 +55,12 @@ class ApcStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function put($key, $value, $minutes)
{
$this->apc->put($this->prefix.$key, $value, $minutes * 60);
$this->apc->put($this->prefix.$key, $value, (int) ($minutes * 60));
}
/**
@@ -113,11 +113,11 @@ class ApcStore extends TaggableStore implements Store
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
$this->apc->flush();
return $this->apc->flush();
}
/**

View File

@@ -83,10 +83,10 @@ class ApcWrapper
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
$this->apcu ? apcu_clear_cache() : apc_clear_cache('user');
return $this->apcu ? apcu_clear_cache() : apc_clear_cache('user');
}
}

View File

@@ -33,7 +33,7 @@ class ArrayStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function put($key, $value, $minutes)
@@ -50,7 +50,8 @@ class ArrayStore extends TaggableStore implements Store
*/
public function increment($key, $value = 1)
{
$this->storage[$key] = ((int) $this->storage[$key]) + $value;
$this->storage[$key] = ! isset($this->storage[$key])
? $value : ((int) $this->storage[$key]) + $value;
return $this->storage[$key];
}
@@ -95,11 +96,13 @@ class ArrayStore extends TaggableStore implements Store
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
$this->storage = [];
return true;
}
/**

View File

@@ -3,11 +3,14 @@
namespace Illuminate\Cache;
use Closure;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Cache\Factory as FactoryContract;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
/**
* @mixin \Illuminate\Contracts\Cache\Repository
*/
class CacheManager implements FactoryContract
{
/**
@@ -46,7 +49,7 @@ class CacheManager implements FactoryContract
* Get a cache store instance by name.
*
* @param string|null $name
* @return mixed
* @return \Illuminate\Contracts\Cache\Repository
*/
public function store($name = null)
{
@@ -74,7 +77,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);
}
/**
@@ -161,7 +164,12 @@ class CacheManager implements FactoryContract
{
$prefix = $this->getPrefix($config);
$memcached = $this->app['memcached.connector']->connect($config['servers']);
$memcached = $this->app['memcached.connector']->connect(
$config['servers'],
$config['persistent_id'] ?? null,
$config['options'] ?? [],
array_filter($config['sasl'] ?? [])
);
return $this->repository(new MemcachedStore($memcached, $prefix));
}
@@ -186,7 +194,7 @@ class CacheManager implements FactoryContract
{
$redis = $this->app['redis'];
$connection = Arr::get($config, 'connection', 'default');
$connection = $config['connection'] ?? 'default';
return $this->repository(new RedisStore($redis, $this->getPrefix($config), $connection));
}
@@ -199,11 +207,11 @@ class CacheManager implements FactoryContract
*/
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)
)
);
}
@@ -218,9 +226,9 @@ class CacheManager implements FactoryContract
{
$repository = new Repository($store);
if ($this->app->bound('Illuminate\Contracts\Events\Dispatcher')) {
if ($this->app->bound(DispatcherContract::class)) {
$repository->setEventDispatcher(
$this->app['Illuminate\Contracts\Events\Dispatcher']
$this->app[DispatcherContract::class]
);
}
@@ -235,7 +243,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,7 +287,7 @@ class CacheManager implements FactoryContract
*/
public function extend($driver, Closure $callback)
{
$this->customCreators[$driver] = $callback;
$this->customCreators[$driver] = $callback->bindTo($this, $this);
return $this;
}
@@ -293,6 +301,6 @@ class CacheManager implements FactoryContract
*/
public function __call($method, $parameters)
{
return call_user_func_array([$this->store(), $method], $parameters);
return $this->store()->$method(...$parameters);
}
}

View File

@@ -3,7 +3,6 @@
namespace Illuminate\Cache;
use Illuminate\Support\ServiceProvider;
use Illuminate\Cache\Console\ClearCommand;
class CacheServiceProvider extends ServiceProvider
{
@@ -32,22 +31,6 @@ class CacheServiceProvider extends ServiceProvider
$this->app->singleton('memcached.connector', function () {
return new MemcachedConnector;
});
$this->registerCommands();
}
/**
* Register the cache related console commands.
*
* @return void
*/
public function registerCommands()
{
$this->app->singleton('command.cache.clear', function ($app) {
return new ClearCommand($app['cache']);
});
$this->commands('command.cache.clear');
}
/**
@@ -58,7 +41,7 @@ class CacheServiceProvider extends ServiceProvider
public function provides()
{
return [
'cache', 'cache.store', 'memcached.connector', 'command.cache.clear',
'cache', 'cache.store', 'memcached.connector',
];
}
}

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,21 +60,55 @@ class ClearCommand extends Command
*/
public function handle()
{
$tags = array_filter(explode(',', $this->option('tags')));
$this->laravel['events']->fire(
'cache:clearing', [$this->argument('store'), $this->tags()]
);
$cache = $this->cache->store($store = $this->argument('store'));
$this->cache()->flush();
$this->laravel['events']->fire('cache:clearing', [$store, $tags]);
$this->flushFacades();
if (! empty($tags)) {
$cache->tags($tags)->flush();
} else {
$cache->flush();
}
$this->laravel['events']->fire(
'cache:cleared', [$this->argument('store'), $this->tags()]
);
$this->info('Cache cleared successfully.');
}
$this->laravel['events']->fire('cache:cleared', [$store, $tags]);
/**
* Flush the real-time facades stored in the cache directory.
*
* @return void
*/
public function flushFacades()
{
foreach ($this->files->files(storage_path('framework/cache')) as $file) {
if (preg_match('/facade-.*\.php$/', $file)) {
$this->files->delete($file);
}
}
}
/**
* Get the cache instance for the command.
*
* @return \Illuminate\Cache\Repository
*/
protected function cache()
{
$cache = $this->cache->store($this->argument('store'));
return empty($this->tags()) ? $cache : $cache->tags($this->tags());
}
/**
* Get the tags passed to the command.
*
* @return array
*/
protected function tags()
{
return array_filter(explode(',', $this->option('tags')));
}
/**

View File

@@ -0,0 +1,57 @@
<?php
namespace Illuminate\Cache\Console;
use Illuminate\Console\Command;
use Illuminate\Cache\CacheManager;
class ForgetCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $signature = 'cache:forget {key : The key to remove} {store? : The store to remove the key from}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Remove an item from the cache';
/**
* The cache manager instance.
*
* @var \Illuminate\Cache\CacheManager
*/
protected $cache;
/**
* Create a new cache clear command instance.
*
* @param \Illuminate\Cache\CacheManager $cache
* @return void
*/
public function __construct(CacheManager $cache)
{
parent::__construct();
$this->cache = $cache;
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$this->cache->store($this->argument('store'))->forget(
$this->argument('key')
);
$this->info('The ['.$this->argument('key').'] key has been removed from the cache.');
}
}

View File

@@ -1,5 +1,6 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
@@ -14,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');
});
}
@@ -26,6 +27,6 @@ class CreateCacheTable extends Migration
*/
public function down()
{
Schema::drop('cache');
Schema::dropIfExists('cache');
}
}

View File

@@ -5,12 +5,12 @@ namespace Illuminate\Cache;
use Closure;
use Exception;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract;
class DatabaseStore implements Store
{
use RetrievesMultipleKeys;
use InteractsWithTime, RetrievesMultipleKeys;
/**
* The database connection instance.
@@ -19,13 +19,6 @@ class DatabaseStore implements Store
*/
protected $connection;
/**
* The encrypter instance.
*
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
protected $encrypter;
/**
* The name of the cache table.
*
@@ -44,16 +37,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;
}
@@ -72,19 +63,22 @@ class DatabaseStore implements Store
// If we have a cache record we will check the expiration time against current
// time on the system and see if the record has expired. If it has, we will
// remove the records from the database table so it isn't returned again.
if (! is_null($cache)) {
if (is_array($cache)) {
$cache = (object) $cache;
}
if (time() >= $cache->expiration) {
$this->forget($key);
return;
}
return $this->encrypter->decrypt($cache->value);
if (is_null($cache)) {
return;
}
$cache = is_array($cache) ? (object) $cache : $cache;
// 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 ($this->currentTime() >= $cache->expiration) {
$this->forget($key);
return;
}
return unserialize($cache->value);
}
/**
@@ -92,24 +86,21 @@ class DatabaseStore implements Store
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function put($key, $value, $minutes)
{
$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 = serialize($value);
$expiration = $this->getTime() + ($minutes * 60);
$expiration = $this->getTime() + (int) ($minutes * 60);
try {
$this->table()->insert(compact('key', 'value', 'expiration'));
} catch (Exception $e) {
$this->table()->where('key', '=', $key)->update(compact('value', 'expiration'));
$this->table()->where('key', $key)->update(compact('value', 'expiration'));
}
}
@@ -154,25 +145,34 @@ class DatabaseStore implements Store
return $this->connection->transaction(function () use ($key, $value, $callback) {
$prefixed = $this->prefix.$key;
$cache = $this->table()->where('key', $prefixed)->lockForUpdate()->first();
$cache = $this->table()->where('key', $prefixed)
->lockForUpdate()->first();
// If there is no value in the cache, we will return false here. Otherwise the
// value will be decrypted and we will proceed with this function to either
// increment or decrement this value based on the given action callbacks.
if (is_null($cache)) {
return false;
}
if (is_array($cache)) {
$cache = (object) $cache;
}
$cache = is_array($cache) ? (object) $cache : $cache;
$current = $this->encrypter->decrypt($cache->value);
$current = 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
// so we do not have to recreate all this logic in each of the functions.
$new = $callback((int) $current, $value);
if (! is_numeric($current)) {
return false;
}
// Here we will update the values in the table. We will also encrypt the value
// 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' => serialize($new),
]);
return $new;
@@ -186,7 +186,7 @@ class DatabaseStore implements Store
*/
protected function getTime()
{
return time();
return $this->currentTime();
}
/**
@@ -217,11 +217,11 @@ class DatabaseStore implements Store
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
$this->table()->delete();
return (bool) $this->table()->delete();
}
/**
@@ -244,16 +244,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.
*

View File

@@ -0,0 +1,46 @@
<?php
namespace Illuminate\Cache\Events;
abstract class CacheEvent
{
/**
* The key of the event.
*
* @var string
*/
public $key;
/**
* The tags that were assigned to the key.
*
* @var array
*/
public $tags;
/**
* Create a new event instance.
*
* @param string $key
* @param array $tags
* @return void
*/
public function __construct($key, array $tags = [])
{
$this->key = $key;
$this->tags = $tags;
}
/**
* Set the tags for the cache event.
*
* @param array $tags
* @return $this
*/
public function setTags($tags)
{
$this->tags = $tags;
return $this;
}
}

View File

@@ -2,15 +2,8 @@
namespace Illuminate\Cache\Events;
class CacheHit
class CacheHit extends CacheEvent
{
/**
* The key that was hit.
*
* @var string
*/
public $key;
/**
* The value that was retrieved.
*
@@ -18,13 +11,6 @@ class CacheHit
*/
public $value;
/**
* The tags that were assigned to the key.
*
* @var array
*/
public $tags;
/**
* Create a new event instance.
*
@@ -35,8 +21,8 @@ class CacheHit
*/
public function __construct($key, $value, array $tags = [])
{
$this->key = $key;
$this->tags = $tags;
parent::__construct($key, $tags);
$this->value = $value;
}
}

View File

@@ -2,32 +2,7 @@
namespace Illuminate\Cache\Events;
class CacheMissed
class CacheMissed extends CacheEvent
{
/**
* The key that was missed.
*
* @var string
*/
public $key;
/**
* The tags that were assigned to the key.
*
* @var array
*/
public $tags;
/**
* Create a new event instance.
*
* @param string $key
* @param array $tags
* @return void
*/
public function __construct($key, array $tags = [])
{
$this->key = $key;
$this->tags = $tags;
}
//
}

View File

@@ -2,32 +2,7 @@
namespace Illuminate\Cache\Events;
class KeyForgotten
class KeyForgotten extends CacheEvent
{
/**
* The key that was forgotten.
*
* @var string
*/
public $key;
/**
* The tags that were assigned to the key.
*
* @var array
*/
public $tags;
/**
* Create a new event instance.
*
* @param string $key
* @param array $tags
* @return void
*/
public function __construct($key, $tags = [])
{
$this->key = $key;
$this->tags = $tags;
}
//
}

View File

@@ -2,15 +2,8 @@
namespace Illuminate\Cache\Events;
class KeyWritten
class KeyWritten extends CacheEvent
{
/**
* The key that was written.
*
* @var string
*/
public $key;
/**
* The value that was written.
*
@@ -25,13 +18,6 @@ class KeyWritten
*/
public $minutes;
/**
* The tags that were assigned to the key.
*
* @var array
*/
public $tags;
/**
* Create a new event instance.
*
@@ -43,8 +29,8 @@ class KeyWritten
*/
public function __construct($key, $value, $minutes, $tags = [])
{
$this->key = $key;
$this->tags = $tags;
parent::__construct($key, $tags);
$this->value = $value;
$this->minutes = $minutes;
}

View File

@@ -3,13 +3,13 @@
namespace Illuminate\Cache;
use Exception;
use Illuminate\Support\Arr;
use Illuminate\Filesystem\Filesystem;
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.
@@ -46,47 +46,7 @@ class FileStore implements Store
*/
public function get($key)
{
return Arr::get($this->getPayload($key), 'data');
}
/**
* Retrieve an item and expiry time from the cache by key.
*
* @param string $key
* @return array
*/
protected function getPayload($key)
{
$path = $this->path($key);
// If the file doesn't exists, we obviously can't 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 {
$expire = substr(
$contents = $this->files->get($path, true), 0, 10
);
} catch (Exception $e) {
return ['data' => null, 'time' => null];
}
// 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 (time() >= $expire) {
$this->forget($key);
return ['data' => null, 'time' => null];
}
$data = unserialize(substr($contents, 10));
// Next, we'll extract the number of minutes 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 the cache. We'll round this out.
$time = ceil(($expire - time()) / 60);
return compact('data', 'time');
return $this->getPayload($key)['data'] ?? null;
}
/**
@@ -94,16 +54,16 @@ class FileStore implements Store
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function put($key, $value, $minutes)
{
$value = $this->expiration($minutes).serialize($value);
$this->ensureCacheDirectoryExists($path = $this->path($key));
$this->createCacheDirectory($path = $this->path($key));
$this->files->put($path, $value, true);
$this->files->put(
$path, $this->expiration($minutes).serialize($value), true
);
}
/**
@@ -112,7 +72,7 @@ class FileStore implements Store
* @param string $path
* @return void
*/
protected function createCacheDirectory($path)
protected function ensureCacheDirectoryExists($path)
{
if (! $this->files->exists(dirname($path))) {
$this->files->makeDirectory(dirname($path), 0777, true, true);
@@ -130,11 +90,9 @@ class FileStore implements Store
{
$raw = $this->getPayload($key);
$int = ((int) $raw['data']) + $value;
$this->put($key, $int, (int) $raw['time']);
return $int;
return tap(((int) $raw['data']) + $value, function ($newValue) use ($key, $raw) {
$this->put($key, $newValue, $raw['time']);
});
}
/**
@@ -169,9 +127,7 @@ class FileStore implements Store
*/
public function forget($key)
{
$file = $this->path($key);
if ($this->files->exists($file)) {
if ($this->files->exists($file = $this->path($key))) {
return $this->files->delete($file);
}
@@ -181,15 +137,71 @@ class FileStore implements Store
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
if ($this->files->isDirectory($this->directory)) {
foreach ($this->files->directories($this->directory) as $directory) {
$this->files->deleteDirectory($directory);
if (! $this->files->isDirectory($this->directory)) {
return false;
}
foreach ($this->files->directories($this->directory) as $directory) {
if (! $this->files->deleteDirectory($directory)) {
return false;
}
}
return true;
}
/**
* Retrieve an item and expiry time from the cache by key.
*
* @param string $key
* @return array
*/
protected function getPayload($key)
{
$path = $this->path($key);
// 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 {
$expire = substr(
$contents = $this->files->get($path, true), 0, 10
);
} catch (Exception $e) {
return $this->emptyPayload();
}
// 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 ($this->currentTime() >= $expire) {
$this->forget($key);
return $this->emptyPayload();
}
$data = unserialize(substr($contents, 10));
// Next, we'll extract the number of minutes 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 - $this->currentTime()) / 60;
return compact('data', 'time');
}
/**
* Get a default empty payload for the cache.
*
* @return array
*/
protected function emptyPayload()
{
return ['data' => null, 'time' => null];
}
/**
@@ -208,18 +220,14 @@ class FileStore implements Store
/**
* Get the expiration time based on the given minutes.
*
* @param int $minutes
* @param float|int $minutes
* @return int
*/
protected function expiration($minutes)
{
$time = time() + ($minutes * 60);
$time = $this->availableAt((int) ($minutes * 60));
if ($minutes === 0 || $time > 9999999999) {
return 9999999999;
}
return (int) $time;
return $minutes === 0 || $time > 9999999999 ? 9999999999 : (int) $time;
}
/**

View File

@@ -0,0 +1,93 @@
<?php
namespace Illuminate\Cache;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\LockTimeoutException;
abstract class Lock
{
use InteractsWithTime;
/**
* The name of the lock.
*
* @var string
*/
protected $name;
/**
* The number of seconds the lock should be maintained.
*
* @var int
*/
protected $seconds;
/**
* Create a new lock instance.
*
* @param string $name
* @param int $seconds
* @return void
*/
public function __construct($name, $seconds)
{
$this->name = $name;
$this->seconds = $seconds;
}
/**
* Attempt to acquire the lock.
*
* @return bool
*/
abstract public function acquire();
/**
* Attempt to acquire the lock.
*
* @param callable|null $callback
* @return bool
*/
public function get($callback = null)
{
$result = $this->acquire();
if ($result && is_callable($callback)) {
return tap($callback(), function () {
$this->release();
});
}
return $result;
}
/**
* Attempt to acquire the lock for the given number of seconds.
*
* @param int $seconds
* @param callable|null $callback
* @return bool
* @throws \Illuminate\Contracts\Cache\LockTimeoutException
*/
public function block($seconds, $callback = null)
{
$starting = $this->currentTime();
while (! $this->acquire()) {
usleep(250 * 1000);
if ($this->currentTime() - $seconds >= $starting) {
throw new LockTimeoutException;
}
}
if (is_callable($callback)) {
return tap($callback(), function () {
$this->release();
});
}
return true;
}
}

View File

@@ -3,7 +3,6 @@
namespace Illuminate\Cache;
use Memcached;
use RuntimeException;
class MemcachedConnector
{
@@ -11,31 +10,26 @@ class MemcachedConnector
* Create a new Memcached connection.
*
* @param array $servers
* @param string|null $connectionId
* @param array $options
* @param array $credentials
* @return \Memcached
*
* @throws \RuntimeException
*/
public function connect(array $servers)
public function connect(array $servers, $connectionId = null, array $options = [], array $credentials = [])
{
$memcached = $this->getMemcached();
$memcached = $this->getMemcached(
$connectionId, $credentials, $options
);
// For each server in the array, we'll just extract the configuration and add
// the server to the Memcached connection. Once we have added all of these
// servers we'll verify the connection is successful and return it back.
foreach ($servers as $server) {
$memcached->addServer(
$server['host'], $server['port'], $server['weight']
);
}
$memcachedStatus = $memcached->getVersion();
if (! is_array($memcachedStatus)) {
throw new RuntimeException('No Memcached servers added.');
}
if (in_array('255.255.255', $memcachedStatus) && count(array_unique($memcachedStatus)) === 1) {
throw new RuntimeException('Could not establish Memcached connection.');
if (! $memcached->getServerList()) {
// For each server in the array, we'll just extract the configuration and add
// the server to the Memcached connection. Once we have added all of these
// servers we'll verify the connection is successful and return it back.
foreach ($servers as $server) {
$memcached->addServer(
$server['host'], $server['port'], $server['weight']
);
}
}
return $memcached;
@@ -44,10 +38,50 @@ class MemcachedConnector
/**
* Get a new Memcached instance.
*
* @param string|null $connectionId
* @param array $credentials
* @param array $options
* @return \Memcached
*/
protected function getMemcached()
protected function getMemcached($connectionId, array $credentials, array $options)
{
return new Memcached;
$memcached = $this->createMemcachedInstance($connectionId);
if (count($credentials) == 2) {
$this->setCredentials($memcached, $credentials);
}
if (count($options)) {
$memcached->setOptions($options);
}
return $memcached;
}
/**
* Create the Memcached instance.
*
* @param string|null $connectionId
* @return \Memcached
*/
protected function createMemcachedInstance($connectionId)
{
return empty($connectionId) ? new Memcached : new Memcached($connectionId);
}
/**
* Set the SASL credentials on the Memcached connection.
*
* @param \Memcached $memcached
* @param array $credentials
* @return void
*/
protected function setCredentials($memcached, $credentials)
{
list($username, $password) = $credentials;
$memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
$memcached->setSaslAuthData($username, $password);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Lock as LockContract;
class MemcachedLock extends Lock implements LockContract
{
/**
* The Memcached instance.
*
* @var \Memcached
*/
protected $memcached;
/**
* Create a new lock instance.
*
* @param \Memcached $memcached
* @param string $name
* @param int $seconds
* @return void
*/
public function __construct($memcached, $name, $seconds)
{
parent::__construct($name, $seconds);
$this->memcached = $memcached;
}
/**
* Attempt to acquire the lock.
*
* @return bool
*/
public function acquire()
{
return $this->memcached->add(
$this->name, 1, $this->seconds
);
}
/**
* Release the lock.
*
* @return void
*/
public function release()
{
$this->memcached->delete($this->name);
}
}

View File

@@ -3,10 +3,15 @@
namespace Illuminate\Cache;
use Memcached;
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, Store
{
use InteractsWithTime;
/**
* The Memcached instance.
*
@@ -21,6 +26,13 @@ class MemcachedStore extends TaggableStore implements Store
*/
protected $prefix;
/**
* Indicates whether we are using Memcached version >= 3.0.0.
*
* @var bool
*/
protected $onVersionThree;
/**
* Create a new Memcached store.
*
@@ -32,12 +44,15 @@ class MemcachedStore extends TaggableStore implements Store
{
$this->setPrefix($prefix);
$this->memcached = $memcached;
$this->onVersionThree = (new ReflectionMethod('Memcached', 'getMulti'))
->getNumberOfParameters() == 2;
}
/**
* Retrieve an item from the cache by key.
*
* @param string|array $key
* @param string $key
* @return mixed
*/
public function get($key)
@@ -63,7 +78,13 @@ class MemcachedStore extends TaggableStore implements Store
return $this->prefix.$key;
}, $keys);
$values = $this->memcached->getMulti($prefixedKeys, null, Memcached::GET_PRESERVE_ORDER);
if ($this->onVersionThree) {
$values = $this->memcached->getMulti($prefixedKeys, Memcached::GET_PRESERVE_ORDER);
} else {
$null = null;
$values = $this->memcached->getMulti($prefixedKeys, $null, Memcached::GET_PRESERVE_ORDER);
}
if ($this->memcached->getResultCode() != 0) {
return array_fill_keys($keys, null);
@@ -77,19 +98,19 @@ class MemcachedStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function put($key, $value, $minutes)
{
$this->memcached->set($this->prefix.$key, $value, $minutes * 60);
$this->memcached->set($this->prefix.$key, $value, $this->toTimestamp($minutes));
}
/**
* Store multiple items in the cache for a given number of minutes.
*
* @param array $values
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function putMany(array $values, $minutes)
@@ -100,7 +121,7 @@ class MemcachedStore extends TaggableStore implements Store
$prefixedValues[$this->prefix.$key] = $value;
}
$this->memcached->setMulti($prefixedValues, $minutes * 60);
$this->memcached->setMulti($prefixedValues, $this->toTimestamp($minutes));
}
/**
@@ -108,12 +129,12 @@ class MemcachedStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param float|int $minutes
* @return bool
*/
public function add($key, $value, $minutes)
{
return $this->memcached->add($this->prefix.$key, $value, $minutes * 60);
return $this->memcached->add($this->prefix.$key, $value, $this->toTimestamp($minutes));
}
/**
@@ -152,6 +173,18 @@ class MemcachedStore extends TaggableStore implements Store
$this->put($key, $value, 0);
}
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0)
{
return new MemcachedLock($this->memcached, $this->prefix.$name, $seconds);
}
/**
* Remove an item from the cache.
*
@@ -166,11 +199,22 @@ class MemcachedStore extends TaggableStore implements Store
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
$this->memcached->flush();
return $this->memcached->flush();
}
/**
* Get the UNIX timestamp for the given number of minutes.
*
* @param int $minutes
* @return int
*/
protected function toTimestamp($minutes)
{
return $minutes > 0 ? $this->availableAt($minutes * 60) : 0;
}
/**

View File

@@ -31,7 +31,7 @@ class NullStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function put($key, $value, $minutes)
@@ -89,11 +89,11 @@ class NullStore extends TaggableStore implements Store
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
//
return true;
}
/**

View File

@@ -2,10 +2,13 @@
namespace Illuminate\Cache;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\Repository as Cache;
class RateLimiter
{
use InteractsWithTime;
/**
* The cache store implementation.
*
@@ -29,21 +32,17 @@ class RateLimiter
*
* @param string $key
* @param int $maxAttempts
* @param int $decayMinutes
* @param float|int $decayMinutes
* @return bool
*/
public function tooManyAttempts($key, $maxAttempts, $decayMinutes = 1)
{
if ($this->cache->has($key.':lockout')) {
return true;
}
if ($this->attempts($key) > $maxAttempts) {
$this->cache->add($key.':lockout', time() + ($decayMinutes * 60), $decayMinutes);
if ($this->attempts($key) >= $maxAttempts) {
if ($this->cache->has($key.':timer')) {
return true;
}
$this->resetAttempts($key);
return true;
}
return false;
@@ -53,14 +52,24 @@ class RateLimiter
* Increment the counter for a given key for a given decay time.
*
* @param string $key
* @param int $decayMinutes
* @param float|int $decayMinutes
* @return int
*/
public function hit($key, $decayMinutes = 1)
{
$this->cache->add($key, 1, $decayMinutes);
$this->cache->add(
$key.':timer', $this->availableAt($decayMinutes * 60), $decayMinutes
);
return (int) $this->cache->increment($key);
$added = $this->cache->add($key, 0, $decayMinutes);
$hits = (int) $this->cache->increment($key);
if (! $added && $hits == 1) {
$this->cache->put($key, 1, $decayMinutes);
}
return $hits;
}
/**
@@ -96,11 +105,11 @@ class RateLimiter
{
$attempts = $this->attempts($key);
return $attempts === 0 ? $maxAttempts : $maxAttempts - $attempts + 1;
return $maxAttempts - $attempts;
}
/**
* Clear the hits and lockout for the given key.
* Clear the hits and lockout timer for the given key.
*
* @param string $key
* @return void
@@ -109,7 +118,7 @@ class RateLimiter
{
$this->resetAttempts($key);
$this->cache->forget($key.':lockout');
$this->cache->forget($key.':timer');
}
/**
@@ -120,6 +129,6 @@ class RateLimiter
*/
public function availableIn($key)
{
return $this->cache->get($key.':lockout') - time();
return $this->cache->get($key.':timer') - $this->currentTime();
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Lock as LockContract;
class RedisLock extends Lock implements LockContract
{
/**
* The Redis factory implementation.
*
* @var \Illuminate\Redis\Connections\Connection
*/
protected $redis;
/**
* Create a new lock instance.
*
* @param \Illuminate\Redis\Connections\Connection $redis
* @param string $name
* @param int $seconds
* @return void
*/
public function __construct($redis, $name, $seconds)
{
parent::__construct($name, $seconds);
$this->redis = $redis;
}
/**
* Attempt to acquire the lock.
*
* @return bool
*/
public function acquire()
{
$result = $this->redis->setnx($this->name, 1);
if ($result === 1 && $this->seconds > 0) {
$this->redis->expire($this->name, $this->seconds);
}
return $result === 1;
}
/**
* Release the lock.
*
* @return void
*/
public function release()
{
$this->redis->del($this->name);
}
}

View File

@@ -3,14 +3,14 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Redis\Database as Redis;
use Illuminate\Contracts\Redis\Factory as Redis;
class RedisStore extends TaggableStore implements Store
{
/**
* The Redis database connection.
* The Redis factory implementation.
*
* @var \Illuminate\Redis\Database
* @var \Illuminate\Contracts\Redis\Factory
*/
protected $redis;
@@ -31,7 +31,7 @@ class RedisStore extends TaggableStore implements Store
/**
* Create a new Redis store.
*
* @param \Illuminate\Redis\Database $redis
* @param \Illuminate\Contracts\Redis\Factory $redis
* @param string $prefix
* @param string $connection
* @return void
@@ -40,7 +40,7 @@ class RedisStore extends TaggableStore implements Store
{
$this->redis = $redis;
$this->setPrefix($prefix);
$this->connection = $connection;
$this->setConnection($connection);
}
/**
@@ -51,9 +51,9 @@ class RedisStore extends TaggableStore implements Store
*/
public function get($key)
{
if (! is_null($value = $this->connection()->get($this->prefix.$key))) {
return is_numeric($value) ? $value : unserialize($value);
}
$value = $this->connection()->get($this->prefix.$key);
return ! is_null($value) ? $this->unserialize($value) : null;
}
/**
@@ -66,19 +66,17 @@ class RedisStore extends TaggableStore implements Store
*/
public function many(array $keys)
{
$return = [];
$results = [];
$prefixedKeys = array_map(function ($key) {
$values = $this->connection()->mget(array_map(function ($key) {
return $this->prefix.$key;
}, $keys);
$values = $this->connection()->mget($prefixedKeys);
}, $keys));
foreach ($values as $index => $value) {
$return[$keys[$index]] = is_numeric($value) ? $value : unserialize($value);
$results[$keys[$index]] = ! is_null($value) ? $this->unserialize($value) : null;
}
return $return;
return $results;
}
/**
@@ -86,21 +84,21 @@ class RedisStore extends TaggableStore implements Store
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function put($key, $value, $minutes)
{
$value = is_numeric($value) ? $value : serialize($value);
$this->connection()->setex($this->prefix.$key, (int) max(1, $minutes * 60), $value);
$this->connection()->setex(
$this->prefix.$key, (int) max(1, $minutes * 60), $this->serialize($value)
);
}
/**
* Store multiple items in the cache for a given number of minutes.
*
* @param array $values
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function putMany(array $values, $minutes)
@@ -114,6 +112,23 @@ class RedisStore extends TaggableStore implements Store
$this->connection()->exec();
}
/**
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return bool
*/
public function add($key, $value, $minutes)
{
$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)
);
}
/**
* Increment the value of an item in the cache.
*
@@ -147,9 +162,19 @@ class RedisStore extends TaggableStore implements Store
*/
public function forever($key, $value)
{
$value = is_numeric($value) ? $value : serialize($value);
$this->connection()->set($this->prefix.$key, $this->serialize($value));
}
$this->connection()->set($this->prefix.$key, $value);
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0)
{
return new RedisLock($this->connection(), $this->prefix.$name, $seconds);
}
/**
@@ -166,11 +191,13 @@ class RedisStore extends TaggableStore implements Store
/**
* Remove all items from the cache.
*
* @return void
* @return bool
*/
public function flush()
{
$this->connection()->flushdb();
return true;
}
/**
@@ -181,7 +208,9 @@ class RedisStore extends TaggableStore implements Store
*/
public function tags($names)
{
return new RedisTaggedCache($this, new TagSet($this, is_array($names) ? $names : func_get_args()));
return new RedisTaggedCache(
$this, new TagSet($this, is_array($names) ? $names : func_get_args())
);
}
/**
@@ -208,7 +237,7 @@ class RedisStore extends TaggableStore implements Store
/**
* Get the Redis database instance.
*
* @return \Illuminate\Redis\Database
* @return \Illuminate\Contracts\Redis\Factory
*/
public function getRedis()
{
@@ -235,4 +264,26 @@ class RedisStore extends TaggableStore implements Store
{
$this->prefix = ! empty($prefix) ? $prefix.':' : '';
}
/**
* Serialize the value.
*
* @param mixed $value
* @return mixed
*/
protected function serialize($value)
{
return is_numeric($value) ? $value : serialize($value);
}
/**
* Unserialize the value.
*
* @param mixed $value
* @return mixed
*/
protected function unserialize($value)
{
return is_numeric($value) ? $value : unserialize($value);
}
}

View File

@@ -22,7 +22,7 @@ class RedisTaggedCache extends TaggedCache
*
* @param string $key
* @param mixed $value
* @param \DateTime|int $minutes
* @param \DateTime|float|int $minutes
* @return void
*/
public function put($key, $value, $minutes = null)
@@ -93,7 +93,7 @@ class RedisTaggedCache extends TaggedCache
*/
protected function pushKeys($namespace, $key, $reference)
{
$fullKey = $this->getPrefix().sha1($namespace).':'.$key;
$fullKey = $this->store->getPrefix().sha1($namespace).':'.$key;
foreach (explode('|', $namespace) as $segment) {
$this->store->connection()->sadd($this->referenceKey($segment, $reference), $fullKey);
@@ -146,7 +146,9 @@ class RedisTaggedCache extends TaggedCache
$values = array_unique($this->store->connection()->smembers($referenceKey));
if (count($values) > 0) {
call_user_func_array([$this->store->connection(), 'del'], $values);
foreach (array_chunk($values, 1000) as $valuesChunk) {
call_user_func_array([$this->store->connection(), 'del'], $valuesChunk);
}
}
}
@@ -159,6 +161,6 @@ class RedisTaggedCache extends TaggedCache
*/
protected function referenceKey($segment, $suffix)
{
return $this->getPrefix().$segment.':'.$suffix;
return $this->store->getPrefix().$segment.':'.$suffix;
}
}

View File

@@ -3,17 +3,26 @@
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;
/**
* @mixin \Illuminate\Contracts\Cache\Store
*/
class Repository implements CacheContract, ArrayAccess
{
use InteractsWithTime;
use Macroable {
__call as macroCall;
}
@@ -35,7 +44,7 @@ class Repository implements CacheContract, ArrayAccess
/**
* The default number of minutes to store items.
*
* @var int
* @var float|int
*/
protected $default = 60;
@@ -50,58 +59,6 @@ class Repository implements CacheContract, ArrayAccess
$this->store = $store;
}
/**
* Set the event dispatcher instance.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function setEventDispatcher(Dispatcher $events)
{
$this->events = $events;
}
/**
* Fire an event for this cache instance.
*
* @param string $event
* @param array $payload
* @return void
*/
protected function fireCacheEvent($event, $payload)
{
if (! isset($this->events)) {
return;
}
switch ($event) {
case 'hit':
if (count($payload) == 2) {
$payload[] = [];
}
return $this->events->fire(new Events\CacheHit($payload[0], $payload[1], $payload[2]));
case 'missed':
if (count($payload) == 1) {
$payload[] = [];
}
return $this->events->fire(new Events\CacheMissed($payload[0], $payload[1]));
case 'delete':
if (count($payload) == 1) {
$payload[] = [];
}
return $this->events->fire(new Events\KeyForgotten($payload[0], $payload[1]));
case 'write':
if (count($payload) == 3) {
$payload[] = [];
}
return $this->events->fire(new Events\KeyWritten($payload[0], $payload[1], $payload[2], $payload[3]));
}
}
/**
* Determine if an item exists in the cache.
*
@@ -128,12 +85,15 @@ class Repository implements CacheContract, ArrayAccess
$value = $this->store->get($this->itemKey($key));
// If we could not find the cache value, we will fire the missed event and get
// the default value for this cache value. This default could be a callback
// so we will execute the value function which will resolve it if needed.
if (is_null($value)) {
$this->fireCacheEvent('missed', [$key]);
$this->event(new CacheMissed($key));
$value = value($default);
} else {
$this->fireCacheEvent('hit', [$key, $value]);
$this->event(new CacheHit($key, $value));
}
return $value;
@@ -149,25 +109,58 @@ class Repository implements CacheContract, ArrayAccess
*/
public function many(array $keys)
{
$normalizedKeys = [];
$values = $this->store->many(collect($keys)->map(function ($value, $key) {
return is_string($key) ? $key : $value;
})->values()->all());
foreach ($keys as $key => $value) {
$normalizedKeys[] = is_string($key) ? $key : $value;
return collect($values)->map(function ($value, $key) use ($keys) {
return $this->handleManyResult($keys, $key, $value);
})->all();
}
/**
* {@inheritdoc}
*/
public function getMultiple($keys, $default = null)
{
if (is_null($default)) {
return $this->many($keys);
}
$values = $this->store->many($normalizedKeys);
foreach ($values as $key => &$value) {
if (is_null($value)) {
$this->fireCacheEvent('missed', [$key]);
$value = isset($keys[$key]) ? value($keys[$key]) : null;
} else {
$this->fireCacheEvent('hit', [$key, $value]);
foreach ($keys as $key) {
if (! isset($default[$key])) {
$default[$key] = null;
}
}
return $values;
return $this->many($default);
}
/**
* Handle a result for the "many" method.
*
* @param array $keys
* @param string $key
* @param mixed $value
* @return mixed
*/
protected function handleManyResult($keys, $key, $value)
{
// If we could not find the cache value, we will fire the missed event and get
// the default value for this cache value. This default could be a callback
// so we will execute the value function which will resolve it if needed.
if (is_null($value)) {
$this->event(new CacheMissed($key));
return isset($keys[$key]) ? value($keys[$key]) : null;
}
// If we found a valid value we will fire the "hit" event and return the value
// back from this function. The "hit" event gives developers an opportunity
// to listen for every possible cache "hit" throughout this applications.
$this->event(new CacheHit($key, $value));
return $value;
}
/**
@@ -179,11 +172,9 @@ class Repository implements CacheContract, ArrayAccess
*/
public function pull($key, $default = null)
{
$value = $this->get($key, $default);
$this->forget($key);
return $value;
return tap($this->get($key, $default), function ($value) use ($key) {
$this->forget($key);
});
}
/**
@@ -191,64 +182,82 @@ class Repository implements CacheContract, ArrayAccess
*
* @param string $key
* @param mixed $value
* @param \DateTime|int $minutes
* @param \DateTimeInterface|\DateInterval|float|int $minutes
* @return void
*/
public function put($key, $value, $minutes = null)
{
if (is_array($key) && filter_var($value, FILTER_VALIDATE_INT) !== false) {
if (is_array($key)) {
return $this->putMany($key, $value);
}
$minutes = $this->getMinutes($minutes);
if (! is_null($minutes)) {
if (! is_null($minutes = $this->getMinutes($minutes))) {
$this->store->put($this->itemKey($key), $value, $minutes);
$this->fireCacheEvent('write', [$key, $value, $minutes]);
$this->event(new KeyWritten($key, $value, $minutes));
}
}
/**
* {@inheritdoc}
*/
public function set($key, $value, $ttl = null)
{
$this->put($key, $value, $ttl);
}
/**
* Store multiple items in the cache for a given number of minutes.
*
* @param array $values
* @param int $minutes
* @param \DateTimeInterface|\DateInterval|float|int $minutes
* @return void
*/
public function putMany(array $values, $minutes)
{
$minutes = $this->getMinutes($minutes);
if (! is_null($minutes)) {
if (! is_null($minutes = $this->getMinutes($minutes))) {
$this->store->putMany($values, $minutes);
foreach ($values as $key => $value) {
$this->fireCacheEvent('write', [$key, $value, $minutes]);
$this->event(new KeyWritten($key, $value, $minutes));
}
}
}
/**
* {@inheritdoc}
*/
public function setMultiple($values, $ttl = null)
{
$this->putMany($values, $ttl);
}
/**
* Store an item in the cache if the key does not exist.
*
* @param string $key
* @param mixed $value
* @param \DateTime|int $minutes
* @param \DateTimeInterface|\DateInterval|float|int $minutes
* @return bool
*/
public function add($key, $value, $minutes)
{
$minutes = $this->getMinutes($minutes);
if (is_null($minutes)) {
if (is_null($minutes = $this->getMinutes($minutes))) {
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);
return $this->store->add(
$this->itemKey($key), $value, $minutes
);
}
// 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);
@@ -258,6 +267,30 @@ class Repository implements CacheContract, ArrayAccess
return false;
}
/**
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return int|bool
*/
public function increment($key, $value = 1)
{
return $this->store->increment($key, $value);
}
/**
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return int|bool
*/
public function decrement($key, $value = 1)
{
return $this->store->decrement($key, $value);
}
/**
* Store an item in the cache indefinitely.
*
@@ -269,23 +302,25 @@ class Repository implements CacheContract, ArrayAccess
{
$this->store->forever($this->itemKey($key), $value);
$this->fireCacheEvent('write', [$key, $value, 0]);
$this->event(new KeyWritten($key, $value, 0));
}
/**
* Get an item from the cache, or store the default value.
*
* @param string $key
* @param \DateTime|int $minutes
* @param \DateTimeInterface|\DateInterval|float|int $minutes
* @param \Closure $callback
* @return mixed
*/
public function remember($key, $minutes, Closure $callback)
{
// If the item exists in the cache we will just return this immediately
// otherwise we will execute the given Closure and cache the result
// of that execution for the given number of minutes in storage.
if (! is_null($value = $this->get($key))) {
$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 (! is_null($value)) {
return $value;
}
@@ -315,10 +350,12 @@ class Repository implements CacheContract, ArrayAccess
*/
public function rememberForever($key, Closure $callback)
{
// If the item exists in the cache we will just return this immediately
// otherwise we will execute the given Closure and cache the result
// of that execution for the given number of minutes. It's easy.
if (! is_null($value = $this->get($key))) {
$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 (! is_null($value)) {
return $value;
}
@@ -330,16 +367,42 @@ class Repository implements CacheContract, ArrayAccess
/**
* Remove an item from the cache.
*
* @param string $key
* @param string $key
* @return bool
*/
public function forget($key)
{
$success = $this->store->forget($this->itemKey($key));
return tap($this->store->forget($this->itemKey($key)), function () use ($key) {
$this->event(new KeyForgotten($key));
});
}
$this->fireCacheEvent('delete', [$key]);
/**
* {@inheritdoc}
*/
public function delete($key)
{
return $this->forget($key);
}
return $success;
/**
* {@inheritdoc}
*/
public function deleteMultiple($keys)
{
foreach ($keys as $key) {
$this->forget($key);
}
return true;
}
/**
* {@inheritdoc}
*/
public function clear()
{
return $this->store->flush();
}
/**
@@ -352,19 +415,17 @@ class Repository implements CacheContract, ArrayAccess
*/
public function tags($names)
{
if (method_exists($this->store, 'tags')) {
$taggedCache = $this->store->tags($names);
if (! is_null($this->events)) {
$taggedCache->setEventDispatcher($this->events);
}
$taggedCache->setDefaultCacheTime($this->default);
return $taggedCache;
if (! method_exists($this->store, 'tags')) {
throw new BadMethodCallException('This cache store does not support tagging.');
}
throw new BadMethodCallException('This cache store does not support tagging.');
$cache = $this->store->tags($names);
if (! is_null($this->events)) {
$cache->setEventDispatcher($this->events);
}
return $cache->setDefaultCacheTime($this->default);
}
/**
@@ -381,7 +442,7 @@ class Repository implements CacheContract, ArrayAccess
/**
* Get the default cache time.
*
* @return int
* @return float|int
*/
public function getDefaultCacheTime()
{
@@ -391,12 +452,14 @@ class Repository implements CacheContract, ArrayAccess
/**
* Set the default cache time in minutes.
*
* @param int $minutes
* @return void
* @param float|int $minutes
* @return $this
*/
public function setDefaultCacheTime($minutes)
{
$this->default = $minutes;
return $this;
}
/**
@@ -409,6 +472,30 @@ class Repository implements CacheContract, ArrayAccess
return $this->store;
}
/**
* Fire an event for this cache instance.
*
* @param string $event
* @return void
*/
protected function event($event)
{
if (isset($this->events)) {
$this->events->dispatch($event);
}
}
/**
* Set the event dispatcher instance.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function setEventDispatcher(Dispatcher $events)
{
$this->events = $events;
}
/**
* Determine if a cached value exists.
*
@@ -457,18 +544,18 @@ class Repository implements CacheContract, ArrayAccess
/**
* Calculate the number of minutes with the given duration.
*
* @param \DateTime|int $duration
* @return int|null
* @param \DateTimeInterface|\DateInterval|float|int $duration
* @return float|int|null
*/
protected function getMinutes($duration)
{
if ($duration instanceof DateTime) {
$fromNow = Carbon::now()->diffInMinutes(Carbon::instance($duration), false);
$duration = $this->parseDateInterval($duration);
return $fromNow > 0 ? $fromNow : null;
if ($duration instanceof DateTimeInterface) {
$duration = Carbon::now()->diffInSeconds(Carbon::createFromTimestamp($duration->getTimestamp()), false) / 60;
}
return is_string($duration) ? (int) $duration : $duration;
return (int) ($duration * 60) > 0 ? $duration : null;
}
/**
@@ -484,7 +571,7 @@ class Repository implements CacheContract, ArrayAccess
return $this->macroCall($method, $parameters);
}
return call_user_func_array([$this->store, $method], $parameters);
return $this->store->$method(...$parameters);
}
/**

View File

@@ -27,7 +27,7 @@ trait RetrievesMultipleKeys
* Store multiple items in the cache for a given number of minutes.
*
* @param array $values
* @param int $minutes
* @param float|int $minutes
* @return void
*/
public function putMany(array $values, $minutes)

View File

@@ -44,24 +44,16 @@ class TagSet
}
/**
* Get the unique tag identifier for a given tag.
* Reset the tag and return the new tag identifier.
*
* @param string $name
* @return string
*/
public function tagId($name)
public function resetTag($name)
{
return $this->store->get($this->tagKey($name)) ?: $this->resetTag($name);
}
$this->store->forever($this->tagKey($name), $id = str_replace('.', '', uniqid('', true)));
/**
* Get an array of tag identifiers for all of the tags in the set.
*
* @return array
*/
protected function tagIds()
{
return array_map([$this, 'tagId'], $this->names);
return $id;
}
/**
@@ -75,16 +67,24 @@ class TagSet
}
/**
* Reset the tag and return the new tag identifier.
* Get an array of tag identifiers for all of the tags in the set.
*
* @return array
*/
protected function tagIds()
{
return array_map([$this, 'tagId'], $this->names);
}
/**
* Get the unique tag identifier for a given tag.
*
* @param string $name
* @return string
*/
public function resetTag($name)
public function tagId($name)
{
$this->store->forever($this->tagKey($name), $id = str_replace('.', '', uniqid('', true)));
return $id;
return $this->store->get($this->tagKey($name)) ?: $this->resetTag($name);
}
/**

View File

@@ -29,16 +29,6 @@ class TaggedCache extends Repository
$this->tags = $tags;
}
/**
* {@inheritdoc}
*/
protected function fireCacheEvent($event, $payload)
{
$payload[] = $this->tags->getNames();
parent::fireCacheEvent($event, $payload);
}
/**
* Increment the value of an item in the cache.
*
@@ -91,4 +81,15 @@ class TaggedCache extends Repository
{
return sha1($this->tags->getNamespace()).':'.$key;
}
/**
* Fire an event for this cache instance.
*
* @param string $event
* @return void
*/
protected function event($event)
{
parent::event($event->setTags($this->tags->getNames()));
}
}

View File

@@ -2,7 +2,7 @@
"name": "illuminate/cache",
"description": "The Illuminate Cache package.",
"license": "MIT",
"homepage": "http://laravel.com",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
@@ -14,10 +14,9 @@
}
],
"require": {
"php": ">=5.5.9",
"illuminate/contracts": "5.2.*",
"illuminate/support": "5.2.*",
"nesbot/carbon": "~1.20"
"php": ">=7.0",
"illuminate/contracts": "5.5.*",
"illuminate/support": "5.5.*"
},
"autoload": {
"psr-4": {
@@ -26,13 +25,16 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.2-dev"
"dev-master": "5.5-dev"
}
},
"suggest": {
"illuminate/database": "Required to use the database cache driver (5.2.*).",
"illuminate/filesystem": "Required to use the file cache driver (5.2.*).",
"illuminate/redis": "Required to use the redis cache driver (5.2.*)."
"illuminate/database": "Required to use the database cache driver (5.5.*).",
"illuminate/filesystem": "Required to use the file cache driver (5.5.*).",
"illuminate/redis": "Required to use the redis cache driver (5.5.*)."
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}

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)) {
list($key, $default) = [$default, null];
}
$config[$key] = Arr::get($this->items, $key, $default);
}
return $config;
}
/**
* Set a given configuration value.
*
@@ -58,11 +83,9 @@ class Repository implements ArrayAccess, ConfigContract
*/
public function set($key, $value = null)
{
if (is_array($key)) {
foreach ($key as $innerKey => $innerValue) {
Arr::set($this->items, $innerKey, $innerValue);
}
} else {
$keys = is_array($key) ? $key : [$key => $value];
foreach ($keys as $key => $value) {
Arr::set($this->items, $key, $value);
}
}

View File

@@ -2,7 +2,7 @@
"name": "illuminate/config",
"description": "The Illuminate Config package.",
"license": "MIT",
"homepage": "http://laravel.com",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
@@ -14,10 +14,9 @@
}
],
"require": {
"php": ">=5.5.9",
"illuminate/contracts": "5.2.*",
"illuminate/filesystem": "5.2.*",
"illuminate/support": "5.2.*"
"php": ">=7.0",
"illuminate/contracts": "5.5.*",
"illuminate/support": "5.5.*"
},
"autoload": {
"psr-4": {
@@ -26,8 +25,11 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.2-dev"
"dev-master": "5.5-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}

View File

@@ -2,11 +2,18 @@
namespace Illuminate\Console;
use Closure;
use Illuminate\Support\ProcessUtils;
use Illuminate\Contracts\Events\Dispatcher;
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\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 Illuminate\Contracts\Console\Application as ApplicationContract;
@@ -27,6 +34,20 @@ class Application extends SymfonyApplication implements ApplicationContract
*/
protected $lastOutput;
/**
* The console application bootstrappers.
*
* @var array
*/
protected static $bootstrappers = [];
/**
* The Event Dispatcher.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events;
/**
* Create a new Artisan console application.
*
@@ -40,10 +61,101 @@ class Application extends SymfonyApplication implements ApplicationContract
parent::__construct('Laravel Framework', $version);
$this->laravel = $laravel;
$this->events = $events;
$this->setAutoExit(false);
$this->setCatchExceptions(false);
$events->fire(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->fire(
new Events\CommandStarting(
$commandName, $input, $output = $output ?: new ConsoleOutput
)
);
$exitCode = parent::run($input, $output);
$this->events->fire(
new Events\CommandFinished($commandName, $input, $output, $exitCode)
);
return $exitCode;
}
/**
* Determine the proper PHP executable.
*
* @return string
*/
public static function phpBinary()
{
return ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false));
}
/**
* Determine the proper Artisan executable.
*
* @return string
*/
public static function artisanBinary()
{
return defined('ARTISAN_BINARY') ? ProcessUtils::escapeArgument(ARTISAN_BINARY) : 'artisan';
}
/**
* Format the given command as a fully-qualified executable command.
*
* @param string $string
* @return string
*/
public static function formatCommandString($string)
{
return sprintf('%s %s %s', static::phpBinary(), static::artisanBinary(), $string);
}
/**
* Register a console "starting" bootstrapper.
*
* @param \Closure $callback
* @return void
*/
public static function starting(Closure $callback)
{
static::$bootstrappers[] = $callback;
}
/**
* Bootstrap the console application.
*
* @return void
*/
protected function bootstrap()
{
foreach (static::$bootstrappers as $bootstrapper) {
$bootstrapper($this);
}
}
/**
* Clear the console application bootstrappers.
*
* @return void
*/
public static function forgetBootstrappers()
{
static::$bootstrappers = [];
}
/**
@@ -51,13 +163,14 @@ class Application extends SymfonyApplication implements ApplicationContract
*
* @param string $command
* @param array $parameters
* @param \Symfony\Component\Console\Output\OutputInterface $outputBuffer
* @return int
*/
public function call($command, array $parameters = [])
public function call($command, array $parameters = [], $outputBuffer = null)
{
$parameters = collect($parameters)->prepend($command);
$this->lastOutput = new BufferedOutput;
$this->lastOutput = $outputBuffer ?: new BufferedOutput;
$this->setCatchExceptions(false);
@@ -141,11 +254,9 @@ class Application extends SymfonyApplication implements ApplicationContract
*/
protected function getDefaultInputDefinition()
{
$definition = parent::getDefaultInputDefinition();
$definition->addOption($this->getEnvironmentOption());
return $definition;
return tap(parent::getDefaultInputDefinition(), function ($definition) {
$definition->addOption($this->getEnvironmentOption());
});
}
/**
@@ -155,7 +266,7 @@ class Application extends SymfonyApplication implements ApplicationContract
*/
protected function getEnvironmentOption()
{
$message = 'The environment the command should run under.';
$message = 'The environment the command should run under';
return new InputOption('--env', null, InputOption::VALUE_OPTIONAL, $message);
}

View File

@@ -2,6 +2,7 @@
namespace Illuminate\Console;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Support\Arrayable;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\ArrayInput;
@@ -15,6 +16,8 @@ use Symfony\Component\Console\Command\Command as SymfonyCommand;
class Command extends SymfonyCommand
{
use Macroable;
/**
* The Laravel application instance.
*
@@ -57,6 +60,13 @@ class Command extends SymfonyCommand
*/
protected $description;
/**
* Indicates whether the command should be shown in the Artisan command list.
*
* @var bool
*/
protected $hidden = false;
/**
* The default verbosity of output commands.
*
@@ -70,10 +80,10 @@ class Command extends SymfonyCommand
* @var array
*/
protected $verbosityMap = [
'v' => OutputInterface::VERBOSITY_VERBOSE,
'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE,
'vvv' => OutputInterface::VERBOSITY_DEBUG,
'quiet' => OutputInterface::VERBOSITY_QUIET,
'v' => OutputInterface::VERBOSITY_VERBOSE,
'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE,
'vvv' => OutputInterface::VERBOSITY_DEBUG,
'quiet' => OutputInterface::VERBOSITY_QUIET,
'normal' => OutputInterface::VERBOSITY_NORMAL,
];
@@ -93,8 +103,13 @@ class Command extends SymfonyCommand
parent::__construct($this->name);
}
// Once we have constructed the command, we'll set the description and other
// related properties of the command. If a signature wasn't used to build
// the command we'll set the arguments and the options on this command.
$this->setDescription($this->description);
$this->setHidden($this->hidden);
if (! isset($this->signature)) {
$this->specifyParameters();
}
@@ -109,8 +124,11 @@ class Command extends SymfonyCommand
{
list($name, $arguments, $options) = Parser::parse($this->signature);
parent::__construct($name);
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);
}
@@ -148,11 +166,9 @@ class Command extends SymfonyCommand
*/
public function run(InputInterface $input, OutputInterface $output)
{
$this->input = $input;
$this->output = new OutputStyle($input, $output);
return parent::run($input, $output);
return parent::run(
$this->input = $input, $this->output = new OutputStyle($input, $output)
);
}
/**
@@ -164,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']);
}
/**
@@ -178,11 +192,11 @@ class Command extends SymfonyCommand
*/
public function call($command, array $arguments = [])
{
$instance = $this->getApplication()->find($command);
$arguments['command'] = $command;
return $instance->run(new ArrayInput($arguments), $this->output);
return $this->getApplication()->find($command)->run(
$this->createInputFromArguments($arguments), $this->output
);
}
/**
@@ -194,11 +208,26 @@ class Command extends SymfonyCommand
*/
public function callSilent($command, array $arguments = [])
{
$instance = $this->getApplication()->find($command);
$arguments['command'] = $command;
return $instance->run(new ArrayInput($arguments), new NullOutput);
return $this->getApplication()->find($command)->run(
$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($arguments), function ($input) {
if ($input->hasParameterOption(['--no-interaction'], true)) {
$input->setInteractive(false);
}
});
}
/**
@@ -215,7 +244,7 @@ class Command extends SymfonyCommand
/**
* Get the value of a command argument.
*
* @param string $key
* @param string|null $key
* @return string|array
*/
public function argument($key = null)
@@ -227,6 +256,16 @@ class Command extends SymfonyCommand
return $this->input->getArgument($key);
}
/**
* Get all of the arguments passed to the command.
*
* @return array
*/
public function arguments()
{
return $this->argument();
}
/**
* Determine if the given option is present.
*
@@ -253,6 +292,16 @@ class Command extends SymfonyCommand
return $this->input->getOption($key);
}
/**
* Get all of the options passed to the command.
*
* @return array
*/
public function options()
{
return $this->option();
}
/**
* Confirm a question with the user.
*
@@ -347,10 +396,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);
@@ -358,7 +408,13 @@ 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();
}
/**
@@ -442,6 +498,32 @@ class Command extends SymfonyCommand
$this->line($string, 'warning', $verbosity);
}
/**
* Write a string in an alert box.
*
* @param string $string
* @return void
*/
public function alert($string)
{
$this->comment(str_repeat('*', strlen($string) + 12));
$this->comment('* '.$string.' *');
$this->comment(str_repeat('*', strlen($string) + 12));
$this->output->newLine();
}
/**
* Set the verbosity level.
*
* @param string|int $level
* @return void
*/
protected function setVerbosity($level)
{
$this->verbosity = $this->parseVerbosity($level);
}
/**
* Get the verbosity level in terms of Symfony's OutputInterface level.
*
@@ -459,17 +541,6 @@ class Command extends SymfonyCommand
return $level;
}
/**
* Set the verbosity level.
*
* @param string|int $level
* @return void
*/
protected function setVerbosity($level)
{
$this->verbosity = $this->parseVerbosity($level);
}
/**
* Get the console command arguments.
*

View File

@@ -9,7 +9,9 @@ trait ConfirmableTrait
/**
* Confirm before proceeding with the action.
*
* @param string $warning
* This method only asks for confirmation in production.
*
* @param string $warning
* @param \Closure|bool|null $callback
* @return bool
*/
@@ -24,10 +26,7 @@ trait ConfirmableTrait
return true;
}
$this->comment(str_repeat('*', strlen($warning) + 12));
$this->comment('* '.$warning.' *');
$this->comment(str_repeat('*', strlen($warning) + 12));
$this->output->writeln('');
$this->alert($warning);
$confirmed = $this->confirm('Do you really wish to run this command?');

View File

@@ -4,7 +4,7 @@ namespace Illuminate\Console;
use Illuminate\Container\Container;
trait AppNamespaceDetectorTrait
trait DetectsApplicationNamespace
{
/**
* Get the application namespace.

View File

@@ -0,0 +1,54 @@
<?php
namespace Illuminate\Console\Events;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CommandFinished
{
/**
* The command name.
*
* @var string
*/
public $command;
/**
* The console input implementation.
*
* @var \Symfony\Component\Console\Input\InputInterface|null
*/
public $input;
/**
* The command output implementation.
*
* @var \Symfony\Component\Console\Output\OutputInterface|null
*/
public $output;
/**
* The command exit code.
*
* @var int
*/
public $exitCode;
/**
* Create a new event instance.
*
* @param string $command
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @param int $exitCode
* @return void
*/
public function __construct($command, InputInterface $input, OutputInterface $output, $exitCode)
{
$this->input = $input;
$this->output = $output;
$this->command = $command;
$this->exitCode = $exitCode;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Illuminate\Console\Events;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CommandStarting
{
/**
* The command name.
*
* @var string
*/
public $command;
/**
* The console input implementation.
*
* @var \Symfony\Component\Console\Input\InputInterface|null
*/
public $input;
/**
* The command output implementation.
*
* @var \Symfony\Component\Console\Output\OutputInterface|null
*/
public $output;
/**
* Create a new event instance.
*
* @param string $command
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return void
*/
public function __construct($command, InputInterface $input, OutputInterface $output)
{
$this->input = $input;
$this->output = $output;
$this->command = $command;
}
}

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