updated-packages

This commit is contained in:
RafficMohammed
2023-01-08 00:13:22 +05:30
parent 3ff7df7487
commit da241bacb6
12659 changed files with 563377 additions and 510538 deletions

View File

@@ -2,6 +2,9 @@
namespace Illuminate\Auth\Access;
use Exception;
use ReflectionClass;
use ReflectionFunction;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use InvalidArgumentException;
@@ -54,6 +57,20 @@ class Gate implements GateContract
*/
protected $afterCallbacks = [];
/**
* All of the defined abilities using class@method notation.
*
* @var array
*/
protected $stringCallbacks = [];
/**
* The callback to be used to guess policy names.
*
* @var callable|null
*/
protected $guessPolicyNamesUsingCallback;
/**
* Create a new gate instance.
*
@@ -63,10 +80,12 @@ class Gate implements GateContract
* @param array $policies
* @param array $beforeCallbacks
* @param array $afterCallbacks
* @param callable|null $guessPolicyNamesUsingCallback
* @return void
*/
public function __construct(Container $container, callable $userResolver, array $abilities = [],
array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = [])
array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = [],
callable $guessPolicyNamesUsingCallback = null)
{
$this->policies = $policies;
$this->container = $container;
@@ -74,6 +93,7 @@ class Gate implements GateContract
$this->userResolver = $userResolver;
$this->afterCallbacks = $afterCallbacks;
$this->beforeCallbacks = $beforeCallbacks;
$this->guessPolicyNamesUsingCallback = $guessPolicyNamesUsingCallback;
}
/**
@@ -108,7 +128,9 @@ class Gate implements GateContract
{
if (is_callable($callback)) {
$this->abilities[$ability] = $callback;
} elseif (is_string($callback) && Str::contains($callback, '@')) {
} elseif (is_string($callback)) {
$this->stringCallbacks[$ability] = $callback;
$this->abilities[$ability] = $this->buildAbilityCallback($ability, $callback);
} else {
throw new InvalidArgumentException("Callback must be a callable or a 'Class@method' string.");
@@ -128,10 +150,11 @@ class Gate implements GateContract
public function resource($name, $class, array $abilities = null)
{
$abilities = $abilities ?: [
'view' => 'view',
'create' => 'create',
'update' => 'update',
'delete' => 'delete',
'viewAny' => 'viewAny',
'view' => 'view',
'create' => 'create',
'update' => 'update',
'delete' => 'delete',
];
foreach ($abilities as $ability => $method) {
@@ -151,7 +174,11 @@ class Gate implements GateContract
protected function buildAbilityCallback($ability, $callback)
{
return function () use ($ability, $callback) {
list($class, $method) = Str::parseCallback($callback);
if (Str::contains($callback, '@')) {
[$class, $method] = Str::parseCallback($callback);
} else {
$class = $callback;
}
$policy = $this->resolvePolicy($class);
@@ -167,7 +194,9 @@ class Gate implements GateContract
return $result;
}
return $policy->{$method}(...func_get_args());
return isset($method)
? $policy->{$method}(...func_get_args())
: $policy(...func_get_args());
};
}
@@ -267,6 +296,18 @@ class Gate implements GateContract
});
}
/**
* Determine if all of the given abilities should be denied for the current user.
*
* @param iterable|string $abilities
* @param array|mixed $arguments
* @return bool
*/
public function none($abilities, $arguments = [])
{
return ! $this->any($abilities, $arguments);
}
/**
* Determine if the given ability should be granted for the current user.
*
@@ -294,14 +335,12 @@ class Gate implements GateContract
* @param array|mixed $arguments
* @return mixed
*/
protected function raw($ability, $arguments = [])
public function raw($ability, $arguments = [])
{
if (! $user = $this->resolveUser()) {
return false;
}
$arguments = Arr::wrap($arguments);
$user = $this->resolveUser();
// First we will call the "before" callbacks for the Gate. If any of these give
// back a non-null response, we will immediately return that result in order
// to let the developers override all checks for some authorization cases.
@@ -316,17 +355,95 @@ class Gate implements GateContract
// After calling the authorization callback, we will call the "after" callbacks
// that are registered with the Gate, which allows a developer to do logging
// if that is required for this application. Then we'll return the result.
$this->callAfterCallbacks(
return $this->callAfterCallbacks(
$user, $ability, $arguments, $result
);
}
return $result;
/**
* Determine whether the callback/method can be called with the given user.
*
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param \Closure|string|array $class
* @param string|null $method
* @return bool
*/
protected function canBeCalledWithUser($user, $class, $method = null)
{
if (! is_null($user)) {
return true;
}
if (! is_null($method)) {
return $this->methodAllowsGuests($class, $method);
}
if (is_array($class)) {
$className = is_string($class[0]) ? $class[0] : get_class($class[0]);
return $this->methodAllowsGuests($className, $class[1]);
}
return $this->callbackAllowsGuests($class);
}
/**
* Determine if the given class method allows guests.
*
* @param string $class
* @param string $method
* @return bool
*/
protected function methodAllowsGuests($class, $method)
{
try {
$reflection = new ReflectionClass($class);
$method = $reflection->getMethod($method);
} catch (Exception $e) {
return false;
}
if ($method) {
$parameters = $method->getParameters();
return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]);
}
return false;
}
/**
* Determine if the callback allows guests.
*
* @param callable $callback
* @return bool
*
* @throws \ReflectionException
*/
protected function callbackAllowsGuests($callback)
{
$parameters = (new ReflectionFunction($callback))->getParameters();
return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]);
}
/**
* Determine if the given parameter allows guests.
*
* @param \ReflectionParameter $parameter
* @return bool
*/
protected function parameterAllowsGuests($parameter)
{
return ($parameter->getClass() && $parameter->allowsNull()) ||
($parameter->isDefaultValueAvailable() && is_null($parameter->getDefaultValue()));
}
/**
* Resolve and call the appropriate authorization callback.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param string $ability
* @param array $arguments
* @return bool
@@ -341,17 +458,19 @@ class Gate implements GateContract
/**
* Call all of the before callbacks and return if a result is given.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param string $ability
* @param array $arguments
* @return bool|null
*/
protected function callBeforeCallbacks($user, $ability, array $arguments)
{
$arguments = array_merge([$user, $ability], [$arguments]);
foreach ($this->beforeCallbacks as $before) {
if (! is_null($result = $before(...$arguments))) {
if (! $this->canBeCalledWithUser($user, $before)) {
continue;
}
if (! is_null($result = $before($user, $ability, $arguments))) {
return $result;
}
}
@@ -364,21 +483,27 @@ class Gate implements GateContract
* @param string $ability
* @param array $arguments
* @param bool $result
* @return void
* @return bool|null
*/
protected function callAfterCallbacks($user, $ability, array $arguments, $result)
{
$arguments = array_merge([$user, $ability, $result], [$arguments]);
foreach ($this->afterCallbacks as $after) {
$after(...$arguments);
if (! $this->canBeCalledWithUser($user, $after)) {
continue;
}
$afterResult = $after($user, $ability, $result, $arguments);
$result = $result ?? $afterResult;
}
return $result;
}
/**
* Resolve the callable for the given ability and arguments.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param string $ability
* @param array $arguments
* @return callable
@@ -391,12 +516,20 @@ class Gate implements GateContract
return $callback;
}
if (isset($this->abilities[$ability])) {
if (isset($this->stringCallbacks[$ability])) {
[$class, $method] = Str::parseCallback($this->stringCallbacks[$ability]);
if ($this->canBeCalledWithUser($user, $class, $method ?: '__invoke')) {
return $this->abilities[$ability];
}
}
if (isset($this->abilities[$ability]) &&
$this->canBeCalledWithUser($user, $this->abilities[$ability])) {
return $this->abilities[$ability];
}
return function () {
return false;
};
}
@@ -420,6 +553,12 @@ class Gate implements GateContract
return $this->resolvePolicy($this->policies[$class]);
}
foreach ($this->guessPolicyName($class) as $guessedPolicy) {
if (class_exists($guessedPolicy)) {
return $this->resolvePolicy($guessedPolicy);
}
}
foreach ($this->policies as $expected => $policy) {
if (is_subclass_of($class, $expected)) {
return $this->resolvePolicy($policy);
@@ -427,11 +566,43 @@ class Gate implements GateContract
}
}
/**
* Guess the policy name for the given class.
*
* @param string $class
* @return array
*/
protected function guessPolicyName($class)
{
if ($this->guessPolicyNamesUsingCallback) {
return Arr::wrap(call_user_func($this->guessPolicyNamesUsingCallback, $class));
}
$classDirname = str_replace('/', '\\', dirname(str_replace('\\', '/', $class)));
return [$classDirname.'\\Policies\\'.class_basename($class).'Policy'];
}
/**
* Specify a callback to be used to guess policy names.
*
* @param callable $callback
* @return $this
*/
public function guessPolicyNamesUsing(callable $callback)
{
$this->guessPolicyNamesUsingCallback = $callback;
return $this;
}
/**
* Build a policy class instance of the given type.
*
* @param object|string $class
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function resolvePolicy($class)
{
@@ -468,18 +639,9 @@ class Gate implements GateContract
return $result;
}
$ability = $this->formatAbilityToMethod($ability);
$method = $this->formatAbilityToMethod($ability);
// If this first argument is a string, that means they are passing a class name
// to the policy. We will remove the first argument from this argument array
// because this policy already knows what type of models it can authorize.
if (isset($arguments[0]) && is_string($arguments[0])) {
array_shift($arguments);
}
return is_callable([$policy, $ability])
? $policy->{$ability}($user, ...$arguments)
: false;
return $this->callPolicyMethod($policy, $method, $user, $arguments);
};
}
@@ -494,11 +656,42 @@ class Gate implements GateContract
*/
protected function callPolicyBefore($policy, $user, $ability, $arguments)
{
if (method_exists($policy, 'before')) {
if (! method_exists($policy, 'before')) {
return;
}
if ($this->canBeCalledWithUser($user, $policy, 'before')) {
return $policy->before($user, $ability, ...$arguments);
}
}
/**
* Call the appropriate method on the given policy.
*
* @param mixed $policy
* @param string $method
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param array $arguments
* @return mixed
*/
protected function callPolicyMethod($policy, $method, $user, array $arguments)
{
// If this first argument is a string, that means they are passing a class name
// to the policy. We will remove the first argument from this argument array
// because this policy already knows what type of models it can authorize.
if (isset($arguments[0]) && is_string($arguments[0])) {
array_shift($arguments);
}
if (! is_callable([$policy, $method])) {
return;
}
if ($this->canBeCalledWithUser($user, $policy, $method)) {
return $policy->{$method}($user, ...$arguments);
}
}
/**
* Format the policy ability into a method name.
*
@@ -524,7 +717,8 @@ class Gate implements GateContract
return new static(
$this->container, $callback, $this->abilities,
$this->policies, $this->beforeCallbacks, $this->afterCallbacks
$this->policies, $this->beforeCallbacks, $this->afterCallbacks,
$this->guessPolicyNamesUsingCallback
);
}

View File

@@ -35,10 +35,10 @@ class Response
/**
* Get the string representation of the message.
*
* @return string|null
* @return string
*/
public function __toString()
{
return $this->message();
return (string) $this->message();
}
}

View File

@@ -13,7 +13,7 @@ class AuthManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
@@ -43,7 +43,7 @@ class AuthManager implements FactoryContract
/**
* Create a new Auth manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
@@ -58,7 +58,7 @@ class AuthManager implements FactoryContract
/**
* Attempt to get the guard from the local cache.
*
* @param string $name
* @param string|null $name
* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
*/
public function guard($name = null)
@@ -94,7 +94,9 @@ class AuthManager implements FactoryContract
return $this->{$driverMethod}($name, $config);
}
throw new InvalidArgumentException("Auth driver [{$config['driver']}] for guard [{$name}] is not defined.");
throw new InvalidArgumentException(
"Auth driver [{$config['driver']}] for guard [{$name}] is not defined."
);
}
/**
@@ -154,7 +156,10 @@ class AuthManager implements FactoryContract
// user in the database or another persistence layer where users are.
$guard = new TokenGuard(
$this->createUserProvider($config['provider'] ?? null),
$this->app['request']
$this->app['request'],
$config['input_key'] ?? 'api_token',
$config['storage_key'] ?? 'api_token',
$config['hash'] ?? false
);
$this->app->refresh('request', $guard, 'setRequest');

View File

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

View File

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

View File

@@ -33,6 +33,7 @@ class AuthMakeCommand extends Command
protected $views = [
'auth/login.stub' => 'auth/login.blade.php',
'auth/register.stub' => 'auth/register.blade.php',
'auth/verify.stub' => 'auth/verify.blade.php',
'auth/passwords/email.stub' => 'auth/passwords/email.blade.php',
'auth/passwords/reset.stub' => 'auth/passwords/reset.blade.php',
'layouts/app.stub' => 'layouts/app.blade.php',
@@ -73,11 +74,11 @@ class AuthMakeCommand extends Command
*/
protected function createDirectories()
{
if (! is_dir($directory = resource_path('views/layouts'))) {
if (! is_dir($directory = $this->getViewPath('layouts'))) {
mkdir($directory, 0755, true);
}
if (! is_dir($directory = resource_path('views/auth/passwords'))) {
if (! is_dir($directory = $this->getViewPath('auth/passwords'))) {
mkdir($directory, 0755, true);
}
}
@@ -90,7 +91,7 @@ class AuthMakeCommand extends Command
protected function exportViews()
{
foreach ($this->views as $key => $value) {
if (file_exists($view = resource_path('views/'.$value)) && ! $this->option('force')) {
if (file_exists($view = $this->getViewPath($value)) && ! $this->option('force')) {
if (! $this->confirm("The [{$value}] view already exists. Do you want to replace it?")) {
continue;
}
@@ -116,4 +117,17 @@ class AuthMakeCommand extends Command
file_get_contents(__DIR__.'/stubs/make/controllers/HomeController.stub')
);
}
/**
* Get full view path relative to the app's configured view path.
*
* @param string $path
* @return string
*/
protected function getViewPath($path)
{
return implode(DIRECTORY_SEPARATOR, [
config('view.paths')[0] ?? resource_path('views'), $path,
]);
}
}

View File

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

View File

@@ -8,20 +8,20 @@
<div class="card-header">{{ __('Login') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}" aria-label="{{ __('Login') }}">
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-sm-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required autofocus>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@if ($errors->has('email'))
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('email') }}</strong>
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
@@ -29,13 +29,13 @@
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@if ($errors->has('password'))
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('password') }}</strong>
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
@@ -57,9 +57,11 @@
{{ __('Login') }}
</button>
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>

