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

@@ -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;
}
}

View File

@@ -47,18 +47,24 @@ abstract class GeneratorCommand extends Command
*
* @return bool|null
*/
public function fire()
public function handle()
{
$name = $this->parseName($this->getNameInput());
$name = $this->qualifyClass($this->getNameInput());
$path = $this->getPath($name);
if ($this->alreadyExists($this->getNameInput())) {
// First we will check to see if the class already exists. If it does, we don't want
// to create the class and overwrite the user's code. So, we will bail out so the
// code is untouched. Otherwise, we will continue generating this class' files.
if ((! $this->hasOption('force') || ! $this->option('force')) && $this->alreadyExists($this->getNameInput())) {
$this->error($this->type.' already exists!');
return false;
}
// Next, we will generate the path to the location where this class' file should get
// written. Then, we will build the class and make the proper replacements on the
// stub files so that it gets the correctly formatted namespace and class name.
$this->makeDirectory($path);
$this->files->put($path, $this->buildClass($name));
@@ -67,50 +73,26 @@ abstract class GeneratorCommand extends Command
}
/**
* Determine if the class already exists.
*
* @param string $rawName
* @return bool
*/
protected function alreadyExists($rawName)
{
$name = $this->parseName($rawName);
return $this->files->exists($this->getPath($name));
}
/**
* Get the destination class path.
* Parse the class name and format according to the root namespace.
*
* @param string $name
* @return string
*/
protected function getPath($name)
protected function qualifyClass($name)
{
$name = str_replace($this->laravel->getNamespace(), '', $name);
$name = ltrim($name, '\\/');
return $this->laravel['path'].'/'.str_replace('\\', '/', $name).'.php';
}
/**
* Parse the name and format according to the root namespace.
*
* @param string $name
* @return string
*/
protected function parseName($name)
{
$rootNamespace = $this->laravel->getNamespace();
$rootNamespace = $this->rootNamespace();
if (Str::startsWith($name, $rootNamespace)) {
return $name;
}
if (Str::contains($name, '/')) {
$name = str_replace('/', '\\', $name);
}
$name = str_replace('/', '\\', $name);
return $this->parseName($this->getDefaultNamespace(trim($rootNamespace, '\\')).'\\'.$name);
return $this->qualifyClass(
$this->getDefaultNamespace(trim($rootNamespace, '\\')).'\\'.$name
);
}
/**
@@ -124,6 +106,30 @@ abstract class GeneratorCommand extends Command
return $rootNamespace;
}
/**
* Determine if the class already exists.
*
* @param string $rawName
* @return bool
*/
protected function alreadyExists($rawName)
{
return $this->files->exists($this->getPath($this->qualifyClass($rawName)));
}
/**
* Get the destination class path.
*
* @param string $name
* @return string
*/
protected function getPath($name)
{
$name = Str::replaceFirst($this->rootNamespace(), '', $name);
return $this->laravel['path'].'/'.str_replace('\\', '/', $name).'.php';
}
/**
* Build the directory for the class if necessary.
*
@@ -135,6 +141,8 @@ abstract class GeneratorCommand extends Command
if (! $this->files->isDirectory(dirname($path))) {
$this->files->makeDirectory(dirname($path), 0777, true, true);
}
return $path;
}
/**
@@ -160,18 +168,16 @@ abstract class GeneratorCommand extends Command
protected function replaceNamespace(&$stub, $name)
{
$stub = str_replace(
'DummyNamespace', $this->getNamespace($name), $stub
);
$stub = str_replace(
'DummyRootNamespace', $this->laravel->getNamespace(), $stub
['DummyNamespace', 'DummyRootNamespace', 'NamespacedDummyUserModel'],
[$this->getNamespace($name), $this->rootNamespace(), config('auth.providers.users.model')],
$stub
);
return $this;
}
/**
* Get the full namespace name for a given class.
* Get the full namespace for a given class, without the class name.
*
* @param string $name
* @return string
@@ -205,6 +211,16 @@ abstract class GeneratorCommand extends Command
return trim($this->argument('name'));
}
/**
* Get the root namespace for the class.
*
* @return string
*/
protected function rootNamespace()
{
return $this->laravel->getNamespace();
}
/**
* Get the console command arguments.
*

View File

@@ -18,28 +18,35 @@ class Parser
* @throws \InvalidArgumentException
*/
public static function parse($expression)
{
$name = static::name($expression);
if (preg_match_all('/\{\s*(.*?)\s*\}/', $expression, $matches)) {
if (count($matches[1])) {
return array_merge([$name], static::parameters($matches[1]));
}
}
return [$name, [], []];
}
/**
* Extract the name of the command from the expression.
*
* @param string $expression
* @return string
*/
protected static function name($expression)
{
if (trim($expression) === '') {
throw new InvalidArgumentException('Console command definition is empty.');
}
preg_match('/[^\s]+/', $expression, $matches);
if (isset($matches[0])) {
$name = $matches[0];
} else {
if (! preg_match('/[^\s]+/', $expression, $matches)) {
throw new InvalidArgumentException('Unable to determine command name from signature.');
}
preg_match_all('/\{\s*(.*?)\s*\}/', $expression, $matches);
$tokens = isset($matches[1]) ? $matches[1] : [];
if (count($tokens)) {
return array_merge([$name], static::parameters($tokens));
}
return [$name, [], []];
return $matches[0];
}
/**
@@ -55,10 +62,10 @@ class Parser
$options = [];
foreach ($tokens as $token) {
if (! Str::startsWith($token, '--')) {
$arguments[] = static::parseArgument($token);
if (preg_match('/-{2,}(.*)/', $token, $matches)) {
$options[] = static::parseOption($matches[1]);
} else {
$options[] = static::parseOption(ltrim($token, '-'));
$arguments[] = static::parseArgument($token);
}
}
@@ -73,15 +80,7 @@ class Parser
*/
protected static function parseArgument($token)
{
$description = null;
if (Str::contains($token, ' : ')) {
list($token, $description) = explode(' : ', $token, 2);
$token = trim($token);
$description = trim($description);
}
list($token, $description) = static::extractDescription($token);
switch (true) {
case Str::endsWith($token, '?*'):
@@ -90,6 +89,8 @@ class Parser
return new InputArgument(trim($token, '*'), InputArgument::IS_ARRAY | InputArgument::REQUIRED, $description);
case Str::endsWith($token, '?'):
return new InputArgument(trim($token, '?'), InputArgument::OPTIONAL, $description);
case preg_match('/(.+)\=\*(.+)/', $token, $matches):
return new InputArgument($matches[1], InputArgument::IS_ARRAY, $description, preg_split('/,\s?/', $matches[2]));
case preg_match('/(.+)\=(.+)/', $token, $matches):
return new InputArgument($matches[1], InputArgument::OPTIONAL, $description, $matches[2]);
default:
@@ -105,21 +106,15 @@ class Parser
*/
protected static function parseOption($token)
{
$description = null;
if (Str::contains($token, ' : ')) {
list($token, $description) = explode(' : ', $token);
$token = trim($token);
$description = trim($description);
}
$shortcut = null;
list($token, $description) = static::extractDescription($token);
$matches = preg_split('/\s*\|\s*/', $token, 2);
if (isset($matches[1])) {
$shortcut = $matches[0];
$token = $matches[1];
} else {
$shortcut = null;
}
switch (true) {
@@ -127,10 +122,25 @@ class Parser
return new InputOption(trim($token, '='), $shortcut, InputOption::VALUE_OPTIONAL, $description);
case Str::endsWith($token, '=*'):
return new InputOption(trim($token, '=*'), $shortcut, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, $description);
case preg_match('/(.+)\=\*(.+)/', $token, $matches):
return new InputOption($matches[1], $shortcut, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, $description, preg_split('/,\s?/', $matches[2]));
case preg_match('/(.+)\=(.+)/', $token, $matches):
return new InputOption($matches[1], $shortcut, InputOption::VALUE_OPTIONAL, $description, $matches[2]);
default:
return new InputOption($token, $shortcut, InputOption::VALUE_NONE, $description);
}
}
/**
* Parse the token into its token and description segments.
*
* @param string $token
* @return array
*/
protected static function extractDescription($token)
{
$parts = preg_split('/\s+:\s+/', trim($token), 2);
return count($parts) === 2 ? $parts : [$token, ''];
}
}

View File

@@ -1,37 +0,0 @@
<?php
namespace Illuminate\Console;
use Illuminate\Support\ServiceProvider;
class ScheduleServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->commands('Illuminate\Console\Scheduling\ScheduleRunCommand');
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [
'Illuminate\Console\Scheduling\ScheduleRunCommand',
];
}
}

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