View File

@@ -14,20 +14,20 @@
</div>
@endif
<form method="POST" action="{{ route('password.email') }}" aria-label="{{ __('Reset Password') }}">
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@if ($errors->has('email'))
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('email') }}</strong>
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>

View File

@@ -8,7 +8,7 @@
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('password.request') }}" aria-label="{{ __('Reset Password') }}">
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
@@ -17,13 +17,13 @@
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email ?? old('email') }}" required autofocus>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
@if ($errors->has('email'))
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('email') }}</strong>
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
@@ -31,13 +31,13 @@
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@if ($errors->has('password'))
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('password') }}</strong>
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
@@ -45,7 +45,7 @@
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>

View File

@@ -8,20 +8,20 @@
<div class="card-header">{{ __('Register') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('register') }}" aria-label="{{ __('Register') }}">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" required autofocus>
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@if ($errors->has('name'))
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('name') }}</strong>
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
@@ -29,13 +29,13 @@
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
@if ($errors->has('email'))
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('email') }}</strong>
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
@@ -43,13 +43,13 @@
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@if ($errors->has('password'))
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('password') }}</strong>
<strong>{{ $message }}</strong>
</span>
@endif
@enderror
</div>
</div>
@@ -57,7 +57,7 @@
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>

View File

@@ -0,0 +1,24 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Verify Your Email Address') }}</div>
<div class="card-body">
@if (session('resent'))
<div class="alert alert-success" role="alert">
{{ __('A fresh verification link has been sent to your email address.') }}
</div>
@endif
{{ __('Before proceeding, please check your email for a verification link.') }}
{{ __('If you did not receive the email') }}, <a href="{{ route('verification.resend') }}">{{ __('click here to request another') }}</a>.
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -2,7 +2,6 @@
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
@@ -14,15 +13,15 @@
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-md navbar-light navbar-laravel">
<nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
@@ -44,9 +43,11 @@
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>

View File

@@ -47,9 +47,9 @@ class EloquentUserProvider implements UserProvider
{
$model = $this->createModel();
return $model->newQuery()
->where($model->getAuthIdentifierName(), $identifier)
->first();
return $this->newModelQuery($model)
->where($model->getAuthIdentifierName(), $identifier)
->first();
}
/**
@@ -63,21 +63,24 @@ class EloquentUserProvider implements UserProvider
{
$model = $this->createModel();
$model = $model->where($model->getAuthIdentifierName(), $identifier)->first();
$retrievedModel = $this->newModelQuery($model)->where(
$model->getAuthIdentifierName(), $identifier
)->first();
if (! $model) {
return null;
if (! $retrievedModel) {
return;
}
$rememberToken = $model->getRememberToken();
$rememberToken = $retrievedModel->getRememberToken();
return $rememberToken && hash_equals($rememberToken, $token) ? $model : null;
return $rememberToken && hash_equals($rememberToken, $token)
? $retrievedModel : null;
}
/**
* Update the "remember me" token for the given user in storage.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param \Illuminate\Contracts\Auth\Authenticatable|\Illuminate\Database\Eloquent\Model $user
* @param string $token
* @return void
*/
@@ -104,14 +107,14 @@ class EloquentUserProvider implements UserProvider
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
Str::contains($this->firstCredentialKey($credentials), 'password'))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
$query = $this->newModelQuery();
foreach ($credentials as $key => $value) {
if (Str::contains($key, 'password')) {
@@ -128,6 +131,19 @@ class EloquentUserProvider implements UserProvider
return $query->first();
}
/**
* Get the first key from the credential array.
*
* @param array $credentials
* @return string|null
*/
protected function firstCredentialKey(array $credentials)
{
foreach ($credentials as $key => $value) {
return $key;
}
}
/**
* Validate a user against the given credentials.
*
@@ -142,6 +158,19 @@ class EloquentUserProvider implements UserProvider
return $this->hasher->check($plain, $user->getAuthPassword());
}
/**
* Get a new query builder for the model instance.
*
* @param \Illuminate\Database\Eloquent\Model|null $model
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function newModelQuery($model = null)
{
return is_null($model)
? $this->createModel()->newQuery()
: $model->newQuery();
}
/**
* Create a new instance of the model.
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ trait GuardHelpers
protected $provider;
/**
* Determine if the current user is authenticated.
* Determine if current user is authenticated. If not, throw an exception.
*
* @return \Illuminate\Contracts\Auth\Authenticatable
*

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,22 @@
<?php
namespace Illuminate\Auth\Listeners;
use Illuminate\Auth\Events\Registered;
use Illuminate\Contracts\Auth\MustVerifyEmail;
class SendEmailVerificationNotification
{
/**
* Handle the event.
*
* @param \Illuminate\Auth\Events\Registered $event
* @return void
*/
public function handle(Registered $event)
{
if ($event->user instanceof MustVerifyEmail && ! $event->user->hasVerifiedEmail()) {
$event->user->sendEmailVerificationNotification();
}
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,31 @@
<?php
namespace Illuminate\Auth\Middleware;
use Closure;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Contracts\Auth\MustVerifyEmail;
class EnsureEmailIsVerified
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $redirectToRoute
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle($request, Closure $next, $redirectToRoute = null)
{
if (! $request->user() ||
($request->user() instanceof MustVerifyEmail &&
! $request->user()->hasVerifiedEmail())) {
return $request->expectsJson()
? abort(403, 'Your email address is not verified.')
: Redirect::route($redirectToRoute ?: 'verification.notice');
}
return $next($request);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Illuminate\Auth;
trait MustVerifyEmail
{
/**
* Determine if the user has verified their email address.
*
* @return bool
*/
public function hasVerifiedEmail()
{
return ! is_null($this->email_verified_at);
}
/**
* Mark the given user's email as verified.
*
* @return bool
*/
public function markEmailAsVerified()
{
return $this->forceFill([
'email_verified_at' => $this->freshTimestamp(),
])->save();
}
/**
* Send the email verification notification.
*
* @return void
*/
public function sendEmailVerificationNotification()
{
$this->notify(new Notifications\VerifyEmail);
}
}

View File

@@ -59,7 +59,8 @@ class ResetPassword extends Notification
return (new MailMessage)
->subject(Lang::getFromJson('Reset Password Notification'))
->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.'))
->action(Lang::getFromJson('Reset Password'), url(config('app.url').route('password.reset', $this->token, false)))
->action(Lang::getFromJson('Reset Password'), url(config('app.url').route('password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->line(Lang::getFromJson('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
->line(Lang::getFromJson('If you did not request a password reset, no further action is required.'));
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Illuminate\Auth\Notifications;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Config;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class VerifyEmail extends Notification
{
/**
* The callback that should be used to build the mail message.
*
* @var \Closure|null
*/
public static $toMailCallback;
/**
* 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)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return (new MailMessage)
->subject(Lang::getFromJson('Verify Email Address'))
->line(Lang::getFromJson('Please click the button below to verify your email address.'))
->action(Lang::getFromJson('Verify Email Address'), $verificationUrl)
->line(Lang::getFromJson('If you did not create an account, no further action is required.'));
}
/**
* Get the verification URL for the given notifiable.
*
* @param mixed $notifiable
* @return string
*/
protected function verificationUrl($notifiable)
{
return URL::temporarySignedRoute(
'verification.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
['id' => $notifiable->getKey()]
);
}
/**
* Set a callback that should be used when building the notification mail message.
*
* @param \Closure $callback
* @return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}

View File

@@ -146,7 +146,7 @@ class PasswordBroker implements PasswordBrokerContract
public function validateNewPassword(array $credentials)
{
if (isset($this->passwordValidator)) {
list($password, $confirm) = [
[$password, $confirm] = [
$credentials['password'],
$credentials['password_confirmation'],
];
@@ -167,12 +167,12 @@ class PasswordBroker implements PasswordBrokerContract
*/
protected function validatePasswordWithDefaults(array $credentials)
{
list($password, $confirm) = [
[$password, $confirm] = [
$credentials['password'],
$credentials['password_confirmation'],
];
return $password === $confirm && mb_strlen($password) >= 6;
return $password === $confirm && mb_strlen($password) >= 8;
}
/**

View File

@@ -14,7 +14,7 @@ class PasswordBrokerManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
@@ -28,7 +28,7 @@ class PasswordBrokerManager implements FactoryContract
/**
* Create a new PasswordBroker manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
@@ -46,9 +46,7 @@ class PasswordBrokerManager implements FactoryContract
{
$name = $name ?: $this->getDefaultDriver();
return isset($this->brokers[$name])
? $this->brokers[$name]
: $this->brokers[$name] = $this->resolve($name);
return $this->brokers[$name] ?? ($this->brokers[$name] = $this->resolve($name));
}
/**

View File

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

View File

@@ -83,6 +83,6 @@ class Recaller
{
$segments = explode('|', $this->recaller);
return count($segments) == 3 && trim($segments[0]) !== '' && trim($segments[1]) !== '';
return count($segments) === 3 && trim($segments[0]) !== '' && trim($segments[1]) !== '';
}
}

View File

@@ -135,9 +135,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
$recaller = $this->recaller();
if (is_null($this->user) && ! is_null($recaller)) {
if (is_null($this->user) && ! is_null($recaller = $this->recaller())) {
$this->user = $this->userFromRecaller($recaller);
if ($this->user) {
@@ -329,6 +327,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
* Get the response for basic authentication.
*
* @return void
*
* @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
*/
protected function failedBasicResponse()
@@ -488,12 +487,12 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
// listening for anytime a user signs out of this application manually.
$this->clearUserDataFromStorage();
if (! is_null($this->user)) {
if (! is_null($this->user) && ! empty($user->getRememberToken())) {
$this->cycleRememberToken($user);
}
if (isset($this->events)) {
$this->events->dispatch(new Events\Logout($user));
$this->events->dispatch(new Events\Logout($this->name, $user));
}
// Once we have fired the logout event we will clear the users out of memory
@@ -551,7 +550,12 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
$attribute => Hash::make($password),
]))->save();
$this->queueRecallerCookie($this->user());
if ($this->recaller() ||
$this->getCookieJar()->hasQueued($this->getRecallerName())) {
$this->queueRecallerCookie($this->user());
}
$this->fireOtherDeviceLogoutEvent($this->user());
return $result;
}
@@ -580,7 +584,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Attempting(
$credentials, $remember
$this->name, $credentials, $remember
));
}
}
@@ -595,7 +599,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireLoginEvent($user, $remember = false)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Login($user, $remember));
$this->events->dispatch(new Events\Login(
$this->name, $user, $remember
));
}
}
@@ -608,7 +614,24 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireAuthenticatedEvent($user)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Authenticated($user));
$this->events->dispatch(new Events\Authenticated(
$this->name, $user
));
}
}
/**
* Fire the other device logout event if the dispatcher is set.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
protected function fireOtherDeviceLogoutEvent($user)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\OtherDeviceLogout(
$this->name, $user
));
}
}
@@ -622,7 +645,9 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireFailedEvent($user, array $credentials)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Failed($user, $credentials));
$this->events->dispatch(new Events\Failed(
$this->name, $user, $credentials
));
}
}

View File

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

View File

@@ -15,10 +15,10 @@
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.6.*",
"illuminate/http": "5.6.*",
"illuminate/queue": "5.6.*",
"illuminate/support": "5.6.*"
"illuminate/contracts": "5.8.*",
"illuminate/http": "5.8.*",
"illuminate/queue": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@@ -27,13 +27,13 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.6-dev"
"dev-master": "5.8-dev"
}
},
"suggest": {
"illuminate/console": "Required to use the auth:clear-resets command (5.6.*).",
"illuminate/queue": "Required to fire login / logout events (5.6.*).",
"illuminate/session": "Required to use the session based guard (5.6.*)."
"illuminate/console": "Required to use the auth:clear-resets command (5.8.*).",
"illuminate/queue": "Required to fire login / logout events (5.8.*).",
"illuminate/session": "Required to use the session based guard (5.8.*)."
},
"config": {
"sort-packages": true

View File

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

View File

@@ -21,7 +21,7 @@ class BroadcastManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
@@ -42,7 +42,7 @@ class BroadcastManager implements FactoryContract
/**
* Create a new manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
@@ -132,7 +132,7 @@ class BroadcastManager implements FactoryContract
/**
* Get a driver instance.
*
* @param string $driver
* @param string|null $driver
* @return mixed
*/
public function connection($driver = null)
@@ -165,7 +165,7 @@ class BroadcastManager implements FactoryContract
}
/**
* Resolve the given store.
* Resolve the given broadcaster.
*
* @param string $name
* @return \Illuminate\Contracts\Broadcasting\Broadcaster
@@ -176,10 +176,6 @@ class BroadcastManager implements FactoryContract
{
$config = $this->getConfig($name);
if (is_null($config)) {
throw new InvalidArgumentException("Broadcaster [{$name}] is not defined.");
}
if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($config);
}
@@ -212,10 +208,16 @@ class BroadcastManager implements FactoryContract
*/
protected function createPusherDriver(array $config)
{
return new PusherBroadcaster(
new Pusher($config['key'], $config['secret'],
$config['app_id'], $config['options'] ?? [])
$pusher = new Pusher(
$config['key'], $config['secret'],
$config['app_id'], $config['options'] ?? []
);
if ($config['log'] ?? false) {
$pusher->setLogger($this->app->make(LoggerInterface::class));
}
return new PusherBroadcaster($pusher);
}
/**
@@ -263,7 +265,11 @@ class BroadcastManager implements FactoryContract
*/
protected function getConfig($name)
{
return $this->app['config']["broadcasting.connections.{$name}"];
if (! is_null($name) && $name !== 'null') {
return $this->app['config']["broadcasting.connections.{$name}"];
}
return ['driver' => 'null'];
}
/**

View File

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

View File

@@ -5,6 +5,7 @@ namespace Illuminate\Broadcasting\Broadcasters;
use Exception;
use ReflectionClass;
use ReflectionFunction;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Container\Container;
use Illuminate\Contracts\Routing\UrlRoutable;
@@ -21,10 +22,17 @@ abstract class Broadcaster implements BroadcasterContract
*/
protected $channels = [];
/**
* The registered channel options.
*
* @var array
*/
protected $channelOptions = [];
/**
* The binding registrar instance.
*
* @var BindingRegistrar
* @var \Illuminate\Contracts\Routing\BindingRegistrar
*/
protected $bindingRegistrar;
@@ -33,12 +41,15 @@ abstract class Broadcaster implements BroadcasterContract
*
* @param string $channel
* @param callable|string $callback
* @param array $options
* @return $this
*/
public function channel($channel, $callback)
public function channel($channel, $callback, $options = [])
{
$this->channels[$channel] = $callback;
$this->channelOptions[$channel] = $options;
return $this;
}
@@ -48,12 +59,13 @@ abstract class Broadcaster implements BroadcasterContract
* @param \Illuminate\Http\Request $request
* @param string $channel
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
protected function verifyUserCanAccessChannel($request, $channel)
{
foreach ($this->channels as $pattern => $callback) {
if (! Str::is(preg_replace('/\{(.*?)\}/', '*', $pattern), $channel)) {
if (! $this->channelNameMatchesPattern($channel, $pattern)) {
continue;
}
@@ -61,7 +73,7 @@ abstract class Broadcaster implements BroadcasterContract
$handler = $this->normalizeChannelHandlerToCallable($callback);
if ($result = $handler($request->user(), ...$parameters)) {
if ($result = $handler($this->retrieveUser($request, $channel), ...$parameters)) {
return $this->validAuthenticationResponse($request, $result);
}
}
@@ -93,6 +105,7 @@ abstract class Broadcaster implements BroadcasterContract
*
* @param callable|string $callback
* @return \ReflectionParameter[]
*
* @throws \Exception
*/
protected function extractParameters($callback)
@@ -111,6 +124,7 @@ abstract class Broadcaster implements BroadcasterContract
*
* @param string $callback
* @return \ReflectionParameter[]
*
* @throws \Exception
*/
protected function extractParametersFromClass($callback)
@@ -180,6 +194,7 @@ abstract class Broadcaster implements BroadcasterContract
* @param mixed $value
* @param array $callbackParameters
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
protected function resolveImplicitBindingIfPossible($key, $value, $callbackParameters)
@@ -256,4 +271,59 @@ abstract class Broadcaster implements BroadcasterContract
->join(...$args);
};
}
/**
* Retrieve the authenticated user using the configured guard (if any).
*
* @param \Illuminate\Http\Request $request
* @param string $channel
* @return mixed
*/
protected function retrieveUser($request, $channel)
{
$options = $this->retrieveChannelOptions($channel);
$guards = $options['guards'] ?? null;
if (is_null($guards)) {
return $request->user();
}
foreach (Arr::wrap($guards) as $guard) {
if ($user = $request->user($guard)) {
return $user;
}
}
}
/**
* Retrieve options for a certain channel.
*
* @param string $channel
* @return array
*/
protected function retrieveChannelOptions($channel)
{
foreach ($this->channelOptions as $pattern => $options) {
if (! $this->channelNameMatchesPattern($channel, $pattern)) {
continue;
}
return $options;
}
return [];
}
/**
* Check if channel name from request match a pattern from registered channels.
*
* @param string $channel
* @param string $pattern
* @return bool
*/
protected function channelNameMatchesPattern($channel, $pattern)
{
return Str::is(preg_replace('/\{(.*?)\}/', '*', $pattern), $channel);
}
}

View File

@@ -10,6 +10,8 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class PusherBroadcaster extends Broadcaster
{
use UsePusherChannelConventions;
/**
* The Pusher SDK instance.
*
@@ -33,19 +35,18 @@ class PusherBroadcaster extends Broadcaster
*
* @param \Illuminate\Http\Request $request
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function auth($request)
{
if (Str::startsWith($request->channel_name, ['private-', 'presence-']) &&
! $request->user()) {
$channelName = $this->normalizeChannelName($request->channel_name);
if ($this->isGuardedChannel($request->channel_name) &&
! $this->retrieveUser($request, $channelName)) {
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
);
@@ -66,11 +67,13 @@ class PusherBroadcaster extends Broadcaster
);
}
$channelName = $this->normalizeChannelName($request->channel_name);
return $this->decodePusherResponse(
$request,
$this->pusher->presence_auth(
$request->channel_name, $request->socket_id,
$request->user()->getAuthIdentifier(), $result
$this->retrieveUser($request, $channelName)->getAuthIdentifier(), $result
)
);
}

View File

@@ -3,12 +3,13 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Contracts\Redis\Factory as Redis;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class RedisBroadcaster extends Broadcaster
{
use UsePusherChannelConventions;
/**
* The Redis instance.
*
@@ -41,19 +42,18 @@ class RedisBroadcaster extends Broadcaster
*
* @param \Illuminate\Http\Request $request
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function auth($request)
{
if (Str::startsWith($request->channel_name, ['private-', 'presence-']) &&
! $request->user()) {
$channelName = $this->normalizeChannelName($request->channel_name);
if ($this->isGuardedChannel($request->channel_name) &&
! $this->retrieveUser($request, $channelName)) {
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
);
@@ -72,8 +72,10 @@ class RedisBroadcaster extends Broadcaster
return json_encode($result);
}
$channelName = $this->normalizeChannelName($request->channel_name);
return json_encode(['channel_data' => [
'user_id' => $request->user()->getAuthIdentifier(),
'user_id' => $this->retrieveUser($request, $channelName)->getAuthIdentifier(),
'user_info' => $result,
]]);
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Illuminate\Broadcasting\Broadcasters;
use Illuminate\Support\Str;
trait UsePusherChannelConventions
{
/**
* Return true if channel is protected by authentication.
*
* @param string $channel
* @return bool
*/
public function isGuardedChannel($channel)
{
return Str::startsWith($channel, ['private-', 'presence-']);
}
/**
* Remove prefix from channel name.
*
* @param string $channel
* @return string
*/
public function normalizeChannelName($channel)
{
if ($this->isGuardedChannel($channel)) {
return Str::startsWith($channel, 'private-')
? Str::replaceFirst('private-', '', $channel)
: Str::replaceFirst('presence-', '', $channel);
}
return $channel;
}
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -15,9 +15,9 @@
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.6.*",
"illuminate/pipeline": "5.6.*",
"illuminate/support": "5.6.*"
"illuminate/contracts": "5.8.*",
"illuminate/pipeline": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@@ -26,7 +26,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.6-dev"
"dev-master": "5.8-dev"
}
},
"config": {

View File

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

View File

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

View File

@@ -3,7 +3,9 @@
namespace Illuminate\Cache;
use Closure;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use Aws\DynamoDb\DynamoDbClient;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Cache\Factory as FactoryContract;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
@@ -16,7 +18,7 @@ class CacheManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
@@ -37,7 +39,7 @@ class CacheManager implements FactoryContract
/**
* Create a new Cache manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
@@ -46,7 +48,7 @@ class CacheManager implements FactoryContract
}
/**
* Get a cache store instance by name.
* Get a cache store instance by name, wrapped in a repository.
*
* @param string|null $name
* @return \Illuminate\Contracts\Cache\Repository
@@ -62,7 +64,7 @@ class CacheManager implements FactoryContract
* Get a cache driver instance.
*
* @param string|null $driver
* @return mixed
* @return \Illuminate\Contracts\Cache\Repository
*/
public function driver($driver = null)
{
@@ -124,7 +126,7 @@ class CacheManager implements FactoryContract
* Create an instance of the APC cache driver.
*
* @param array $config
* @return \Illuminate\Cache\ApcStore
* @return \Illuminate\Cache\Repository
*/
protected function createApcDriver(array $config)
{
@@ -136,7 +138,7 @@ class CacheManager implements FactoryContract
/**
* Create an instance of the array cache driver.
*
* @return \Illuminate\Cache\ArrayStore
* @return \Illuminate\Cache\Repository
*/
protected function createArrayDriver()
{
@@ -147,7 +149,7 @@ class CacheManager implements FactoryContract
* Create an instance of the file cache driver.
*
* @param array $config
* @return \Illuminate\Cache\FileStore
* @return \Illuminate\Cache\Repository
*/
protected function createFileDriver(array $config)
{
@@ -158,7 +160,7 @@ class CacheManager implements FactoryContract
* Create an instance of the Memcached cache driver.
*
* @param array $config
* @return \Illuminate\Cache\MemcachedStore
* @return \Illuminate\Cache\Repository
*/
protected function createMemcachedDriver(array $config)
{
@@ -177,7 +179,7 @@ class CacheManager implements FactoryContract
/**
* Create an instance of the Null cache driver.
*
* @return \Illuminate\Cache\NullStore
* @return \Illuminate\Cache\Repository
*/
protected function createNullDriver()
{
@@ -188,7 +190,7 @@ class CacheManager implements FactoryContract
* Create an instance of the Redis cache driver.
*
* @param array $config
* @return \Illuminate\Cache\RedisStore
* @return \Illuminate\Cache\Repository
*/
protected function createRedisDriver(array $config)
{
@@ -203,7 +205,7 @@ class CacheManager implements FactoryContract
* Create an instance of the database cache driver.
*
* @param array $config
* @return \Illuminate\Cache\DatabaseStore
* @return \Illuminate\Cache\Repository
*/
protected function createDatabaseDriver(array $config)
{
@@ -216,6 +218,38 @@ class CacheManager implements FactoryContract
);
}
/**
* Create an instance of the DynamoDB cache driver.
*
* @param array $config
* @return \Illuminate\Cache\Repository
*/
protected function createDynamodbDriver(array $config)
{
$dynamoConfig = [
'region' => $config['region'],
'version' => 'latest',
'endpoint' => $config['endpoint'] ?? null,
];
if ($config['key'] && $config['secret']) {
$dynamoConfig['credentials'] = Arr::only(
$config, ['key', 'secret', 'token']
);
}
return $this->repository(
new DynamoDbStore(
new DynamoDbClient($dynamoConfig),
$config['table'],
$config['attributes']['key'] ?? 'key',
$config['attributes']['value'] ?? 'value',
$config['attributes']['expiration'] ?? 'expires_at',
$this->getPrefix($config)
)
);
}
/**
* Create a new cache repository with the given implementation.
*
@@ -278,10 +312,29 @@ class CacheManager implements FactoryContract
$this->app['config']['cache.default'] = $name;
}
/**
* Unset the given driver instances.
*
* @param array|string|null $name
* @return $this
*/
public function forgetDriver($name = null)
{
$name = $name ?? $this->getDefaultDriver();
foreach ((array) $name as $cacheName) {
if (isset($this->stores[$cacheName])) {
unset($this->stores[$cacheName]);
}
}
return $this;
}
/**
* Register a custom driver creator Closure.
*
* @param string $driver
* @param string $driver
* @param \Closure $callback
* @return $this
*/
@@ -296,7 +349,7 @@ class CacheManager implements FactoryContract
* Dynamically call the default driver instance.
*
* @param string $method
* @param array $parameters
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)

View File

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

View File

@@ -60,15 +60,19 @@ class ClearCommand extends Command
*/
public function handle()
{
$this->laravel['events']->fire(
$this->laravel['events']->dispatch(
'cache:clearing', [$this->argument('store'), $this->tags()]
);
$this->cache()->flush();
$successful = $this->cache()->flush();
$this->flushFacades();
$this->laravel['events']->fire(
if (! $successful) {
return $this->error('Failed to clear cache. Make sure you have the appropriate permissions.');
}
$this->laravel['events']->dispatch(
'cache:cleared', [$this->argument('store'), $this->tags()]
);
@@ -123,7 +127,7 @@ class ClearCommand extends Command
protected function getArguments()
{
return [
['store', InputArgument::OPTIONAL, 'The name of the store you would like to clear.'],
['store', InputArgument::OPTIONAL, 'The name of the store you would like to clear'],
];
}
@@ -135,7 +139,7 @@ class ClearCommand extends Command
protected function getOptions()
{
return [
['tags', null, InputOption::VALUE_OPTIONAL, 'The cache tags you would like to clear.', null],
['tags', null, InputOption::VALUE_OPTIONAL, 'The cache tags you would like to clear', null],
];
}
}

View File

@@ -4,8 +4,10 @@ namespace Illuminate\Cache;
use Closure;
use Exception;
use Illuminate\Support\Str;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Database\PostgresConnection;
use Illuminate\Database\ConnectionInterface;
class DatabaseStore implements Store
@@ -78,29 +80,31 @@ class DatabaseStore implements Store
return;
}
return unserialize($cache->value);
return $this->unserialize($cache->value);
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$key = $this->prefix.$key;
$value = serialize($value);
$value = $this->serialize($value);
$expiration = $this->getTime() + (int) ($minutes * 60);
$expiration = $this->getTime() + $seconds;
try {
$this->table()->insert(compact('key', 'value', 'expiration'));
return $this->table()->insert(compact('key', 'value', 'expiration'));
} catch (Exception $e) {
$this->table()->where('key', $key)->update(compact('value', 'expiration'));
$result = $this->table()->where('key', $key)->update(compact('value', 'expiration'));
return $result > 0;
}
}
@@ -157,7 +161,7 @@ class DatabaseStore implements Store
$cache = is_array($cache) ? (object) $cache : $cache;
$current = unserialize($cache->value);
$current = $this->unserialize($cache->value);
// Here we'll call this callback function that was given to the function which
// is used to either increment or decrement the function. We use a callback
@@ -172,7 +176,7 @@ class DatabaseStore implements Store
// since database cache values are encrypted by default with secure storage
// that can't be easily read. We will return the new value after storing.
$this->table()->where('key', $prefixed)->update([
'value' => serialize($new),
'value' => $this->serialize($new),
]);
return $new;
@@ -194,11 +198,11 @@ class DatabaseStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->put($key, $value, 5256000);
return $this->put($key, $value, 315360000);
}
/**
@@ -221,7 +225,9 @@ class DatabaseStore implements Store
*/
public function flush()
{
return (bool) $this->table()->delete();
$this->table()->delete();
return true;
}
/**
@@ -253,4 +259,36 @@ class DatabaseStore implements Store
{
return $this->prefix;
}
/**
* Serialize the given value.
*
* @param mixed $value
* @return string
*/
protected function serialize($value)
{
$result = serialize($value);
if ($this->connection instanceof PostgresConnection && Str::contains($result, "\0")) {
$result = base64_encode($result);
}
return $result;
}
/**
* Unserialize the given value.
*
* @param string $value
* @return mixed
*/
protected function unserialize($value)
{
if ($this->connection instanceof PostgresConnection && ! Str::contains($value, [':', ';'])) {
$value = base64_decode($value);
}
return unserialize($value);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Illuminate\Cache;
class DynamoDbLock extends Lock
{
/**
* The DynamoDB client instance.
*
* @var \Illuminate\Cache\DynamoDbStore
*/
protected $dynamo;
/**
* Create a new lock instance.
*
* @param \Illuminate\Cache\DynamoDbStore $dynamo
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return void
*/
public function __construct(DynamoDbStore $dynamo, $name, $seconds, $owner = null)
{
parent::__construct($name, $seconds, $owner);
$this->dynamo = $dynamo;
}
/**
* Attempt to acquire the lock.
*
* @return bool
*/
public function acquire()
{
return $this->dynamo->add(
$this->name, $this->owner, $this->seconds
);
}
/**
* Release the lock.
*
* @return void
*/
public function release()
{
if ($this->isOwnedByCurrentProcess()) {
$this->dynamo->forget($this->name);
}
}
/**
* Release this lock in disregard of ownership.
*
* @return void
*/
public function forceRelease()
{
$this->dynamo->forget($this->name);
}
/**
* Returns the owner value written into the driver for this lock.
*
* @return mixed
*/
protected function getCurrentOwner()
{
return $this->dynamo->get($this->name);
}
}

View File

@@ -0,0 +1,525 @@
<?php
namespace Illuminate\Cache;
use RuntimeException;
use Illuminate\Support\Str;
use Illuminate\Support\Carbon;
use Aws\DynamoDb\DynamoDbClient;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\LockProvider;
use Aws\DynamoDb\Exception\DynamoDbException;
class DynamoDbStore implements Store, LockProvider
{
use InteractsWithTime;
/**
* The DynamoDB client instance.
*
* @var \Aws\DynamoDb\DynamoDbClient
*/
protected $dynamo;
/**
* The table name.
*
* @var string
*/
protected $table;
/**
* The name of the attribute that should hold the key.
*
* @var string
*/
protected $keyAttribute;
/**
* The name of the attribute that should hold the value.
*
* @var string
*/
protected $valueAttribute;
/**
* The name of the attribute that should hold the expiration timestamp.
*
* @var string
*/
protected $expirationAttribute;
/**
* A string that should be prepended to keys.
*
* @var string
*/
protected $prefix;
/**
* Create a new store instance.
*
* @param \Aws\DynamoDb\DynamoDbClient $dynamo
* @param string $table
* @param string $keyAttribute
* @param string $valueAttribute
* @param string $expirationAttribute
* @param string $prefix
* @return void
*/
public function __construct(DynamoDbClient $dynamo,
$table,
$keyAttribute = 'key',
$valueAttribute = 'value',
$expirationAttribute = 'expires_at',
$prefix = '')
{
$this->table = $table;
$this->dynamo = $dynamo;
$this->keyAttribute = $keyAttribute;
$this->valueAttribute = $valueAttribute;
$this->expirationAttribute = $expirationAttribute;
$this->setPrefix($prefix);
}
/**
* Retrieve an item from the cache by key.
*
* @param string $key
* @return mixed
*/
public function get($key)
{
$response = $this->dynamo->getItem([
'TableName' => $this->table,
'ConsistentRead' => false,
'Key' => [
$this->keyAttribute => [
'S' => $this->prefix.$key,
],
],
]);
if (! isset($response['Item'])) {
return;
}
if ($this->isExpired($response['Item'])) {
return;
}
if (isset($response['Item'][$this->valueAttribute])) {
return $this->unserialize(
$response['Item'][$this->valueAttribute]['S'] ??
$response['Item'][$this->valueAttribute]['N'] ??
null
);
}
}
/**
* Retrieve multiple items from the cache by key.
*
* Items not found in the cache will have a null value.
*
* @param array $keys
* @return array
*/
public function many(array $keys)
{
$prefixedKeys = array_map(function ($key) {
return $this->prefix.$key;
}, $keys);
$response = $this->dynamo->batchGetItem([
'RequestItems' => [
$this->table => [
'ConsistentRead' => false,
'Keys' => collect($prefixedKeys)->map(function ($key) {
return [
$this->keyAttribute => [
'S' => $key,
],
];
})->all(),
],
],
]);
$now = Carbon::now();
return array_merge(collect(array_flip($keys))->map(function () {
})->all(), collect($response['Responses'][$this->table])->mapWithKeys(function ($response) use ($now) {
if ($this->isExpired($response, $now)) {
$value = null;
} else {
$value = $this->unserialize(
$response[$this->valueAttribute]['S'] ??
$response[$this->valueAttribute]['N'] ??
null
);
}
return [Str::replaceFirst($this->prefix, '', $response[$this->keyAttribute]['S']) => $value];
})->all());
}
/**
* Determine if the given item is expired.
*
* @param array $item
* @param \DateTimeInterface|null $expiration
* @return bool
*/
protected function isExpired(array $item, $expiration = null)
{
$expiration = $expiration ?: Carbon::now();
return isset($item[$this->expirationAttribute]) &&
$expiration->getTimestamp() >= $item[$this->expirationAttribute]['N'];
}
/**
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function put($key, $value, $seconds)
{
$this->dynamo->putItem([
'TableName' => $this->table,
'Item' => [
$this->keyAttribute => [
'S' => $this->prefix.$key,
],
$this->valueAttribute => [
$this->type($value) => $this->serialize($value),
],
$this->expirationAttribute => [
'N' => (string) $this->toTimestamp($seconds),
],
],
]);
return true;
}
/**
* Store multiple items in the cache for a given number of $seconds.
*
* @param array $values
* @param int $seconds
* @return bool
*/
public function putMany(array $values, $seconds)
{
$expiration = $this->toTimestamp($seconds);
$this->dynamo->batchWriteItem([
'RequestItems' => [
$this->table => collect($values)->map(function ($value, $key) use ($expiration) {
return [
'PutRequest' => [
'Item' => [
$this->keyAttribute => [
'S' => $this->prefix.$key,
],
$this->valueAttribute => [
$this->type($value) => $this->serialize($value),
],
$this->expirationAttribute => [
'N' => (string) $expiration,
],
],
],
];
})->values()->all(),
],
]);
return true;
}
/**
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function add($key, $value, $seconds)
{
try {
$this->dynamo->putItem([
'TableName' => $this->table,
'Item' => [
$this->keyAttribute => [
'S' => $this->prefix.$key,
],
$this->valueAttribute => [
$this->type($value) => $this->serialize($value),
],
$this->expirationAttribute => [
'N' => (string) $this->toTimestamp($seconds),
],
],
'ConditionExpression' => 'attribute_not_exists(#key) OR #expires_at < :now',
'ExpressionAttributeNames' => [
'#key' => $this->keyAttribute,
'#expires_at' => $this->expirationAttribute,
],
'ExpressionAttributeValues' => [
':now' => [
'N' => (string) Carbon::now()->getTimestamp(),
],
],
]);
return true;
} catch (DynamoDbException $e) {
if (Str::contains($e->getMessage(), 'ConditionalCheckFailed')) {
return false;
}
throw $e;
}
}
/**
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return int|bool
*/
public function increment($key, $value = 1)
{
try {
$response = $this->dynamo->updateItem([
'TableName' => $this->table,
'Key' => [
$this->keyAttribute => [
'S' => $this->prefix.$key,
],
],
'ConditionExpression' => 'attribute_exists(#key) AND #expires_at > :now',
'UpdateExpression' => 'SET #value = #value + :amount',
'ExpressionAttributeNames' => [
'#key' => $this->keyAttribute,
'#value' => $this->valueAttribute,
'#expires_at' => $this->expirationAttribute,
],
'ExpressionAttributeValues' => [
':now' => [
'N' => (string) Carbon::now()->getTimestamp(),
],
':amount' => [
'N' => (string) $value,
],
],
'ReturnValues' => 'UPDATED_NEW',
]);
return (int) $response['Attributes'][$this->valueAttribute]['N'];
} catch (DynamoDbException $e) {
if (Str::contains($e->getMessage(), 'ConditionalCheckFailed')) {
return false;
}
throw $e;
}
}
/**
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return int|bool
*/
public function decrement($key, $value = 1)
{
try {
$response = $this->dynamo->updateItem([
'TableName' => $this->table,
'Key' => [
$this->keyAttribute => [
'S' => $this->prefix.$key,
],
],
'ConditionExpression' => 'attribute_exists(#key) AND #expires_at > :now',
'UpdateExpression' => 'SET #value = #value - :amount',
'ExpressionAttributeNames' => [
'#key' => $this->keyAttribute,
'#value' => $this->valueAttribute,
'#expires_at' => $this->expirationAttribute,
],
'ExpressionAttributeValues' => [
':now' => [
'N' => (string) Carbon::now()->getTimestamp(),
],
':amount' => [
'N' => (string) $value,
],
],
'ReturnValues' => 'UPDATED_NEW',
]);
return (int) $response['Attributes'][$this->valueAttribute]['N'];
} catch (DynamoDbException $e) {
if (Str::contains($e->getMessage(), 'ConditionalCheckFailed')) {
return false;
}
throw $e;
}
}
/**
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
{
return $this->put($key, $value, now()->addYears(5)->getTimestamp());
}
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)
{
return new DynamoDbLock($this, $this->prefix.$name, $seconds, $owner);
}
/**
* Restore a lock instance using the owner identifier.
*
* @param string $name
* @param string $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function restoreLock($name, $owner)
{
return $this->lock($name, 0, $owner);
}
/**
* Remove an item from the cache.
*
* @param string $key
* @return bool
*/
public function forget($key)
{
$this->dynamo->deleteItem([
'TableName' => $this->table,
'Key' => [
$this->keyAttribute => [
'S' => $this->prefix.$key,
],
],
]);
return true;
}
/**
* Remove all items from the cache.
*
* @return bool
*/
public function flush()
{
throw new RuntimeException('DynamoDb does not support flushing an entire table. Please create a new table.');
}
/**
* Get the UNIX timestamp for the given number of seconds.
*
* @param int $seconds
* @return int
*/
protected function toTimestamp($seconds)
{
return $seconds > 0
? $this->availableAt($seconds)
: Carbon::now()->getTimestamp();
}
/**
* Serialize the value.
*
* @param mixed $value
* @return mixed
*/
protected function serialize($value)
{
return is_numeric($value) ? (string) $value : serialize($value);
}
/**
* Unserialize the value.
*
* @param mixed $value
* @return mixed
*/
protected function unserialize($value)
{
if (filter_var($value, FILTER_VALIDATE_INT) !== false) {
return (int) $value;
}
if (is_numeric($value)) {
return (float) $value;
}
return unserialize($value);
}
/**
* Get the DynamoDB type for the given value.
*
* @param mixed $value
* @return string
*/
protected function type($value)
{
return is_numeric($value) ? 'N' : 'S';
}
/**
* Get the cache key prefix.
*
* @return string
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* Set the cache key prefix.
*
* @param string $prefix
* @return void
*/
public function setPrefix($prefix)
{
$this->prefix = ! empty($prefix) ? $prefix.':' : '';
}
}

View File

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

View File

@@ -50,20 +50,22 @@ class FileStore implements Store
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$this->ensureCacheDirectoryExists($path = $this->path($key));
$this->files->put(
$path, $this->expiration($minutes).serialize($value), true
$result = $this->files->put(
$path, $this->expiration($seconds).serialize($value), true
);
return $result !== false && $result > 0;
}
/**
@@ -112,11 +114,11 @@ class FileStore implements Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->put($key, $value, 0);
return $this->put($key, $value, 0);
}
/**
@@ -184,12 +186,18 @@ class FileStore implements Store
return $this->emptyPayload();
}
$data = unserialize(substr($contents, 10));
try {
$data = unserialize(substr($contents, 10));
} catch (Exception $e) {
$this->forget($key);
// Next, we'll extract the number of minutes that are remaining for a cache
return $this->emptyPayload();
}
// Next, we'll extract the number of seconds that are remaining for a cache
// so that we can properly retain the time for things like the increment
// operation that may be performed on this cache on a later operation.
$time = ($expire - $this->currentTime()) / 60;
$time = $expire - $this->currentTime();
return compact('data', 'time');
}
@@ -218,16 +226,16 @@ class FileStore implements Store
}
/**
* Get the expiration time based on the given minutes.
* Get the expiration time based on the given seconds.
*
* @param float|int $minutes
* @param int $seconds
* @return int
*/
protected function expiration($minutes)
protected function expiration($seconds)
{
$time = $this->availableAt((int) ($minutes * 60));
$time = $this->availableAt($seconds);
return $minutes === 0 || $time > 9999999999 ? 9999999999 : (int) $time;
return $seconds === 0 || $time > 9999999999 ? 9999999999 : $time;
}
/**

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -2,10 +2,12 @@
namespace Illuminate\Cache;
use Illuminate\Support\Str;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\Lock as LockContract;
use Illuminate\Contracts\Cache\LockTimeoutException;
abstract class Lock
abstract class Lock implements LockContract
{
use InteractsWithTime;
@@ -23,16 +25,29 @@ abstract class Lock
*/
protected $seconds;
/**
* The scope identifier of this lock.
*
* @var string
*/
protected $owner;
/**
* Create a new lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return void
*/
public function __construct($name, $seconds)
public function __construct($name, $seconds, $owner = null)
{
if (is_null($owner)) {
$owner = Str::random();
}
$this->name = $name;
$this->owner = $owner;
$this->seconds = $seconds;
}
@@ -43,20 +58,36 @@ abstract class Lock
*/
abstract public function acquire();
/**
* Release the lock.
*
* @return void
*/
abstract public function release();
/**
* Returns the owner value written into the driver for this lock.
*
* @return string
*/
abstract protected function getCurrentOwner();
/**
* Attempt to acquire the lock.
*
* @param callable|null $callback
* @return bool
* @return mixed
*/
public function get($callback = null)
{
$result = $this->acquire();
if ($result && is_callable($callback)) {
return tap($callback(), function () {
try {
return $callback();
} finally {
$this->release();
});
}
}
return $result;
@@ -68,6 +99,7 @@ abstract class Lock
* @param int $seconds
* @param callable|null $callback
* @return bool
*
* @throws \Illuminate\Contracts\Cache\LockTimeoutException
*/
public function block($seconds, $callback = null)
@@ -83,11 +115,33 @@ abstract class Lock
}
if (is_callable($callback)) {
return tap($callback(), function () {
try {
return $callback();
} finally {
$this->release();
});
}
}
return true;
}
/**
* Returns the current owner of the lock.
*
* @return string
*/
public function owner()
{
return $this->owner;
}
/**
* Determines whether this lock is allowed to release the lock in the driver.
*
* @return bool
*/
protected function isOwnedByCurrentProcess()
{
return $this->getCurrentOwner() === $this->owner;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Illuminate\Cache;
class LuaScripts
{
/**
* Get the Lua script to atomically release a lock.
*
* KEYS[1] - The name of the lock
* ARGV[1] - The owner key of the lock instance trying to release it
*
* @return string
*/
public static function releaseLock()
{
return <<<'LUA'
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
LUA;
}
}

View File

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

View File

@@ -2,9 +2,7 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Lock as LockContract;
class MemcachedLock extends Lock implements LockContract
class MemcachedLock extends Lock
{
/**
* The Memcached instance.
@@ -19,11 +17,12 @@ class MemcachedLock extends Lock implements LockContract
* @param \Memcached $memcached
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return void
*/
public function __construct($memcached, $name, $seconds)
public function __construct($memcached, $name, $seconds, $owner = null)
{
parent::__construct($name, $seconds);
parent::__construct($name, $seconds, $owner);
$this->memcached = $memcached;
}
@@ -36,7 +35,7 @@ class MemcachedLock extends Lock implements LockContract
public function acquire()
{
return $this->memcached->add(
$this->name, 1, $this->seconds
$this->name, $this->owner, $this->seconds
);
}
@@ -46,7 +45,29 @@ class MemcachedLock extends Lock implements LockContract
* @return void
*/
public function release()
{
if ($this->isOwnedByCurrentProcess()) {
$this->memcached->delete($this->name);
}
}
/**
* Releases this lock in disregard of ownership.
*
* @return void
*/
public function forceRelease()
{
$this->memcached->delete($this->name);
}
/**
* Returns the owner value written into the driver for this lock.
*
* @return mixed
*/
protected function getCurrentOwner()
{
return $this->memcached->get($this->name);
}
}

View File

@@ -4,11 +4,10 @@ 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 LockProvider, Store
class MemcachedStore extends TaggableStore implements LockProvider
{
use InteractsWithTime;
@@ -94,26 +93,28 @@ class MemcachedStore extends TaggableStore implements LockProvider, Store
}
/**
* Store an item in the cache for a given number of minutes.
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function put($key, $value, $minutes)
public function put($key, $value, $seconds)
{
$this->memcached->set($this->prefix.$key, $value, $this->toTimestamp($minutes));
return $this->memcached->set(
$this->prefix.$key, $value, $this->calculateExpiration($seconds)
);
}
/**
* Store multiple items in the cache for a given number of minutes.
* Store multiple items in the cache for a given number of seconds.
*
* @param array $values
* @param float|int $minutes
* @return void
* @param int $seconds
* @return bool
*/
public function putMany(array $values, $minutes)
public function putMany(array $values, $seconds)
{
$prefixedValues = [];
@@ -121,7 +122,9 @@ class MemcachedStore extends TaggableStore implements LockProvider, Store
$prefixedValues[$this->prefix.$key] = $value;
}
$this->memcached->setMulti($prefixedValues, $this->toTimestamp($minutes));
return $this->memcached->setMulti(
$prefixedValues, $this->calculateExpiration($seconds)
);
}
/**
@@ -129,12 +132,14 @@ class MemcachedStore extends TaggableStore implements LockProvider, Store
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @param int $seconds
* @return bool
*/
public function add($key, $value, $minutes)
public function add($key, $value, $seconds)
{
return $this->memcached->add($this->prefix.$key, $value, $this->toTimestamp($minutes));
return $this->memcached->add(
$this->prefix.$key, $value, $this->calculateExpiration($seconds)
);
}
/**
@@ -166,23 +171,36 @@ class MemcachedStore extends TaggableStore implements LockProvider, Store
*
* @param string $key
* @param mixed $value
* @return void
* @return bool
*/
public function forever($key, $value)
{
$this->put($key, $value, 0);
return $this->put($key, $value, 0);
}
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0)
public function lock($name, $seconds = 0, $owner = null)
{
return new MemcachedLock($this->memcached, $this->prefix.$name, $seconds);
return new MemcachedLock($this->memcached, $this->prefix.$name, $seconds, $owner);
}
/**
* Restore a lock instance using the owner identifier.
*
* @param string $name
* @param string $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function restoreLock($name, $owner)
{
return $this->lock($name, 0, $owner);
}
/**
@@ -207,14 +225,25 @@ class MemcachedStore extends TaggableStore implements LockProvider, Store
}
/**
* Get the UNIX timestamp for the given number of minutes.
* Get the expiration time of the key.
*
* @param int $minutes
* @param int $seconds
* @return int
*/
protected function toTimestamp($minutes)
protected function calculateExpiration($seconds)
{
return $minutes > 0 ? $this->availableAt($minutes * 60) : 0;
return $this->toTimestamp($seconds);
}
/**
* Get the UNIX timestamp for the given number of seconds.
*
* @param int $seconds
* @return int
*/
protected function toTimestamp($seconds)
{
return $seconds > 0 ? $this->availableAt($seconds) : 0;
}
/**

View File

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

View File

@@ -51,21 +51,21 @@ class RateLimiter
* Increment the counter for a given key for a given decay time.
*
* @param string $key
* @param float|int $decayMinutes
* @param int $decaySeconds
* @return int
*/
public function hit($key, $decayMinutes = 1)
public function hit($key, $decaySeconds = 60)
{
$this->cache->add(
$key.':timer', $this->availableAt($decayMinutes * 60), $decayMinutes
$key.':timer', $this->availableAt($decaySeconds), $decaySeconds
);
$added = $this->cache->add($key, 0, $decayMinutes);
$added = $this->cache->add($key, 0, $decaySeconds);
$hits = (int) $this->cache->increment($key);
if (! $added && $hits == 1) {
$this->cache->put($key, 1, $decayMinutes);
$this->cache->put($key, 1, $decaySeconds);
}
return $hits;

View File

@@ -2,9 +2,7 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\Lock as LockContract;
class RedisLock extends Lock implements LockContract
class RedisLock extends Lock
{
/**
* The Redis factory implementation.
@@ -19,11 +17,12 @@ class RedisLock extends Lock implements LockContract
* @param \Illuminate\Redis\Connections\Connection $redis
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return void
*/
public function __construct($redis, $name, $seconds)
public function __construct($redis, $name, $seconds, $owner = null)
{
parent::__construct($name, $seconds);
parent::__construct($name, $seconds, $owner);
$this->redis = $redis;
}
@@ -35,7 +34,7 @@ class RedisLock extends Lock implements LockContract
*/
public function acquire()
{
$result = $this->redis->setnx($this->name, 1);
$result = $this->redis->setnx($this->name, $this->owner);
if ($result === 1 && $this->seconds > 0) {
$this->redis->expire($this->name, $this->seconds);
@@ -50,7 +49,27 @@ class RedisLock extends Lock implements LockContract
* @return void
*/
public function release()
{
$this->redis->eval(LuaScripts::releaseLock(), 1, $this->name, $this->owner);
}
/**
* Releases this lock in disregard of ownership.
*
* @return void
*/
public function forceRelease()
{
$this->redis->del($this->name);
}
/**
* Returns the owner value written into the driver for this lock.
*
* @return string
*/
protected function getCurrentOwner()
{
return $this->redis->get($this->name);
}
}

View File

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

View File

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

View File

@@ -42,11 +42,11 @@ class Repository implements CacheContract, ArrayAccess
protected $events;
/**
* The default number of minutes to store items.
* The default number of seconds to store items.
*
* @var float|int
* @var int|null
*/
protected $default = 60;
protected $default = 3600;
/**
* Create a new cache repository instance.
@@ -70,11 +70,22 @@ class Repository implements CacheContract, ArrayAccess
return ! is_null($this->get($key));
}
/**
* Determine if an item doesn't exist in the cache.
*
* @param string $key
* @return bool
*/
public function missing($key)
{
return ! $this->has($key);
}
/**
* Retrieve an item from the cache by key.
*
* @param string $key
* @param mixed $default
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null)
@@ -123,17 +134,13 @@ class Repository implements CacheContract, ArrayAccess
*/
public function getMultiple($keys, $default = null)
{
if (is_null($default)) {
return $this->many($keys);
}
$defaults = [];
foreach ($keys as $key) {
if (! isset($default[$key])) {
$default[$key] = null;
}
$defaults[$key] = $default;
}
return $this->many($default);
return $this->many($defaults);
}
/**
@@ -167,7 +174,7 @@ class Repository implements CacheContract, ArrayAccess
* Retrieve an item from the cache and delete it.
*
* @param string $key
* @param mixed $default
* @param mixed $default
* @return mixed
*/
public function pull($key, $default = null)
@@ -181,21 +188,33 @@ class Repository implements CacheContract, ArrayAccess
* Store an item in the cache.
*
* @param string $key
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|float|int|null $minutes
* @return void
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function put($key, $value, $minutes = null)
public function put($key, $value, $ttl = null)
{
if (is_array($key)) {
return $this->putMany($key, $value);
}
if (! is_null($minutes = $this->getMinutes($minutes))) {
$this->store->put($this->itemKey($key), $value, $minutes);
$this->event(new KeyWritten($key, $value, $minutes));
if ($ttl === null) {
return $this->forever($key, $value);
}
$seconds = $this->getSeconds($ttl);
if ($seconds <= 0) {
return $this->forget($key);
}
$result = $this->store->put($this->itemKey($key), $value, $seconds);
if ($result) {
$this->event(new KeyWritten($key, $value, $seconds));
}
return $result;
}
/**
@@ -203,25 +222,56 @@ class Repository implements CacheContract, ArrayAccess
*/
public function set($key, $value, $ttl = null)
{
$this->put($key, $value, $ttl);
return $this->put($key, $value, $ttl);
}
/**
* Store multiple items in the cache for a given number of minutes.
* Store multiple items in the cache for a given number of seconds.
*
* @param array $values
* @param \DateTimeInterface|\DateInterval|float|int $minutes
* @return void
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function putMany(array $values, $minutes)
public function putMany(array $values, $ttl = null)
{
if (! is_null($minutes = $this->getMinutes($minutes))) {
$this->store->putMany($values, $minutes);
if ($ttl === null) {
return $this->putManyForever($values);
}
$seconds = $this->getSeconds($ttl);
if ($seconds <= 0) {
return $this->deleteMultiple(array_keys($values));
}
$result = $this->store->putMany($values, $seconds);
if ($result) {
foreach ($values as $key => $value) {
$this->event(new KeyWritten($key, $value, $minutes));
$this->event(new KeyWritten($key, $value, $seconds));
}
}
return $result;
}
/**
* Store multiple items in the cache indefinitely.
*
* @param array $values
* @return bool
*/
protected function putManyForever(array $values)
{
$result = true;
foreach ($values as $key => $value) {
if (! $this->forever($key, $value)) {
$result = false;
}
}
return $result;
}
/**
@@ -229,39 +279,41 @@ class Repository implements CacheContract, ArrayAccess
*/
public function setMultiple($values, $ttl = null)
{
$this->putMany($values, $ttl);
return $this->putMany(is_array($values) ? $values : iterator_to_array($values), $ttl);
}
/**
* Store an item in the cache if the key does not exist.
*
* @param string $key
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|float|int $minutes
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
public function add($key, $value, $minutes)
public function add($key, $value, $ttl = null)
{
if (is_null($minutes = $this->getMinutes($minutes))) {
return false;
}
if ($ttl !== null) {
if ($this->getSeconds($ttl) <= 0) {
return false;
}
// If the store has an "add" method we will call the method on the store so it
// has a chance to override this logic. Some drivers better support the way
// this operation should work with a total "atomic" implementation of it.
if (method_exists($this->store, 'add')) {
return $this->store->add(
$this->itemKey($key), $value, $minutes
);
// If the store has an "add" method we will call the method on the store so it
// has a chance to override this logic. Some drivers better support the way
// this operation should work with a total "atomic" implementation of it.
if (method_exists($this->store, 'add')) {
$seconds = $this->getSeconds($ttl);
return $this->store->add(
$this->itemKey($key), $value, $seconds
);
}
}
// If the value did not exist in the cache, we will put the value in the cache
// so it exists for subsequent requests. Then, we will return true so it is
// easy to know if the value gets added. Otherwise, we will return false.
if (is_null($this->get($key))) {
$this->put($key, $value, $minutes);
return true;
return $this->put($key, $value, $ttl);
}
return false;
@@ -295,44 +347,48 @@ class Repository implements CacheContract, ArrayAccess
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @return void
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
{
$this->store->forever($this->itemKey($key), $value);
$result = $this->store->forever($this->itemKey($key), $value);
$this->event(new KeyWritten($key, $value, 0));
if ($result) {
$this->event(new KeyWritten($key, $value));
}
return $result;
}
/**
* Get an item from the cache, or store the default value.
* Get an item from the cache, or execute the given Closure and store the result.
*
* @param string $key
* @param \DateTimeInterface|\DateInterval|float|int $minutes
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @param \Closure $callback
* @return mixed
*/
public function remember($key, $minutes, Closure $callback)
public function remember($key, $ttl, Closure $callback)
{
$value = $this->get($key);
// If the item exists in the cache we will just return this immediately and if
// not we will execute the given Closure and cache the result of that for a
// given number of minutes so it's available for all subsequent requests.
// given number of seconds so it's available for all subsequent requests.
if (! is_null($value)) {
return $value;
}
$this->put($key, $value = $callback(), $minutes);
$this->put($key, $value = $callback(), $ttl);
return $value;
}
/**
* Get an item from the cache, or store the default value forever.
* Get an item from the cache, or execute the given Closure and store the result forever.
*
* @param string $key
* @param string $key
* @param \Closure $callback
* @return mixed
*/
@@ -342,9 +398,9 @@ class Repository implements CacheContract, ArrayAccess
}
/**
* Get an item from the cache, or store the default value forever.
* Get an item from the cache, or execute the given Closure and store the result forever.
*
* @param string $key
* @param string $key
* @param \Closure $callback
* @return mixed
*/
@@ -352,9 +408,9 @@ class Repository implements CacheContract, ArrayAccess
{
$value = $this->get($key);
// If the item exists in the cache we will just return this immediately and if
// not we will execute the given Closure and cache the result of that for a
// given number of minutes so it's available for all subsequent requests.
// If the item exists in the cache we will just return this immediately
// and if not we will execute the given Closure and cache the result
// of that forever so it is available for all subsequent requests.
if (! is_null($value)) {
return $value;
}
@@ -372,8 +428,10 @@ class Repository implements CacheContract, ArrayAccess
*/
public function forget($key)
{
return tap($this->store->forget($this->itemKey($key)), function () use ($key) {
$this->event(new KeyForgotten($key));
return tap($this->store->forget($this->itemKey($key)), function ($result) use ($key) {
if ($result) {
$this->event(new KeyForgotten($key));
}
});
}
@@ -390,11 +448,15 @@ class Repository implements CacheContract, ArrayAccess
*/
public function deleteMultiple($keys)
{
$result = true;
foreach ($keys as $key) {
$this->forget($key);
if (! $this->forget($key)) {
$result = false;
}
}
return true;
return $result;
}
/**
@@ -442,7 +504,7 @@ class Repository implements CacheContract, ArrayAccess
/**
* Get the default cache time.
*
* @return float|int
* @return int
*/
public function getDefaultCacheTime()
{
@@ -450,14 +512,14 @@ class Repository implements CacheContract, ArrayAccess
}
/**
* Set the default cache time in minutes.
* Set the default cache time in seconds.
*
* @param float|int $minutes
* @param int|null $seconds
* @return $this
*/
public function setDefaultCacheTime($minutes)
public function setDefaultCacheTime($seconds)
{
$this->default = $minutes;
$this->default = $seconds;
return $this;
}
@@ -542,27 +604,27 @@ class Repository implements CacheContract, ArrayAccess
}
/**
* Calculate the number of minutes with the given duration.
* Calculate the number of seconds for the given TTL.
*
* @param \DateTimeInterface|\DateInterval|float|int $duration
* @return float|int|null
* @param \DateTimeInterface|\DateInterval|int $ttl
* @return int
*/
protected function getMinutes($duration)
protected function getSeconds($ttl)
{
$duration = $this->parseDateInterval($duration);
$duration = $this->parseDateInterval($ttl);
if ($duration instanceof DateTimeInterface) {
$duration = Carbon::now()->diffInSeconds(Carbon::createFromTimestamp($duration->getTimestamp()), false) / 60;
$duration = Carbon::now()->diffInRealSeconds($duration, false);
}
return (int) ($duration * 60) > 0 ? $duration : null;
return (int) $duration > 0 ? $duration : 0;
}
/**
* Handle dynamic calls into macros or pass missing methods to the store.
*
* @param string $method
* @param array $parameters
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)

View File

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

View File

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

View File

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

View File

@@ -15,8 +15,8 @@
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.6.*",
"illuminate/support": "5.6.*"
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@@ -25,13 +25,13 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.6-dev"
"dev-master": "5.8-dev"
}
},
"suggest": {
"illuminate/database": "Required to use the database cache driver (5.6.*).",
"illuminate/filesystem": "Required to use the file cache driver (5.6.*).",
"illuminate/redis": "Required to use the redis cache driver (5.6.*)."
"illuminate/database": "Required to use the database cache driver (5.8.*).",
"illuminate/filesystem": "Required to use the file cache driver (5.8.*).",
"illuminate/redis": "Required to use the redis cache driver (5.8.*)."
},
"config": {
"sort-packages": true

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -65,7 +65,7 @@ class Repository implements ArrayAccess, ConfigContract
foreach ($keys as $key => $default) {
if (is_numeric($key)) {
list($key, $default) = [$default, null];
[$key, $default] = [$default, null];
}
$config[$key] = Arr::get($this->items, $key, $default);

View File

@@ -15,8 +15,8 @@
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.6.*",
"illuminate/support": "5.6.*"
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*"
},
"autoload": {
"psr-4": {
@@ -25,7 +25,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.6-dev"
"dev-master": "5.8-dev"
}
},
"config": {

View File

@@ -9,6 +9,7 @@ use Illuminate\Contracts\Container\Container;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
@@ -80,7 +81,7 @@ class Application extends SymfonyApplication implements ApplicationContract
$input = $input ?: new ArgvInput
);
$this->events->fire(
$this->events->dispatch(
new Events\CommandStarting(
$commandName, $input, $output = $output ?: new ConsoleOutput
)
@@ -88,7 +89,7 @@ class Application extends SymfonyApplication implements ApplicationContract
$exitCode = parent::run($input, $output);
$this->events->fire(
$this->events->dispatch(
new Events\CommandFinished($commandName, $input, $output, $exitCode)
);
@@ -171,25 +172,41 @@ class Application extends SymfonyApplication implements ApplicationContract
*/
public function call($command, array $parameters = [], $outputBuffer = null)
{
if (is_subclass_of($command, SymfonyCommand::class)) {
$command = $this->laravel->make($command)->getName();
}
[$command, $input] = $this->parseCommand($command, $parameters);
if (! $this->has($command)) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $command));
}
array_unshift($parameters, $command);
return $this->run(
$input, $this->lastOutput = $outputBuffer ?: new BufferedOutput
);
}
$this->lastOutput = $outputBuffer ?: new BufferedOutput;
/**
* Parse the incoming Artisan command and its input.
*
* @param string $command
* @param array $parameters
* @return array
*/
protected function parseCommand($command, $parameters)
{
if (is_subclass_of($command, SymfonyCommand::class)) {
$callingClass = true;
$this->setCatchExceptions(false);
$command = $this->laravel->make($command)->getName();
}
$result = $this->run(new ArrayInput($parameters), $this->lastOutput);
if (! isset($callingClass) && empty($parameters)) {
$command = $this->getCommandName($input = new StringInput($command));
} else {
array_unshift($parameters, $command);
$this->setCatchExceptions(true);
$input = new ArrayInput($parameters);
}
return $result;
return [$command, $input ?? null];
}
/**
@@ -259,7 +276,7 @@ class Application extends SymfonyApplication implements ApplicationContract
}
/**
* Get the default input definitions for the applications.
* Get the default input definition for the application.
*
* This is used to add the --env option to every available command.
*

View File

@@ -109,7 +109,7 @@ class Command extends SymfonyCommand
// the command we'll set the arguments and the options on this command.
$this->setDescription($this->description);
$this->setHidden($this->hidden);
$this->setHidden($this->isHidden());
if (! isset($this->signature)) {
$this->specifyParameters();
@@ -123,7 +123,7 @@ class Command extends SymfonyCommand
*/
protected function configureUsingFluentDefinition()
{
list($name, $arguments, $options) = Parser::parse($this->signature);
[$name, $arguments, $options] = Parser::parse($this->signature);
parent::__construct($this->name = $name);
@@ -162,8 +162,12 @@ class Command extends SymfonyCommand
*/
public function run(InputInterface $input, OutputInterface $output)
{
$this->output = $this->laravel->make(
OutputStyle::class, ['input' => $input, 'output' => $output]
);
return parent::run(
$this->input = $input, $this->output = new OutputStyle($input, $output)
$this->input = $input, $this->output
);
}
@@ -219,13 +223,31 @@ class Command extends SymfonyCommand
*/
protected function createInputFromArguments(array $arguments)
{
return tap(new ArrayInput($arguments), function ($input) {
return tap(new ArrayInput(array_merge($this->context(), $arguments)), function ($input) {
if ($input->hasParameterOption(['--no-interaction'], true)) {
$input->setInteractive(false);
}
});
}
/**
* Get all of the context passed to the command.
*
* @return array
*/
protected function context()
{
return collect($this->option())->only([
'ansi',
'no-ansi',
'no-interaction',
'quiet',
'verbose',
])->filter()->mapWithKeys(function ($value, $key) {
return ["--{$key}" => $value];
})->all();
}
/**
* Determine if the given argument is present.
*
@@ -277,7 +299,7 @@ class Command extends SymfonyCommand
* Get the value of a command option.
*
* @param string|null $key
* @return string|array|null
* @return string|array|bool|null
*/
public function option($key = null)
{
@@ -429,7 +451,7 @@ class Command extends SymfonyCommand
* Write a string as standard output.
*
* @param string $string
* @param string $style
* @param string|null $style
* @param int|string|null $verbosity
* @return void
*/
@@ -539,6 +561,24 @@ class Command extends SymfonyCommand
return $level;
}
/**
* {@inheritdoc}
*/
public function isHidden()
{
return $this->hidden;
}
/**
* {@inheritdoc}
*/
public function setHidden($hidden)
{
parent::setHidden($this->hidden = $hidden);
return $this;
}
/**
* Get the console command arguments.
*
@@ -562,7 +602,7 @@ class Command extends SymfonyCommand
/**
* Get the output implementation.
*
* @return \Symfony\Component\Console\Output\OutputInterface
* @return \Illuminate\Console\OutputStyle
*/
public function getOutput()
{

View File

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

View File

@@ -46,6 +46,7 @@ abstract class GeneratorCommand extends Command
* Execute the console command.
*
* @return bool|null
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function handle()
{
@@ -152,6 +153,7 @@ abstract class GeneratorCommand extends Command
*
* @param string $name
* @return string
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function buildClass($name)
{
@@ -171,7 +173,7 @@ abstract class GeneratorCommand extends Command
{
$stub = str_replace(
['DummyNamespace', 'DummyRootNamespace', 'NamespacedDummyUserModel'],
[$this->getNamespace($name), $this->rootNamespace(), config('auth.providers.users.model')],
[$this->getNamespace($name), $this->rootNamespace(), $this->userProviderModel()],
$stub
);
@@ -223,6 +225,20 @@ abstract class GeneratorCommand extends Command
return $this->laravel->getNamespace();
}
/**
* Get the model for the default guard's user provider.
*
* @return string|null
*/
protected function userProviderModel()
{
$guard = config('auth.defaults.guard');
$provider = config("auth.guards.{$guard}.provider");
return config("auth.providers.{$provider}.model");
}
/**
* Get the console command arguments.
*

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -82,7 +82,7 @@ class Parser
*/
protected static function parseArgument($token)
{
list($token, $description) = static::extractDescription($token);
[$token, $description] = static::extractDescription($token);
switch (true) {
case Str::endsWith($token, '?*'):
@@ -108,7 +108,7 @@ class Parser
*/
protected static function parseOption($token)
{
list($token, $description) = static::extractDescription($token);
[$token, $description] = static::extractDescription($token);
$matches = preg_split('/\s*\|\s*/', $token, 2);

View File

@@ -40,7 +40,7 @@ class CacheEventMutex implements EventMutex
public function create(Event $event)
{
return $this->cache->store($this->store)->add(
$event->mutexName(), true, $event->expiresAt
$event->mutexName(), true, $event->expiresAt * 60
);
}

View File

@@ -42,7 +42,7 @@ class CacheSchedulingMutex implements SchedulingMutex
public function create(Event $event, DateTimeInterface $time)
{
return $this->cache->store($this->store)->add(
$event->mutexName().$time->format('Hi'), true, 60
$event->mutexName().$time->format('Hi'), true, 3600
);
}

View File

@@ -71,7 +71,9 @@ class CallbackEvent extends Event
parent::callBeforeCallbacks($container);
try {
$response = $container->call($this->callback, $this->parameters);
$response = is_object($this->callback)
? $container->call([$this->callback, '__invoke'], $this->parameters)
: $container->call($this->callback, $this->parameters);
} finally {
$this->removeMutex();
@@ -88,7 +90,7 @@ class CallbackEvent extends Event
*/
protected function removeMutex()
{
if ($this->description) {
if ($this->description && $this->withoutOverlapping) {
$this->mutex->forget($this);
}
}

View File

@@ -7,6 +7,7 @@ use Cron\CronExpression;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use GuzzleHttp\Client as HttpClient;
use Illuminate\Support\Facades\Date;
use Illuminate\Contracts\Mail\Mailer;
use Symfony\Component\Process\Process;
use Illuminate\Support\Traits\Macroable;
@@ -142,17 +143,27 @@ class Event
*/
public $mutex;
/**
* The exit status code of the command.
*
* @var int|null
*/
public $exitCode;
/**
* Create a new event instance.
*
* @param \Illuminate\Console\Scheduling\EventMutex $mutex
* @param string $command
* @param \DateTimeZone|string|null $timezone
* @return void
*/
public function __construct(EventMutex $mutex, $command)
public function __construct(EventMutex $mutex, $command, $timezone = null)
{
$this->mutex = $mutex;
$this->command = $command;
$this->timezone = $timezone;
$this->output = $this->getDefaultOutput();
}
@@ -163,7 +174,7 @@ class Event
*/
public function getDefaultOutput()
{
return (DIRECTORY_SEPARATOR == '\\') ? 'NUL' : '/dev/null';
return (DIRECTORY_SEPARATOR === '\\') ? 'NUL' : '/dev/null';
}
/**
@@ -204,9 +215,7 @@ class Event
{
$this->callBeforeCallbacks($container);
(new Process(
$this->buildCommand(), base_path(), null, null, null
))->run();
$this->exitCode = Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run();
$this->callAfterCallbacks($container);
}
@@ -221,9 +230,7 @@ class Event
{
$this->callBeforeCallbacks($container);
(new Process(
$this->buildCommand(), base_path(), null, null, null
))->run();
Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run();
}
/**
@@ -338,6 +345,18 @@ class Event
return true;
}
/**
* Ensure that the output is stored on disk in a log file.
*
* @return $this
*/
public function storeOutput()
{
$this->ensureOutputIsBeingCaptured();
return $this;
}
/**
* Send the output of the command to a given location.
*
@@ -376,7 +395,7 @@ class Event
*/
public function emailOutputTo($addresses, $onlyIfOutputExists = false)
{
$this->ensureOutputIsBeingCapturedForEmail();
$this->ensureOutputIsBeingCaptured();
$addresses = Arr::wrap($addresses);
@@ -399,11 +418,28 @@ class Event
}
/**
* Ensure that output is being captured for email.
* E-mail the results of the scheduled operation if it fails.
*
* @param array|mixed $addresses
* @return $this
*/
public function emailOutputOnFailure($addresses)
{
$this->ensureOutputIsBeingCaptured();
$addresses = Arr::wrap($addresses);
return $this->onFailure(function (Mailer $mailer) use ($addresses) {
$this->emailOutput($mailer, $addresses, false);
});
}
/**
* Ensure that the command output is being captured.
*
* @return void
*/
protected function ensureOutputIsBeingCapturedForEmail()
protected function ensureOutputIsBeingCaptured()
{
if (is_null($this->output) || $this->output == $this->getDefaultOutput()) {
$this->sendOutputTo(storage_path('logs/schedule-'.sha1($this->mutexName()).'.log'));
@@ -495,6 +531,32 @@ class Event
return $value ? $this->thenPing($url) : $this;
}
/**
* Register a callback to ping a given URL if the operation succeeds.
*
* @param string $url
* @return $this
*/
public function pingOnSuccess($url)
{
return $this->onSuccess(function () use ($url) {
(new HttpClient)->get($url);
});
}
/**
* Register a callback to ping a given URL if the operation fails.
*
* @param string $url
* @return $this
*/
public function pingOnFailure($url)
{
return $this->onFailure(function () use ($url) {
(new HttpClient)->get($url);
});
}
/**
* State that the command should run in background.
*
@@ -643,6 +705,36 @@ class Event
return $this;
}
/**
* Register a callback to be called if the operation succeeds.
*
* @param \Closure $callback
* @return $this
*/
public function onSuccess(Closure $callback)
{
return $this->then(function (Container $container) use ($callback) {
if (0 === $this->exitCode) {
$container->call($callback);
}
});
}
/**
* Register a callback to be called if the operation fails.
*
* @param \Closure $callback
* @return $this
*/
public function onFailure(Closure $callback)
{
return $this->then(function (Container $container) use ($callback) {
if (0 !== $this->exitCode) {
$container->call($callback);
}
});
}
/**
* Set the human-friendly description of the event.
*
@@ -684,16 +776,16 @@ class Event
/**
* Determine the next due date for an event.
*
* @param \DateTime|string $currentTime
* @param \DateTimeInterface|string $currentTime
* @param int $nth
* @param bool $allowCurrentDate
* @return \Illuminate\Support\Carbon
*/
public function nextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
{
return Carbon::instance(CronExpression::factory(
return Date::instance(CronExpression::factory(
$this->getExpression()
)->getNextRunDate($currentTime, $nth, $allowCurrentDate));
)->getNextRunDate($currentTime, $nth, $allowCurrentDate, $this->timezone));
}
/**

View File

@@ -124,11 +124,13 @@ trait ManagesFrequencies
/**
* Schedule the event to run hourly at a given offset in the hour.
*
* @param int $offset
* @param array|int $offset
* @return $this
*/
public function hourlyAt($offset)
{
$offset = is_array($offset) ? implode(',', $offset) : $offset;
return $this->spliceIntoPosition(1, $offset);
}
@@ -165,7 +167,7 @@ trait ManagesFrequencies
$segments = explode(':', $time);
return $this->spliceIntoPosition(2, (int) $segments[0])
->spliceIntoPosition(1, count($segments) == 2 ? (int) $segments[1] : '0');
->spliceIntoPosition(1, count($segments) === 2 ? (int) $segments[1] : '0');
}
/**

View File

@@ -31,13 +31,23 @@ class Schedule
*/
protected $schedulingMutex;
/**
* The timezone the date should be evaluated on.
*
* @var \DateTimeZone|string
*/
protected $timezone;
/**
* Create a new schedule instance.
*
* @param \DateTimeZone|string|null $timezone
* @return void
*/
public function __construct()
public function __construct($timezone = null)
{
$this->timezone = $timezone;
$container = Container::getInstance();
$this->eventMutex = $container->bound(EventMutex::class)
@@ -53,7 +63,7 @@ class Schedule
* Add a new callback event to the schedule.
*
* @param string|callable $callback
* @param array $parameters
* @param array $parameters
* @return \Illuminate\Console\Scheduling\CallbackEvent
*/
public function call($callback, array $parameters = [])
@@ -88,15 +98,18 @@ class Schedule
*
* @param object|string $job
* @param string|null $queue
* @param string|null $connection
* @return \Illuminate\Console\Scheduling\CallbackEvent
*/
public function job($job, $queue = null)
public function job($job, $queue = null, $connection = null)
{
return $this->call(function () use ($job, $queue) {
return $this->call(function () use ($job, $queue, $connection) {
$job = is_string($job) ? resolve($job) : $job;
if ($job instanceof ShouldQueue) {
dispatch($job)->onQueue($queue);
dispatch($job)
->onConnection($connection ?? $job->connection)
->onQueue($queue ?? $job->queue);
} else {
dispatch_now($job);
}
@@ -116,7 +129,7 @@ class Schedule
$command .= ' '.$this->compileParameters($parameters);
}
$this->events[] = $event = new Event($this->eventMutex, $command);
$this->events[] = $event = new Event($this->eventMutex, $command, $this->timezone);
return $event;
}

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Console\Scheduling;
use Illuminate\Support\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Date;
class ScheduleRunCommand extends Command
{
@@ -52,7 +52,7 @@ class ScheduleRunCommand extends Command
{
$this->schedule = $schedule;
$this->startedAt = Carbon::now();
$this->startedAt = Date::now();
parent::__construct();
}

View File

@@ -15,9 +15,10 @@
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.6.*",
"illuminate/support": "5.6.*",
"symfony/console": "~4.0"
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*",
"symfony/console": "^4.2",
"symfony/process": "^4.2"
},
"autoload": {
"psr-4": {
@@ -26,13 +27,13 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.6-dev"
"dev-master": "5.8-dev"
}
},
"suggest": {
"dragonmantank/cron-expression": "Required to use scheduling component (~2.0).",
"guzzlehttp/guzzle": "Required to use the ping methods on schedules (~6.0).",
"symfony/process": "Required to use scheduling component (~4.0)."
"dragonmantank/cron-expression": "Required to use scheduling component (^2.0).",
"guzzlehttp/guzzle": "Required to use the ping methods on schedules (^6.0).",
"illuminate/filesystem": "Required to use the generator command (5.8.*)"
},
"config": {
"sort-packages": true

View File

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

View File

@@ -3,10 +3,12 @@
namespace Illuminate\Container;
use Closure;
use Exception;
use ArrayAccess;
use LogicException;
use ReflectionClass;
use ReflectionParameter;
use Illuminate\Support\Arr;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Container\Container as ContainerContract;
@@ -22,124 +24,130 @@ class Container implements ArrayAccess, ContainerContract
/**
* An array of the types that have been resolved.
*
* @var array
* @var bool[]
*/
protected $resolved = [];
/**
* The container's bindings.
*
* @var array
* @var array[]
*/
protected $bindings = [];
/**
* The container's method bindings.
*
* @var array
* @var \Closure[]
*/
protected $methodBindings = [];
/**
* The container's shared instances.
*
* @var array
* @var object[]
*/
protected $instances = [];
/**
* The registered type aliases.
*
* @var array
* @var string[]
*/
protected $aliases = [];
/**
* The registered aliases keyed by the abstract name.
*
* @var array
* @var array[]
*/
protected $abstractAliases = [];
/**
* The extension closures for services.
*
* @var array
* @var array[]
*/
protected $extenders = [];
/**
* All of the registered tags.
*
* @var array
* @var array[]
*/
protected $tags = [];
/**
* The stack of concretions currently being built.
*
* @var array
* @var array[]
*/
protected $buildStack = [];
/**
* The parameter override stack.
*
* @var array
* @var array[]
*/
protected $with = [];
/**
* The contextual binding map.
*
* @var array
* @var array[]
*/
public $contextual = [];
/**
* All of the registered rebound callbacks.
*
* @var array
* @var array[]
*/
protected $reboundCallbacks = [];
/**
* All of the global resolving callbacks.
*
* @var array
* @var \Closure[]
*/
protected $globalResolvingCallbacks = [];
/**
* All of the global after resolving callbacks.
*
* @var array
* @var \Closure[]
*/
protected $globalAfterResolvingCallbacks = [];
/**
* All of the resolving callbacks by class type.
*
* @var array
* @var array[]
*/
protected $resolvingCallbacks = [];
/**
* All of the after resolving callbacks by class type.
*
* @var array
* @var array[]
*/
protected $afterResolvingCallbacks = [];
/**
* Define a contextual binding.
*
* @param string $concrete
* @param array|string $concrete
* @return \Illuminate\Contracts\Container\ContextualBindingBuilder
*/
public function when($concrete)
{
return new ContextualBindingBuilder($this, $this->getAlias($concrete));
$aliases = [];
foreach (Arr::wrap($concrete) as $c) {
$aliases[] = $this->getAlias($c);
}
return new ContextualBindingBuilder($this, $aliases);
}
/**
@@ -188,7 +196,7 @@ class Container implements ArrayAccess, ContainerContract
public function isShared($abstract)
{
return isset($this->instances[$abstract]) ||
(isset($this->bindings[$abstract]['shared']) &&
(isset($this->bindings[$abstract]['shared']) &&
$this->bindings[$abstract]['shared'] === true);
}
@@ -213,11 +221,11 @@ class Container implements ArrayAccess, ContainerContract
*/
public function bind($abstract, $concrete = null, $shared = false)
{
$this->dropStaleInstances($abstract);
// If no concrete type was given, we will simply set the concrete type to the
// abstract type. After that, the concrete type to be registered as shared
// without being forced to state their classes in both of the parameters.
$this->dropStaleInstances($abstract);
if (is_null($concrete)) {
$concrete = $abstract;
}
@@ -253,7 +261,9 @@ class Container implements ArrayAccess, ContainerContract
return $container->build($concrete);
}
return $container->make($concrete, $parameters);
return $container->resolve(
$concrete, $parameters, $raiseEvents = false
);
};
}
@@ -447,19 +457,19 @@ class Container implements ArrayAccess, ContainerContract
* Resolve all of the bindings for a given tag.
*
* @param string $tag
* @return array
* @return iterable
*/
public function tagged($tag)
{
$results = [];
if (isset($this->tags[$tag])) {
foreach ($this->tags[$tag] as $abstract) {
$results[] = $this->make($abstract);
}
if (! isset($this->tags[$tag])) {
return [];
}
return $results;
return new RewindableGenerator(function () use ($tag) {
foreach ($this->tags[$tag] as $abstract) {
yield $this->make($abstract);
}
}, count($this->tags[$tag]));
}
/**
@@ -468,9 +478,15 @@ class Container implements ArrayAccess, ContainerContract
* @param string $abstract
* @param string $alias
* @return void
*
* @throws \LogicException
*/
public function alias($abstract, $alias)
{
if ($alias === $abstract) {
throw new LogicException("[{$abstract}] is aliased to itself.");
}
$this->aliases[$alias] = $abstract;
$this->abstractAliases[$abstract][] = $alias;
@@ -530,11 +546,7 @@ class Container implements ArrayAccess, ContainerContract
*/
protected function getReboundCallbacks($abstract)
{
if (isset($this->reboundCallbacks[$abstract])) {
return $this->reboundCallbacks[$abstract];
}
return [];
return $this->reboundCallbacks[$abstract] ?? [];
}
/**
@@ -595,6 +607,8 @@ class Container implements ArrayAccess, ContainerContract
* @param string $abstract
* @param array $parameters
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function make($abstract, array $parameters = [])
{
@@ -606,11 +620,15 @@ class Container implements ArrayAccess, ContainerContract
*/
public function get($id)
{
if ($this->has($id)) {
try {
return $this->resolve($id);
}
} catch (Exception $e) {
if ($this->has($id)) {
throw $e;
}
throw new EntryNotFoundException;
throw new EntryNotFoundException($id);
}
}
/**
@@ -618,9 +636,12 @@ class Container implements ArrayAccess, ContainerContract
*
* @param string $abstract
* @param array $parameters
* @param bool $raiseEvents
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function resolve($abstract, $parameters = [])
protected function resolve($abstract, $parameters = [], $raiseEvents = true)
{
$abstract = $this->getAlias($abstract);
@@ -662,7 +683,9 @@ class Container implements ArrayAccess, ContainerContract
$this->instances[$abstract] = $object;
}
$this->fireResolvingCallbacks($abstract, $object);
if ($raiseEvents) {
$this->fireResolvingCallbacks($abstract, $object);
}
// Before returning, we will also set the resolved flag to "true" and pop off
// the parameter overrides for this build. After those two things are done
@@ -700,7 +723,7 @@ class Container implements ArrayAccess, ContainerContract
* Get the contextual concrete binding for the given abstract.
*
* @param string $abstract
* @return string|null
* @return \Closure|string|null
*/
protected function getContextualConcrete($abstract)
{
@@ -726,13 +749,11 @@ class Container implements ArrayAccess, ContainerContract
* Find the concrete binding for the given abstract in the contextual binding array.
*
* @param string $abstract
* @return string|null
* @return \Closure|string|null
*/
protected function findInContextualBindings($abstract)
{
if (isset($this->contextual[end($this->buildStack)][$abstract])) {
return $this->contextual[end($this->buildStack)][$abstract];
}
return $this->contextual[end($this->buildStack)][$abstract] ?? null;
}
/**
@@ -767,7 +788,7 @@ class Container implements ArrayAccess, ContainerContract
$reflector = new ReflectionClass($concrete);
// If the type is not instantiable, the developer is attempting to resolve
// an abstract type such as an Interface of Abstract Class and there is
// an abstract type such as an Interface or Abstract Class and there is
// no binding registered for the abstractions so we need to bail out.
if (! $reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
@@ -791,9 +812,13 @@ class Container implements ArrayAccess, ContainerContract
// Once we have all the constructor's parameters we can create each of the
// dependency instances and then use the reflection instances to make a
// new instance of this class, injecting the created dependencies in.
$instances = $this->resolveDependencies(
$dependencies
);
try {
$instances = $this->resolveDependencies($dependencies);
} catch (BindingResolutionException $e) {
array_pop($this->buildStack);
throw $e;
}
array_pop($this->buildStack);
@@ -805,6 +830,8 @@ class Container implements ArrayAccess, ContainerContract
*
* @param array $dependencies
* @return array
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function resolveDependencies(array $dependencies)
{
@@ -1073,8 +1100,6 @@ class Container implements ArrayAccess, ContainerContract
*
* @param string $abstract
* @return string
*
* @throws \LogicException
*/
public function getAlias($abstract)
{
@@ -1082,10 +1107,6 @@ class Container implements ArrayAccess, ContainerContract
return $abstract;
}
if ($this->aliases[$abstract] === $abstract) {
throw new LogicException("[{$abstract}] is aliased to itself.");
}
return $this->getAlias($this->aliases[$abstract]);
}
@@ -1099,11 +1120,7 @@ class Container implements ArrayAccess, ContainerContract
{
$abstract = $this->getAlias($abstract);
if (isset($this->extenders[$abstract])) {
return $this->extenders[$abstract];
}
return [];
return $this->extenders[$abstract] ?? [];
}
/**
@@ -1164,7 +1181,7 @@ class Container implements ArrayAccess, ContainerContract
}
/**
* Set the globally available instance of the container.
* Get the globally available instance of the container.
*
* @return static
*/

View File

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

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,60 @@
<?php
namespace Illuminate\Container;
use Countable;
use IteratorAggregate;
class RewindableGenerator implements Countable, IteratorAggregate
{
/**
* The generator callback.
*
* @var callable
*/
protected $generator;
/**
* The number of tagged services.
*
* @var callable|int
*/
protected $count;
/**
* Create a new generator instance.
*
* @param callable $generator
* @param callable|int $count
* @return void
*/
public function __construct(callable $generator, $count)
{
$this->count = $count;
$this->generator = $generator;
}
/**
* Get an iterator from the generator.
*
* @return mixed
*/
public function getIterator()
{
return ($this->generator)();
}
/**
* Get the total number of tagged services.
*
* @return int
*/
public function count()
{
if (is_callable($count = $this->count)) {
$this->count = $count();
}
return $this->count;
}
}

View File

@@ -15,8 +15,9 @@
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.6.*",
"psr/container": "~1.0"
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*",
"psr/container": "^1.0"
},
"autoload": {
"psr-4": {
@@ -25,7 +26,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.6-dev"
"dev-master": "5.8-dev"
}
},
"config": {

View File

@@ -93,6 +93,15 @@ interface Gate
*/
public function authorize($ability, $arguments = []);
/**
* Get the raw result from the authorization callback.
*
* @param string $ability
* @param array|mixed $arguments
* @return mixed
*/
public function raw($ability, $arguments = []);
/**
* Get a policy instance for a given class.
*

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