laravel-6 support

This commit is contained in:
RafficMohammed
2023-01-08 01:17:22 +05:30
parent 1a5c16ae4b
commit 774eed8b0e
4962 changed files with 279380 additions and 297961 deletions

View File

@@ -6,5 +6,58 @@ use Exception;
class AuthorizationException extends Exception
{
//
/**
* The response from the gate.
*
* @var \Illuminate\Auth\Access\Response
*/
protected $response;
/**
* Create a new authorization exception instance.
*
* @param string|null $message
* @param mixed $code
* @param \Exception|null $previous
* @return void
*/
public function __construct($message = null, $code = null, Exception $previous = null)
{
parent::__construct($message ?? 'This action is unauthorized.', 0, $previous);
$this->code = $code ?: 0;
}
/**
* Get the response from the gate.
*
* @return \Illuminate\Auth\Access\Response
*/
public function response()
{
return $this->response;
}
/**
* Set the response from the gate.
*
* @param \Illuminate\Auth\Access\Response $response
* @return $this
*/
public function setResponse($response)
{
$this->response = $response;
return $this;
}
/**
* Create a deny response object from this exception.
*
* @return \Illuminate\Auth\Access\Response
*/
public function toResponse()
{
return Response::deny($this->message, $this->code);
}
}

View File

@@ -3,13 +3,13 @@
namespace Illuminate\Auth\Access;
use Exception;
use ReflectionClass;
use ReflectionFunction;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use ReflectionClass;
use ReflectionFunction;
class Gate implements GateContract
{
@@ -144,7 +144,7 @@ class Gate implements GateContract
*
* @param string $name
* @param string $class
* @param array|null $abilities
* @param array|null $abilities
* @return $this
*/
public function resource($name, $class, array $abilities = null)
@@ -274,11 +274,7 @@ class Gate implements GateContract
public function check($abilities, $arguments = [])
{
return collect($abilities)->every(function ($ability) use ($arguments) {
try {
return (bool) $this->raw($ability, $arguments);
} catch (AuthorizationException $e) {
return false;
}
return $this->inspect($ability, $arguments)->allowed();
});
}
@@ -319,13 +315,29 @@ class Gate implements GateContract
*/
public function authorize($ability, $arguments = [])
{
$result = $this->raw($ability, $arguments);
return $this->inspect($ability, $arguments)->authorize();
}
if ($result instanceof Response) {
return $result;
/**
* Inspect the user for the given ability.
*
* @param string $ability
* @param array|mixed $arguments
* @return \Illuminate\Auth\Access\Response
*/
public function inspect($ability, $arguments = [])
{
try {
$result = $this->raw($ability, $arguments);
if ($result instanceof Response) {
return $result;
}
return $result ? Response::allow() : Response::deny();
} catch (AuthorizationException $e) {
return $e->toResponse();
}
return $result ? $this->allow() : $this->deny();
}
/**
@@ -334,6 +346,8 @@ class Gate implements GateContract
* @param string $ability
* @param array|mixed $arguments
* @return mixed
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function raw($ability, $arguments = [])
{
@@ -365,7 +379,7 @@ class Gate implements GateContract
*
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param \Closure|string|array $class
* @param string|null $method
* @param string|null $method
* @return bool
*/
protected function canBeCalledWithUser($user, $class, $method = null)
@@ -436,7 +450,7 @@ class Gate implements GateContract
*/
protected function parameterAllowsGuests($parameter)
{
return ($parameter->getClass() && $parameter->allowsNull()) ||
return ($parameter->hasType() && $parameter->allowsNull()) ||
($parameter->isDefaultValueAvailable() && is_null($parameter->getDefaultValue()));
}
@@ -530,6 +544,7 @@ class Gate implements GateContract
}
return function () {
//
};
}

View File

@@ -8,23 +8,23 @@ trait HandlesAuthorization
* Create a new access response.
*
* @param string|null $message
* @param mixed $code
* @return \Illuminate\Auth\Access\Response
*/
protected function allow($message = null)
protected function allow($message = null, $code = null)
{
return new Response($message);
return Response::allow($message, $code);
}
/**
* Throws an unauthorized exception.
*
* @param string $message
* @return void
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @param string|null $message
* @param mixed|null $code
* @return \Illuminate\Auth\Access\Response
*/
protected function deny($message = 'This action is unauthorized.')
protected function deny($message = null, $code = null)
{
throw new AuthorizationException($message);
return Response::deny($message, $code);
}
}

View File

@@ -2,8 +2,17 @@
namespace Illuminate\Auth\Access;
class Response
use Illuminate\Contracts\Support\Arrayable;
class Response implements Arrayable
{
/**
* Indicates whether the response was allowed.
*
* @var bool
*/
protected $allowed;
/**
* The response message.
*
@@ -11,17 +20,72 @@ class Response
*/
protected $message;
/**
* The response code.
*
* @var mixed
*/
protected $code;
/**
* Create a new response.
*
* @param string|null $message
* @param bool $allowed
* @param string $message
* @param mixed $code
* @return void
*/
public function __construct($message = null)
public function __construct($allowed, $message = '', $code = null)
{
$this->code = $code;
$this->allowed = $allowed;
$this->message = $message;
}
/**
* Create a new "allow" Response.
*
* @param string|null $message
* @param mixed $code
* @return \Illuminate\Auth\Access\Response
*/
public static function allow($message = null, $code = null)
{
return new static(true, $message, $code);
}
/**
* Create a new "deny" Response.
*
* @param string|null $message
* @param mixed $code
* @return \Illuminate\Auth\Access\Response
*/
public static function deny($message = null, $code = null)
{
return new static(false, $message, $code);
}
/**
* Determine if the response was allowed.
*
* @return bool
*/
public function allowed()
{
return $this->allowed;
}
/**
* Determine if the response was denied.
*
* @return bool
*/
public function denied()
{
return ! $this->allowed();
}
/**
* Get the response message.
*
@@ -32,6 +96,47 @@ class Response
return $this->message;
}
/**
* Get the response code / reason.
*
* @return mixed
*/
public function code()
{
return $this->code;
}
/**
* Throw authorization exception if response was denied.
*
* @return \Illuminate\Auth\Access\Response
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function authorize()
{
if ($this->denied()) {
throw (new AuthorizationException($this->message(), $this->code()))
->setResponse($this);
}
return $this;
}
/**
* Convert the response to an array.
*
* @return array
*/
public function toArray()
{
return [
'allowed' => $this->allowed(),
'message' => $this->message(),
'code' => $this->code(),
];
}
/**
* Get the string representation of the message.
*

View File

@@ -3,8 +3,8 @@
namespace Illuminate\Auth;
use Closure;
use InvalidArgumentException;
use Illuminate\Contracts\Auth\Factory as FactoryContract;
use InvalidArgumentException;
class AuthManager implements FactoryContract
{
@@ -285,6 +285,16 @@ class AuthManager implements FactoryContract
return $this;
}
/**
* Determines if any guards have already been resolved.
*
* @return bool
*/
public function hasResolvedGuards()
{
return count($this->guards) > 0;
}
/**
* Dynamically call the default driver instance.
*

View File

@@ -3,9 +3,12 @@
namespace Illuminate\Auth;
use Illuminate\Auth\Access\Gate;
use Illuminate\Support\ServiceProvider;
use Illuminate\Auth\Middleware\RequirePassword;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Support\ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
@@ -19,6 +22,7 @@ class AuthServiceProvider extends ServiceProvider
$this->registerAuthenticator();
$this->registerUserResolver();
$this->registerAccessGate();
$this->registerRequirePassword();
$this->registerRequestRebindHandler();
$this->registerEventRebindHandler();
}
@@ -72,6 +76,24 @@ class AuthServiceProvider extends ServiceProvider
});
}
/**
* Register a resolver for the authenticated user.
*
* @return void
*/
protected function registerRequirePassword()
{
$this->app->bind(
RequirePassword::class, function ($app) {
return new RequirePassword(
$app[ResponseFactory::class],
$app[UrlGenerator::class],
$app['config']->get('auth.password_timeout')
);
}
);
}
/**
* Handle the re-binding of the request binding.
*
@@ -98,6 +120,10 @@ class AuthServiceProvider extends ServiceProvider
return;
}
if ($app['auth']->hasResolvedGuards() === false) {
return;
}
if (method_exists($guard = $app['auth']->guard(), 'setDispatcher')) {
$guard->setDispatcher($dispatcher);
}

View File

@@ -1,133 +0,0 @@
<?php
namespace Illuminate\Auth\Console;
use Illuminate\Console\Command;
use Illuminate\Console\DetectsApplicationNamespace;
class AuthMakeCommand extends Command
{
use DetectsApplicationNamespace;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'make:auth
{--views : Only scaffold the authentication views}
{--force : Overwrite existing views by default}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Scaffold basic login and registration views and routes';
/**
* The views that need to be exported.
*
* @var array
*/
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',
'home.stub' => 'home.blade.php',
];
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$this->createDirectories();
$this->exportViews();
if (! $this->option('views')) {
file_put_contents(
app_path('Http/Controllers/HomeController.php'),
$this->compileControllerStub()
);
file_put_contents(
base_path('routes/web.php'),
file_get_contents(__DIR__.'/stubs/make/routes.stub'),
FILE_APPEND
);
}
$this->info('Authentication scaffolding generated successfully.');
}
/**
* Create the directories for the files.
*
* @return void
*/
protected function createDirectories()
{
if (! is_dir($directory = $this->getViewPath('layouts'))) {
mkdir($directory, 0755, true);
}
if (! is_dir($directory = $this->getViewPath('auth/passwords'))) {
mkdir($directory, 0755, true);
}
}
/**
* Export the authentication views.
*
* @return void
*/
protected function exportViews()
{
foreach ($this->views as $key => $value) {
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;
}
}
copy(
__DIR__.'/stubs/make/views/'.$key,
$view
);
}
}
/**
* Compiles the HomeController stub.
*
* @return string
*/
protected function compileControllerStub()
{
return str_replace(
'{{namespace}}',
$this->getAppNamespace(),
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

@@ -1,28 +0,0 @@
<?php
namespace {{namespace}}Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
}

View File

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

View File

@@ -1,73 +0,0 @@
@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">{{ __('Login') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@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 @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Login') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -1,47 +0,0 @@
@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">{{ __('Reset Password') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<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 @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -1,65 +0,0 @@
@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">{{ __('Reset Password') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<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 @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Reset Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -1,77 +0,0 @@
@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">{{ __('Register') }}</div>
<div class="card-body">
<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 @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<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 @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -1,24 +0,0 @@
@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

@@ -1,23 +0,0 @@
@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">Dashboard</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
You are logged in!
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -66,7 +66,7 @@ trait CreatesUserProviders
*/
protected function createDatabaseProvider($config)
{
$connection = $this->app['db']->connection();
$connection = $this->app['db']->connection($config['connection'] ?? null);
return new DatabaseUserProvider($connection, $this->app['hash'], $config['table']);
}

View File

@@ -2,12 +2,12 @@
namespace Illuminate\Auth;
use Illuminate\Support\Str;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Support\Str;
class DatabaseUserProvider implements UserProvider
{

View File

@@ -2,11 +2,11 @@
namespace Illuminate\Auth;
use Illuminate\Support\Str;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Str;
class EloquentUserProvider implements UserProvider
{

View File

@@ -0,0 +1,37 @@
<?php
namespace Illuminate\Auth\Events;
use Illuminate\Queue\SerializesModels;
class CurrentDeviceLogout
{
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

@@ -32,7 +32,7 @@ class Login
/**
* Create a new event instance.
*
* @param string $guard
* @param string $guard
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param bool $remember
* @return void

View File

@@ -25,7 +25,7 @@ class Logout
/**
* Create a new event instance.
*
* @param string $guard
* @param string $guard
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/

View File

@@ -0,0 +1,37 @@
<?php
namespace Illuminate\Auth\Events;
use Illuminate\Queue\SerializesModels;
class Validated
{
use SerializesModels;
/**
* The authentication guard name.
*
* @var string
*/
public $guard;
/**
* The user retrieved and validated from the User Provider.
*
* @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

@@ -41,9 +41,7 @@ class GenericUser implements UserContract
*/
public function getAuthIdentifier()
{
$name = $this->getAuthIdentifierName();
return $this->attributes[$name];
return $this->attributes[$this->getAuthIdentifierName()];
}
/**

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Auth;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\UserProvider;
/**
* These methods are typically the same across all guards.

View File

@@ -64,6 +64,20 @@ class Authenticate
}
}
$this->unauthenticated($request, $guards);
}
/**
* Handle an unauthenticated user.
*
* @param \Illuminate\Http\Request $request
* @param array $guards
* @return void
*
* @throws \Illuminate\Auth\AuthenticationException
*/
protected function unauthenticated($request, array $guards)
{
throw new AuthenticationException(
'Unauthenticated.', $guards, $this->redirectTo($request)
);
@@ -73,7 +87,7 @@ class Authenticate
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string
* @return string|null
*/
protected function redirectTo($request)
{

View File

@@ -3,8 +3,8 @@
namespace Illuminate\Auth\Middleware;
use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Database\Eloquent\Model;
class Authorize
{
@@ -50,7 +50,7 @@ class Authorize
*
* @param \Illuminate\Http\Request $request
* @param array|null $models
* @return array|string|\Illuminate\Database\Eloquent\Model
* @return \Illuminate\Database\Eloquent\Model|array|string
*/
protected function getGateArguments($request, $models)
{

View File

@@ -3,8 +3,8 @@
namespace Illuminate\Auth\Middleware;
use Closure;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Redirect;
class EnsureEmailIsVerified
{

View File

@@ -0,0 +1,84 @@
<?php
namespace Illuminate\Auth\Middleware;
use Closure;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\Routing\UrlGenerator;
class RequirePassword
{
/**
* The response factory instance.
*
* @var \Illuminate\Contracts\Routing\ResponseFactory
*/
protected $responseFactory;
/**
* The URL generator instance.
*
* @var \Illuminate\Contracts\Routing\UrlGenerator
*/
protected $urlGenerator;
/**
* The password timeout.
*
* @var int
*/
protected $passwordTimeout;
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Routing\ResponseFactory $responseFactory
* @param \Illuminate\Contracts\Routing\UrlGenerator $urlGenerator
* @param int|null $passwordTimeout
* @return void
*/
public function __construct(ResponseFactory $responseFactory, UrlGenerator $urlGenerator, $passwordTimeout = null)
{
$this->responseFactory = $responseFactory;
$this->urlGenerator = $urlGenerator;
$this->passwordTimeout = $passwordTimeout ?: 10800;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $redirectToRoute
* @return mixed
*/
public function handle($request, Closure $next, $redirectToRoute = null)
{
if ($this->shouldConfirmPassword($request)) {
if ($request->expectsJson()) {
return $this->responseFactory->json([
'message' => 'Password confirmation required.',
], 423);
}
return $this->responseFactory->redirectGuest(
$this->urlGenerator->route($redirectToRoute ?? 'password.confirm')
);
}
return $next($request);
}
/**
* Determine if the confirmation timeout has expired.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function shouldConfirmPassword($request)
{
$confirmedAt = time() - $request->session()->get('auth.password_confirmed_at', 0);
return $confirmedAt > $this->passwordTimeout;
}
}

View File

@@ -2,6 +2,8 @@
namespace Illuminate\Auth;
use Illuminate\Auth\Notifications\VerifyEmail;
trait MustVerifyEmail
{
/**
@@ -33,6 +35,16 @@ trait MustVerifyEmail
*/
public function sendEmailVerificationNotification()
{
$this->notify(new Notifications\VerifyEmail);
$this->notify(new VerifyEmail);
}
/**
* Get the email address that should be used for verification.
*
* @return string
*/
public function getEmailForVerification()
{
return $this->email;
}
}

View File

@@ -2,9 +2,9 @@
namespace Illuminate\Auth\Notifications;
use Illuminate\Support\Facades\Lang;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Lang;
class ResetPassword extends Notification
{
@@ -57,11 +57,11 @@ 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', ['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.'));
->subject(Lang::get('Reset Password Notification'))
->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
->action(Lang::get('Reset Password'), url(route('password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
->line(Lang::get('If you did not request a password reset, no further action is required.'));
}
/**

View File

@@ -2,12 +2,12 @@
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;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;
class VerifyEmail extends Notification
{
@@ -44,10 +44,10 @@ class VerifyEmail extends Notification
}
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.'));
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $verificationUrl)
->line(Lang::get('If you did not create an account, no further action is required.'));
}
/**
@@ -61,7 +61,10 @@ class VerifyEmail extends Notification
return URL::temporarySignedRoute(
'verification.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
['id' => $notifiable->getKey()]
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}

View File

@@ -2,11 +2,11 @@
namespace Illuminate\Auth\Passwords;
use Illuminate\Support\Str;
use Illuminate\Support\Carbon;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
class DatabaseTokenRepository implements TokenRepositoryInterface
{
@@ -45,6 +45,13 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
*/
protected $expires;
/**
* Minimum number of seconds before re-redefining the token.
*
* @var int
*/
protected $throttle;
/**
* Create a new token repository instance.
*
@@ -53,16 +60,19 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
* @param string $table
* @param string $hashKey
* @param int $expires
* @param int $throttle
* @return void
*/
public function __construct(ConnectionInterface $connection, HasherContract $hasher,
$table, $hashKey, $expires = 60)
$table, $hashKey, $expires = 60,
$throttle = 60)
{
$this->table = $table;
$this->hasher = $hasher;
$this->hashKey = $hashKey;
$this->expires = $expires * 60;
$this->connection = $connection;
$this->throttle = $throttle;
}
/**
@@ -139,6 +149,38 @@ class DatabaseTokenRepository implements TokenRepositoryInterface
return Carbon::parse($createdAt)->addSeconds($this->expires)->isPast();
}
/**
* Determine if the given user recently created a password reset token.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return bool
*/
public function recentlyCreatedToken(CanResetPasswordContract $user)
{
$record = (array) $this->getTable()->where(
'email', $user->getEmailForPasswordReset()
)->first();
return $record && $this->tokenRecentlyCreated($record['created_at']);
}
/**
* Determine if the token was recently created.
*
* @param string $createdAt
* @return bool
*/
protected function tokenRecentlyCreated($createdAt)
{
if ($this->throttle <= 0) {
return false;
}
return Carbon::parse($createdAt)->addSeconds(
$this->throttle
)->isFuture();
}
/**
* Delete a token record by user.
*

View File

@@ -3,11 +3,11 @@
namespace Illuminate\Auth\Passwords;
use Closure;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Contracts\Auth\PasswordBroker as PasswordBrokerContract;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Arr;
use UnexpectedValueException;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Auth\PasswordBroker as PasswordBrokerContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class PasswordBroker implements PasswordBrokerContract
{
@@ -25,13 +25,6 @@ class PasswordBroker implements PasswordBrokerContract
*/
protected $users;
/**
* The custom password validator callback.
*
* @var \Closure
*/
protected $passwordValidator;
/**
* Create a new password broker instance.
*
@@ -39,8 +32,7 @@ class PasswordBroker implements PasswordBrokerContract
* @param \Illuminate\Contracts\Auth\UserProvider $users
* @return void
*/
public function __construct(TokenRepositoryInterface $tokens,
UserProvider $users)
public function __construct(TokenRepositoryInterface $tokens, UserProvider $users)
{
$this->users = $users;
$this->tokens = $tokens;
@@ -63,6 +55,11 @@ class PasswordBroker implements PasswordBrokerContract
return static::INVALID_USER;
}
if (method_exists($this->tokens, 'recentlyCreatedToken') &&
$this->tokens->recentlyCreatedToken($user)) {
return static::RESET_THROTTLED;
}
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
@@ -82,11 +79,11 @@ class PasswordBroker implements PasswordBrokerContract
*/
public function reset(array $credentials, Closure $callback)
{
$user = $this->validateReset($credentials);
// If the responses from the validate method is not a user instance, we will
// assume that it is a redirect and simply return it from this method and
// the user is properly redirected having an error message on the post.
$user = $this->validateReset($credentials);
if (! $user instanceof CanResetPasswordContract) {
return $user;
}
@@ -115,10 +112,6 @@ class PasswordBroker implements PasswordBrokerContract
return static::INVALID_USER;
}
if (! $this->validateNewPassword($credentials)) {
return static::INVALID_PASSWORD;
}
if (! $this->tokens->exists($user, $credentials['token'])) {
return static::INVALID_TOKEN;
}
@@ -126,55 +119,6 @@ class PasswordBroker implements PasswordBrokerContract
return $user;
}
/**
* Set a custom password validator.
*
* @param \Closure $callback
* @return void
*/
public function validator(Closure $callback)
{
$this->passwordValidator = $callback;
}
/**
* Determine if the passwords match for the request.
*
* @param array $credentials
* @return bool
*/
public function validateNewPassword(array $credentials)
{
if (isset($this->passwordValidator)) {
[$password, $confirm] = [
$credentials['password'],
$credentials['password_confirmation'],
];
return call_user_func(
$this->passwordValidator, $credentials
) && $password === $confirm;
}
return $this->validatePasswordWithDefaults($credentials);
}
/**
* Determine if the passwords are valid for the request.
*
* @param array $credentials
* @return bool
*/
protected function validatePasswordWithDefaults(array $credentials)
{
[$password, $confirm] = [
$credentials['password'],
$credentials['password_confirmation'],
];
return $password === $confirm && mb_strlen($password) >= 8;
}
/**
* Get the user for the given credentials.
*
@@ -199,7 +143,7 @@ class PasswordBroker implements PasswordBrokerContract
/**
* Create a new password reset token for the given user.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return string
*/
public function createToken(CanResetPasswordContract $user)
@@ -210,7 +154,7 @@ class PasswordBroker implements PasswordBrokerContract
/**
* Delete password reset tokens of the given user.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return void
*/
public function deleteToken(CanResetPasswordContract $user)
@@ -221,8 +165,8 @@ class PasswordBroker implements PasswordBrokerContract
/**
* Validate the given password reset token.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $token
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $token
* @return bool
*/
public function tokenExists(CanResetPasswordContract $user, $token)

View File

@@ -2,9 +2,9 @@
namespace Illuminate\Auth\Passwords;
use Illuminate\Contracts\Auth\PasswordBrokerFactory as FactoryContract;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Illuminate\Contracts\Auth\PasswordBrokerFactory as FactoryContract;
/**
* @mixin \Illuminate\Contracts\Auth\PasswordBroker
@@ -95,7 +95,8 @@ class PasswordBrokerManager implements FactoryContract
$this->app['hash'],
$config['table'],
$key,
$config['expire']
$config['expire'],
$config['throttle'] ?? 0
);
}
@@ -135,7 +136,7 @@ class PasswordBrokerManager 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

@@ -2,8 +2,8 @@
namespace Illuminate\Auth\Passwords;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
class PasswordResetServiceProvider extends ServiceProvider implements DeferrableProvider
{

View File

@@ -2,10 +2,10 @@
namespace Illuminate\Auth;
use Illuminate\Http\Request;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Traits\Macroable;
class RequestGuard implements Guard
{
@@ -30,7 +30,7 @@ class RequestGuard implements Guard
*
* @param callable $callback
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Contracts\Auth\UserProvider|null $provider
* @param \Illuminate\Contracts\Auth\UserProvider|null $provider
* @return void
*/
public function __construct(callable $callback, Request $request, UserProvider $provider = null)

View File

@@ -2,19 +2,27 @@
namespace Illuminate\Auth;
use RuntimeException;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Session\Session;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Auth\StatefulGuard;
use Symfony\Component\HttpFoundation\Request;
use Illuminate\Contracts\Auth\SupportsBasicAuth;
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Illuminate\Auth\Events\Attempting;
use Illuminate\Auth\Events\Authenticated;
use Illuminate\Auth\Events\CurrentDeviceLogout;
use Illuminate\Auth\Events\Failed;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Auth\Events\OtherDeviceLogout;
use Illuminate\Auth\Events\Validated;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Contracts\Auth\SupportsBasicAuth;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use RuntimeException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class SessionGuard implements StatefulGuard, SupportsBasicAuth
{
@@ -339,7 +347,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
* Attempt to authenticate a user using the given credentials.
*
* @param array $credentials
* @param bool $remember
* @param bool $remember
* @return bool
*/
public function attempt(array $credentials = [], $remember = false)
@@ -374,14 +382,20 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*/
protected function hasValidCredentials($user, $credentials)
{
return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
$validated = ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
if ($validated) {
$this->fireValidatedEvent($user);
}
return $validated;
}
/**
* Log the given user ID into the application.
*
* @param mixed $id
* @param bool $remember
* @param bool $remember
* @return \Illuminate\Contracts\Auth\Authenticatable|false
*/
public function loginUsingId($id, $remember = false)
@@ -482,17 +496,17 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
{
$user = $this->user();
// If we have an event dispatcher instance, we can fire off the logout event
// so any further processing can be done. This allows the developer to be
// listening for anytime a user signs out of this application manually.
$this->clearUserDataFromStorage();
if (! is_null($this->user) && ! empty($user->getRememberToken())) {
$this->cycleRememberToken($user);
}
// If we have an event dispatcher instance, we can fire off the logout event
// so any further processing can be done. This allows the developer to be
// listening for anytime a user signs out of this application manually.
if (isset($this->events)) {
$this->events->dispatch(new Events\Logout($this->name, $user));
$this->events->dispatch(new Logout($this->name, $user));
}
// Once we have fired the logout event we will clear the users out of memory
@@ -531,6 +545,32 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
$this->provider->updateRememberToken($user, $token);
}
/**
* Log the user out of the application on their current device only.
*
* @return void
*/
public function logoutCurrentDevice()
{
$user = $this->user();
$this->clearUserDataFromStorage();
// If we have an event dispatcher instance, we can fire off the logout event
// so any further processing can be done. This allows the developer to be
// listening for anytime a user signs out of this application manually.
if (isset($this->events)) {
$this->events->dispatch(new CurrentDeviceLogout($this->name, $user));
}
// Once we have fired the logout event we will clear the users out of memory
// so they are no longer available as the user is no longer considered as
// being signed into this application and should not be available here.
$this->user = null;
$this->loggedOut = true;
}
/**
* Invalidate other sessions for the current user.
*
@@ -583,12 +623,26 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireAttemptEvent(array $credentials, $remember = false)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Attempting(
$this->events->dispatch(new Attempting(
$this->name, $credentials, $remember
));
}
}
/**
* Fires the validated event if the dispatcher is set.
*
* @param $user
*/
protected function fireValidatedEvent($user)
{
if (isset($this->events)) {
$this->events->dispatch(new Validated(
$this->name, $user
));
}
}
/**
* Fire the login event if the dispatcher is set.
*
@@ -599,7 +653,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireLoginEvent($user, $remember = false)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Login(
$this->events->dispatch(new Login(
$this->name, $user, $remember
));
}
@@ -614,7 +668,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireAuthenticatedEvent($user)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Authenticated(
$this->events->dispatch(new Authenticated(
$this->name, $user
));
}
@@ -629,7 +683,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireOtherDeviceLogoutEvent($user)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\OtherDeviceLogout(
$this->events->dispatch(new OtherDeviceLogout(
$this->name, $user
));
}
@@ -645,7 +699,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
protected function fireFailedEvent($user, array $credentials)
{
if (isset($this->events)) {
$this->events->dispatch(new Events\Failed(
$this->events->dispatch(new Failed(
$this->name, $user, $credentials
));
}

View File

@@ -2,9 +2,9 @@
namespace Illuminate\Auth;
use Illuminate\Http\Request;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
class TokenGuard implements Guard
{

View File

@@ -14,11 +14,11 @@
}
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/http": "5.8.*",
"illuminate/queue": "5.8.*",
"illuminate/support": "5.8.*"
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/http": "^6.0",
"illuminate/queue": "^6.0",
"illuminate/support": "^6.0"
},
"autoload": {
"psr-4": {
@@ -27,13 +27,13 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.8-dev"
"dev-master": "6.x-dev"
}
},
"suggest": {
"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.*)."
"illuminate/console": "Required to use the auth:clear-resets command (^6.0).",
"illuminate/queue": "Required to fire login / logout events (^6.0).",
"illuminate/session": "Required to use the session based guard (^6.0)."
},
"config": {
"sort-packages": true

View File

@@ -2,13 +2,13 @@
namespace Illuminate\Broadcasting;
use ReflectionClass;
use ReflectionProperty;
use Illuminate\Support\Arr;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\Broadcaster;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Broadcasting\Broadcaster;
use Illuminate\Support\Arr;
use ReflectionClass;
use ReflectionProperty;
class BroadcastEvent implements ShouldQueue
{
@@ -21,6 +21,20 @@ class BroadcastEvent implements ShouldQueue
*/
public $event;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout;
/**
* Create a new job handler instance.
*
@@ -30,6 +44,8 @@ class BroadcastEvent implements ShouldQueue
public function __construct($event)
{
$this->event = $event;
$this->tries = property_exists($event, 'tries') ? $event->tries : null;
$this->timeout = property_exists($event, 'timeout') ? $event->timeout : null;
}
/**

View File

@@ -3,15 +3,16 @@
namespace Illuminate\Broadcasting;
use Closure;
use Pusher\Pusher;
use Psr\Log\LoggerInterface;
use InvalidArgumentException;
use Illuminate\Broadcasting\Broadcasters\LogBroadcaster;
use Illuminate\Broadcasting\Broadcasters\NullBroadcaster;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Broadcasting\Broadcasters\RedisBroadcaster;
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
use Illuminate\Broadcasting\Broadcasters\RedisBroadcaster;
use Illuminate\Contracts\Broadcasting\Factory as FactoryContract;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcherContract;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Pusher\Pusher;
/**
* @mixin \Illuminate\Contracts\Broadcasting\Broadcaster
@@ -93,7 +94,7 @@ class BroadcastManager implements FactoryContract
* Begin broadcasting an event.
*
* @param mixed|null $event
* @return \Illuminate\Broadcasting\PendingBroadcast|void
* @return \Illuminate\Broadcasting\PendingBroadcast
*/
public function event($event = null)
{
@@ -108,10 +109,8 @@ class BroadcastManager implements FactoryContract
*/
public function queue($event)
{
$connection = $event instanceof ShouldBroadcastNow ? 'sync' : null;
if (is_null($connection) && isset($event->connection)) {
$connection = $event->connection;
if ($event instanceof ShouldBroadcastNow) {
return $this->app->make(BusDispatcherContract::class)->dispatchNow(new BroadcastEvent(clone $event));
}
$queue = null;
@@ -124,7 +123,7 @@ class BroadcastManager implements FactoryContract
$queue = $event->queue;
}
$this->app->make('queue')->connection($connection)->pushOn(
$this->app->make('queue')->connection($event->connection ?? null)->pushOn(
$queue, new BroadcastEvent(clone $event)
);
}
@@ -229,7 +228,8 @@ class BroadcastManager implements FactoryContract
protected function createRedisDriver(array $config)
{
return new RedisBroadcaster(
$this->app->make('redis'), $config['connection'] ?? null
$this->app->make('redis'), $config['connection'] ?? null,
$this->app['config']->get('database.redis.options.prefix', '')
);
}
@@ -296,7 +296,7 @@ class BroadcastManager implements FactoryContract
/**
* Register a custom driver creator Closure.
*
* @param string $driver
* @param string $driver
* @param \Closure $callback
* @return $this
*/
@@ -311,7 +311,7 @@ class BroadcastManager 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

@@ -2,10 +2,10 @@
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;
use Illuminate\Contracts\Broadcasting\Factory as BroadcastingFactory;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider implements DeferrableProvider
{

View File

@@ -3,15 +3,16 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Exception;
use Illuminate\Container\Container;
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
use Illuminate\Contracts\Routing\BindingRegistrar;
use Illuminate\Contracts\Routing\UrlRoutable;
use Illuminate\Support\Arr;
use Illuminate\Support\Reflector;
use Illuminate\Support\Str;
use ReflectionClass;
use ReflectionFunction;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Container\Container;
use Illuminate\Contracts\Routing\UrlRoutable;
use Illuminate\Contracts\Routing\BindingRegistrar;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
abstract class Broadcaster implements BroadcasterContract
{
@@ -204,9 +205,9 @@ abstract class Broadcaster implements BroadcasterContract
continue;
}
$instance = $parameter->getClass()->newInstance();
$className = Reflector::getParameterClassName($parameter);
if (! $model = $instance->resolveRouteBinding($value)) {
if (is_null($model = (new $className)->resolveRouteBinding($value))) {
throw new AccessDeniedHttpException;
}
@@ -225,8 +226,8 @@ abstract class Broadcaster implements BroadcasterContract
*/
protected function isImplicitlyBindable($key, $parameter)
{
return $parameter->name === $key && $parameter->getClass() &&
$parameter->getClass()->isSubclassOf(UrlRoutable::class);
return $parameter->getName() === $key &&
Reflector::isParameterSubclassOf($parameter, UrlRoutable::class);
}
/**
@@ -261,7 +262,7 @@ abstract class Broadcaster implements BroadcasterContract
* Normalize the given callback into a callable.
*
* @param mixed $callback
* @return callable|\Closure
* @return \Closure|callable
*/
protected function normalizeChannelHandlerToCallable($callback)
{

View File

@@ -2,10 +2,10 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Pusher\Pusher;
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Broadcasting\BroadcastException;
use Pusher\Pusher;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class PusherBroadcaster extends Broadcaster
@@ -102,6 +102,8 @@ class PusherBroadcaster extends Broadcaster
* @param string $event
* @param array $payload
* @return void
*
* @throws \Illuminate\Broadcasting\BroadcastException
*/
public function broadcast(array $channels, $event, array $payload = [])
{
@@ -117,7 +119,9 @@ class PusherBroadcaster extends Broadcaster
}
throw new BroadcastException(
is_bool($response) ? 'Failed to connect to Pusher.' : $response['body']
! empty($response['body'])
? sprintf('Pusher error: %s.', $response['body'])
: 'Failed to connect to Pusher.'
);
}

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Broadcasting\Broadcasters;
use Illuminate\Support\Arr;
use Illuminate\Contracts\Redis\Factory as Redis;
use Illuminate\Support\Arr;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class RedisBroadcaster extends Broadcaster
@@ -24,16 +24,25 @@ class RedisBroadcaster extends Broadcaster
*/
protected $connection;
/**
* The Redis key prefix.
*
* @var string
*/
protected $prefix;
/**
* Create a new broadcaster instance.
*
* @param \Illuminate\Contracts\Redis\Factory $redis
* @param string|null $connection
* @param string $prefix
* @return void
*/
public function __construct(Redis $redis, $connection = null)
public function __construct(Redis $redis, $connection = null, $prefix = '')
{
$this->redis = $redis;
$this->prefix = $prefix;
$this->connection = $connection;
}
@@ -47,7 +56,9 @@ class RedisBroadcaster extends Broadcaster
*/
public function auth($request)
{
$channelName = $this->normalizeChannelName($request->channel_name);
$channelName = $this->normalizeChannelName(
str_replace($this->prefix, '', $request->channel_name)
);
if ($this->isGuardedChannel($request->channel_name) &&
! $this->retrieveUser($request, $channelName)) {
@@ -90,6 +101,10 @@ class RedisBroadcaster extends Broadcaster
*/
public function broadcast(array $channels, $event, array $payload = [])
{
if (empty($channels)) {
return;
}
$connection = $this->redis->connection($this->connection);
$payload = json_encode([
@@ -98,8 +113,39 @@ class RedisBroadcaster extends Broadcaster
'socket' => Arr::pull($payload, 'socket'),
]);
foreach ($this->formatChannels($channels) as $channel) {
$connection->publish($channel, $payload);
}
$connection->eval(
$this->broadcastMultipleChannelsScript(),
0, $payload, ...$this->formatChannels($channels)
);
}
/**
* Get the Lua script for broadcasting to multiple channels.
*
* ARGV[1] - The payload
* ARGV[2...] - The channels
*
* @return string
*/
protected function broadcastMultipleChannelsScript()
{
return <<<'LUA'
for i = 2, #ARGV do
redis.call('publish', ARGV[i], ARGV[1])
end
LUA;
}
/**
* Format the channel array into an array of strings.
*
* @param array $channels
* @return array
*/
protected function formatChannels(array $channels)
{
return array_map(function ($channel) {
return $this->prefix.$channel;
}, parent::formatChannels($channels));
}
}

View File

@@ -25,10 +25,10 @@ trait UsePusherChannelConventions
*/
public function normalizeChannelName($channel)
{
if ($this->isGuardedChannel($channel)) {
return Str::startsWith($channel, 'private-')
? Str::replaceFirst('private-', '', $channel)
: Str::replaceFirst('presence-', '', $channel);
foreach (['private-encrypted-', 'private-', 'presence-'] as $prefix) {
if (Str::startsWith($channel, $prefix)) {
return Str::replaceFirst($prefix, '', $channel);
}
}
return $channel;

View File

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

View File

@@ -14,13 +14,13 @@
}
],
"require": {
"php": "^7.1.3",
"php": "^7.2.5|^8.0",
"ext-json": "*",
"psr/log": "^1.0",
"illuminate/bus": "5.8.*",
"illuminate/contracts": "5.8.*",
"illuminate/queue": "5.8.*",
"illuminate/support": "5.8.*"
"illuminate/bus": "^6.0",
"illuminate/contracts": "^6.0",
"illuminate/queue": "^6.0",
"illuminate/support": "^6.0"
},
"autoload": {
"psr-4": {
@@ -29,11 +29,11 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.8-dev"
"dev-master": "6.x-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 (^4.0)."
},
"config": {
"sort-packages": true

View File

@@ -2,11 +2,11 @@
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;
use Illuminate\Contracts\Queue\Factory as QueueFactoryContract;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
class BusServiceProvider extends ServiceProvider implements DeferrableProvider
{

View File

@@ -3,12 +3,12 @@
namespace Illuminate\Bus;
use Closure;
use RuntimeException;
use Illuminate\Pipeline\Pipeline;
use Illuminate\Contracts\Bus\QueueingDispatcher;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Queue\Queue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Bus\QueueingDispatcher;
use Illuminate\Pipeline\Pipeline;
use RuntimeException;
class Dispatcher implements QueueingDispatcher
{
@@ -140,8 +140,6 @@ class Dispatcher implements QueueingDispatcher
*
* @param mixed $command
* @return mixed
*
* @throws \RuntimeException
*/
public function dispatchToQueue($command)
{
@@ -184,6 +182,20 @@ class Dispatcher implements QueueingDispatcher
return $queue->push($command);
}
/**
* Dispatch a command to its appropriate handler after the current process.
*
* @param mixed $command
* @param mixed $handler
* @return void
*/
public function dispatchAfterResponse($command, $handler = null)
{
$this->container->terminating(function () use ($command, $handler) {
$this->dispatchNow($command, $handler);
});
}
/**
* Set the pipes through which commands should be piped before dispatching.
*

View File

@@ -2,6 +2,8 @@
namespace Illuminate\Bus;
use Illuminate\Support\Arr;
trait Queueable
{
/**
@@ -39,6 +41,11 @@ trait Queueable
*/
public $delay;
/**
* The middleware the job should be dispatched through.
*/
public $middleware = [];
/**
* The jobs that should run if this job is successful.
*
@@ -113,6 +120,29 @@ trait Queueable
return $this;
}
/**
* Get the middleware the job should be dispatched through.
*
* @return array
*/
public function middleware()
{
return [];
}
/**
* Specify the middleware the job should be dispatched through.
*
* @param array|object $middleware
* @return $this
*/
public function through($middleware)
{
$this->middleware = Arr::wrap($middleware);
return $this;
}
/**
* Set the jobs that should run if this job is successful.
*

View File

@@ -14,10 +14,10 @@
}
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/pipeline": "5.8.*",
"illuminate/support": "5.8.*"
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/pipeline": "^6.0",
"illuminate/support": "^6.0"
},
"autoload": {
"psr-4": {
@@ -26,7 +26,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.8-dev"
"dev-master": "6.x-dev"
}
},
"config": {

View File

@@ -52,7 +52,7 @@ class ApcStore extends TaggableStore
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -65,7 +65,7 @@ class ApcStore extends TaggableStore
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function increment($key, $value = 1)
@@ -77,7 +77,7 @@ class ApcStore extends TaggableStore
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function decrement($key, $value = 1)
@@ -89,7 +89,7 @@ class ApcStore extends TaggableStore
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return bool
*/
public function forever($key, $value)

View File

@@ -36,8 +36,8 @@ class ApcWrapper
* Store an item in the cache.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @param mixed $value
* @param int $seconds
* @return array|bool
*/
public function put($key, $value, $seconds)
@@ -49,7 +49,7 @@ class ApcWrapper
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function increment($key, $value)
@@ -61,7 +61,7 @@ class ApcWrapper
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function decrement($key, $value)

View File

@@ -0,0 +1,102 @@
<?php
namespace Illuminate\Cache;
use Carbon\Carbon;
class ArrayLock extends Lock
{
/**
* The parent array cache store.
*
* @var \Illuminate\Cache\ArrayStore
*/
protected $store;
/**
* Create a new lock instance.
*
* @param \Illuminate\Cache\ArrayStore $store
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return void
*/
public function __construct($store, $name, $seconds, $owner = null)
{
parent::__construct($name, $seconds, $owner);
$this->store = $store;
}
/**
* Attempt to acquire the lock.
*
* @return bool
*/
public function acquire()
{
$expiration = $this->store->locks[$this->name]['expiresAt'] ?? Carbon::now()->addSecond();
if ($this->exists() && $expiration->isFuture()) {
return false;
}
$this->store->locks[$this->name] = [
'owner' => $this->owner,
'expiresAt' => $this->seconds === 0 ? null : Carbon::now()->addSeconds($this->seconds),
];
return true;
}
/**
* Determine if the current lock exists.
*
* @return bool
*/
protected function exists()
{
return isset($this->store->locks[$this->name]);
}
/**
* Release the lock.
*
* @return bool
*/
public function release()
{
if (! $this->exists()) {
return false;
}
if (! $this->isOwnedByCurrentProcess()) {
return false;
}
$this->forceRelease();
return true;
}
/**
* Returns the owner value written into the driver for this lock.
*
* @return string
*/
protected function getCurrentOwner()
{
return $this->store->locks[$this->name]['owner'];
}
/**
* Releases this lock in disregard of ownership.
*
* @return void
*/
public function forceRelease()
{
unset($this->store->locks[$this->name]);
}
}

View File

@@ -2,9 +2,10 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Support\InteractsWithTime;
class ArrayStore extends TaggableStore
class ArrayStore extends TaggableStore implements LockProvider
{
use InteractsWithTime, RetrievesMultipleKeys;
@@ -15,6 +16,13 @@ class ArrayStore extends TaggableStore
*/
protected $storage = [];
/**
* The array of locks.
*
* @var array
*/
public $locks = [];
/**
* Retrieve an item from the cache by key.
*
@@ -44,7 +52,7 @@ class ArrayStore extends TaggableStore
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -62,7 +70,7 @@ class ArrayStore extends TaggableStore
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int
*/
public function increment($key, $value = 1)
@@ -82,7 +90,7 @@ class ArrayStore extends TaggableStore
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int
*/
public function decrement($key, $value = 1)
@@ -94,7 +102,7 @@ class ArrayStore extends TaggableStore
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
@@ -162,4 +170,29 @@ class ArrayStore extends TaggableStore
{
return $seconds > 0 ? $this->availableAt($seconds) : 0;
}
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)
{
return new ArrayLock($this, $name, $seconds, $owner);
}
/**
* Restore a lock instance using the owner identifier.
*
* @param string $name
* @param string $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function restoreLock($name, $owner)
{
return $this->lock($name, 0, $owner);
}
}

View File

@@ -2,13 +2,13 @@
namespace Illuminate\Cache;
use Aws\DynamoDb\DynamoDbClient;
use Closure;
use Illuminate\Contracts\Cache\Factory as FactoryContract;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
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;
/**
* @mixin \Illuminate\Contracts\Cache\Repository
@@ -153,7 +153,7 @@ class CacheManager implements FactoryContract
*/
protected function createFileDriver(array $config)
{
return $this->repository(new FileStore($this->app['files'], $config['path']));
return $this->repository(new FileStore($this->app['files'], $config['path'], $config['permission'] ?? null));
}
/**
@@ -226,21 +226,11 @@ class CacheManager implements FactoryContract
*/
protected function createDynamodbDriver(array $config)
{
$dynamoConfig = [
'region' => $config['region'],
'version' => 'latest',
'endpoint' => $config['endpoint'] ?? null,
];
if ($config['key'] && $config['secret']) {
$dynamoConfig['credentials'] = Arr::only(
$config, ['key', 'secret', 'token']
);
}
$client = $this->newDynamodbClient($config);
return $this->repository(
new DynamoDbStore(
new DynamoDbClient($dynamoConfig),
$client,
$config['table'],
$config['attributes']['key'] ?? 'key',
$config['attributes']['value'] ?? 'value',
@@ -250,6 +240,28 @@ class CacheManager implements FactoryContract
);
}
/**
* Create new DynamoDb Client instance.
*
* @return DynamoDbClient
*/
protected function newDynamodbClient(array $config)
{
$dynamoConfig = [
'region' => $config['region'],
'version' => 'latest',
'endpoint' => $config['endpoint'] ?? null,
];
if (isset($config['key']) && isset($config['secret'])) {
$dynamoConfig['credentials'] = Arr::only(
$config, ['key', 'secret', 'token']
);
}
return new DynamoDbClient($dynamoConfig);
}
/**
* Create a new cache repository with the given implementation.
*
@@ -258,15 +270,36 @@ class CacheManager implements FactoryContract
*/
public function repository(Store $store)
{
$repository = new Repository($store);
return tap(new Repository($store), function ($repository) {
$this->setEventDispatcher($repository);
});
}
if ($this->app->bound(DispatcherContract::class)) {
$repository->setEventDispatcher(
$this->app[DispatcherContract::class]
);
/**
* Set the event dispatcher on the given repository instance.
*
* @param \Illuminate\Cache\Repository $repository
* @return void
*/
protected function setEventDispatcher(Repository $repository)
{
if (! $this->app->bound(DispatcherContract::class)) {
return;
}
return $repository;
$repository->setEventDispatcher(
$this->app[DispatcherContract::class]
);
}
/**
* Re-set the event dispatcher on all resolved cache repositories.
*
* @return void
*/
public function refreshEventDispatcher()
{
array_map([$this, 'setEventDispatcher'], $this->stores);
}
/**

View File

@@ -2,8 +2,9 @@
namespace Illuminate\Cache;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Cache\Adapter\Psr16Adapter;
class CacheServiceProvider extends ServiceProvider implements DeferrableProvider
{
@@ -22,6 +23,10 @@ class CacheServiceProvider extends ServiceProvider implements DeferrableProvider
return $app['cache']->driver();
});
$this->app->singleton('cache.psr6', function ($app) {
return new Psr16Adapter($app['cache.store']);
});
$this->app->singleton('memcached.connector', function () {
return new MemcachedConnector;
});
@@ -35,7 +40,7 @@ class CacheServiceProvider extends ServiceProvider implements DeferrableProvider
public function provides()
{
return [
'cache', 'cache.store', 'memcached.connector',
'cache', 'cache.store', 'cache.psr6', 'memcached.connector',
];
}
}

View File

@@ -3,8 +3,8 @@
namespace Illuminate\Cache\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Composer;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Composer;
class CacheTableCommand extends Command
{

View File

@@ -2,11 +2,11 @@
namespace Illuminate\Cache\Console;
use Illuminate\Console\Command;
use Illuminate\Cache\CacheManager;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class ClearCommand extends Command
{

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Cache\Console;
use Illuminate\Console\Command;
use Illuminate\Cache\CacheManager;
use Illuminate\Console\Command;
class ForgetCommand extends Command
{

View File

@@ -1,8 +1,8 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCacheTable extends Migration
{

View File

@@ -4,11 +4,11 @@ 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;
use Illuminate\Database\PostgresConnection;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Support\Str;
class DatabaseStore implements Store
{
@@ -87,7 +87,7 @@ class DatabaseStore implements Store
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -112,7 +112,7 @@ class DatabaseStore implements Store
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function increment($key, $value = 1)
@@ -126,7 +126,7 @@ class DatabaseStore implements Store
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function decrement($key, $value = 1)
@@ -197,7 +197,7 @@ class DatabaseStore implements Store
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return bool
*/
public function forever($key, $value)

View File

@@ -42,13 +42,15 @@ class DynamoDbLock extends Lock
/**
* Release the lock.
*
* @return void
* @return bool
*/
public function release()
{
if ($this->isOwnedByCurrentProcess()) {
$this->dynamo->forget($this->name);
return $this->dynamo->forget($this->name);
}
return false;
}
/**

View File

@@ -2,16 +2,16 @@
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;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Support\Carbon;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Support\Str;
use RuntimeException;
class DynamoDbStore implements Store, LockProvider
class DynamoDbStore implements LockProvider, Store
{
use InteractsWithTime;
@@ -151,6 +151,7 @@ class DynamoDbStore implements Store, LockProvider
$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;
@@ -390,7 +391,7 @@ class DynamoDbStore implements Store, LockProvider
*/
public function forever($key, $value)
{
return $this->put($key, $value, now()->addYears(5)->getTimestamp());
return $this->put($key, $value, Carbon::now()->addYears(5)->getTimestamp());
}
/**
@@ -442,6 +443,8 @@ class DynamoDbStore implements Store, LockProvider
* Remove all items from the cache.
*
* @return bool
*
* @throws \RuntimeException
*/
public function flush()
{
@@ -522,4 +525,14 @@ class DynamoDbStore implements Store, LockProvider
{
$this->prefix = ! empty($prefix) ? $prefix.':' : '';
}
/**
* Get the DynamoDb Client instance.
*
* @return DynamoDbClient
*/
public function getClient()
{
return $this->dynamo;
}
}

View File

@@ -25,17 +25,26 @@ class FileStore implements Store
*/
protected $directory;
/**
* Octal representation of the cache file permissions.
*
* @var int|null
*/
protected $filePermission;
/**
* Create a new file cache store instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @param string $directory
* @param int|null $filePermission
* @return void
*/
public function __construct(Filesystem $files, $directory)
public function __construct(Filesystem $files, $directory, $filePermission = null)
{
$this->files = $files;
$this->directory = $directory;
$this->filePermission = $filePermission;
}
/**
@@ -53,7 +62,7 @@ class FileStore implements Store
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -65,7 +74,13 @@ class FileStore implements Store
$path, $this->expiration($seconds).serialize($value), true
);
return $result !== false && $result > 0;
if ($result !== false && $result > 0) {
$this->ensureFileHasCorrectPermissions($path);
return true;
}
return false;
}
/**
@@ -81,11 +96,27 @@ class FileStore implements Store
}
}
/**
* Ensure the cache file has the correct permissions.
*
* @param string $path
* @return void
*/
protected function ensureFileHasCorrectPermissions($path)
{
if (is_null($this->filePermission) ||
intval($this->files->chmod($path), 8) == $this->filePermission) {
return;
}
$this->files->chmod($path, $this->filePermission);
}
/**
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int
*/
public function increment($key, $value = 1)
@@ -101,7 +132,7 @@ class FileStore implements Store
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int
*/
public function decrement($key, $value = 1)
@@ -113,7 +144,7 @@ class FileStore implements Store
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
@@ -148,7 +179,9 @@ class FileStore implements Store
}
foreach ($this->files->directories($this->directory) as $directory) {
if (! $this->files->deleteDirectory($directory)) {
$deleted = $this->files->deleteDirectory($directory);
if (! $deleted || $this->files->exists($directory)) {
return false;
}
}

View File

@@ -2,10 +2,10 @@
namespace Illuminate\Cache;
use Illuminate\Support\Str;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\Lock as LockContract;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Support\Str;
abstract class Lock implements LockContract
{
@@ -61,7 +61,7 @@ abstract class Lock implements LockContract
/**
* Release the lock.
*
* @return void
* @return bool
*/
abstract public function release();
@@ -98,7 +98,7 @@ abstract class Lock implements LockContract
*
* @param int $seconds
* @param callable|null $callback
* @return bool
* @return mixed
*
* @throws \Illuminate\Contracts\Cache\LockTimeoutException
*/

View File

@@ -42,13 +42,15 @@ class MemcachedLock extends Lock
/**
* Release the lock.
*
* @return void
* @return bool
*/
public function release()
{
if ($this->isOwnedByCurrentProcess()) {
$this->memcached->delete($this->name);
return $this->memcached->delete($this->name);
}
return false;
}
/**

View File

@@ -2,10 +2,10 @@
namespace Illuminate\Cache;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Support\InteractsWithTime;
use Memcached;
use ReflectionMethod;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\LockProvider;
class MemcachedStore extends TaggableStore implements LockProvider
{
@@ -36,7 +36,7 @@ class MemcachedStore extends TaggableStore implements LockProvider
* Create a new Memcached store.
*
* @param \Memcached $memcached
* @param string $prefix
* @param string $prefix
* @return void
*/
public function __construct($memcached, $prefix = '')
@@ -96,7 +96,7 @@ class MemcachedStore extends TaggableStore implements LockProvider
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -131,7 +131,7 @@ class MemcachedStore extends TaggableStore implements LockProvider
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -146,7 +146,7 @@ class MemcachedStore extends TaggableStore implements LockProvider
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function increment($key, $value = 1)
@@ -158,7 +158,7 @@ class MemcachedStore extends TaggableStore implements LockProvider
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function decrement($key, $value = 1)
@@ -170,7 +170,7 @@ class MemcachedStore extends TaggableStore implements LockProvider
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
@@ -181,9 +181,9 @@ class MemcachedStore extends TaggableStore implements LockProvider
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)

View File

@@ -6,13 +6,6 @@ class NullStore extends TaggableStore
{
use RetrievesMultipleKeys;
/**
* The array of stored values.
*
* @var array
*/
protected $storage = [];
/**
* Retrieve an item from the cache by key.
*
@@ -21,13 +14,14 @@ class NullStore extends TaggableStore
*/
public function get($key)
{
//
}
/**
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -40,7 +34,7 @@ class NullStore extends TaggableStore
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function increment($key, $value = 1)
@@ -52,7 +46,7 @@ class NullStore extends TaggableStore
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int|bool
*/
public function decrement($key, $value = 1)
@@ -64,7 +58,7 @@ class NullStore extends TaggableStore
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return bool
*/
public function forever($key, $value)

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Cache;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Support\InteractsWithTime;
class RateLimiter
{
@@ -36,6 +36,8 @@ class RateLimiter
*/
public function tooManyAttempts($key, $maxAttempts)
{
$key = $this->cleanRateLimiterKey($key);
if ($this->attempts($key) >= $maxAttempts) {
if ($this->cache->has($key.':timer')) {
return true;
@@ -56,6 +58,8 @@ class RateLimiter
*/
public function hit($key, $decaySeconds = 60)
{
$key = $this->cleanRateLimiterKey($key);
$this->cache->add(
$key.':timer', $this->availableAt($decaySeconds), $decaySeconds
);
@@ -79,6 +83,8 @@ class RateLimiter
*/
public function attempts($key)
{
$key = $this->cleanRateLimiterKey($key);
return $this->cache->get($key, 0);
}
@@ -90,6 +96,8 @@ class RateLimiter
*/
public function resetAttempts($key)
{
$key = $this->cleanRateLimiterKey($key);
return $this->cache->forget($key);
}
@@ -102,6 +110,8 @@ class RateLimiter
*/
public function retriesLeft($key, $maxAttempts)
{
$key = $this->cleanRateLimiterKey($key);
$attempts = $this->attempts($key);
return $maxAttempts - $attempts;
@@ -115,6 +125,8 @@ class RateLimiter
*/
public function clear($key)
{
$key = $this->cleanRateLimiterKey($key);
$this->resetAttempts($key);
$this->cache->forget($key.':timer');
@@ -128,6 +140,19 @@ class RateLimiter
*/
public function availableIn($key)
{
$key = $this->cleanRateLimiterKey($key);
return $this->cache->get($key.':timer') - $this->currentTime();
}
/**
* Clean the rate limiter key from unicode characters.
*
* @param string $key
* @return string
*/
public function cleanRateLimiterKey($key)
{
return preg_replace('/&([a-z])[a-z]+;/i', '$1', htmlentities($key));
}
}

View File

@@ -34,23 +34,21 @@ class RedisLock extends Lock
*/
public function acquire()
{
$result = $this->redis->setnx($this->name, $this->owner);
if ($result === 1 && $this->seconds > 0) {
$this->redis->expire($this->name, $this->seconds);
if ($this->seconds > 0) {
return $this->redis->set($this->name, $this->owner, 'EX', $this->seconds, 'NX') == true;
} else {
return $this->redis->setnx($this->name, $this->owner) === 1;
}
return $result === 1;
}
/**
* Release the lock.
*
* @return void
* @return bool
*/
public function release()
{
$this->redis->eval(LuaScripts::releaseLock(), 1, $this->name, $this->owner);
return (bool) $this->redis->eval(LuaScripts::releaseLock(), 1, $this->name, $this->owner);
}
/**

View File

@@ -83,7 +83,7 @@ class RedisStore extends TaggableStore implements LockProvider
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -122,7 +122,7 @@ class RedisStore extends TaggableStore implements LockProvider
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param int $seconds
* @return bool
*/
@@ -139,7 +139,7 @@ class RedisStore extends TaggableStore implements LockProvider
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int
*/
public function increment($key, $value = 1)
@@ -151,7 +151,7 @@ class RedisStore extends TaggableStore implements LockProvider
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return int
*/
public function decrement($key, $value = 1)
@@ -163,7 +163,7 @@ class RedisStore extends TaggableStore implements LockProvider
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
@@ -174,9 +174,9 @@ class RedisStore extends TaggableStore implements LockProvider
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)
@@ -235,7 +235,7 @@ class RedisStore extends TaggableStore implements LockProvider
/**
* Get the Redis connection instance.
*
* @return \Predis\ClientInterface
* @return \Illuminate\Redis\Connections\Connection
*/
public function connection()
{
@@ -292,7 +292,7 @@ class RedisStore extends TaggableStore implements LockProvider
*/
protected function serialize($value)
{
return is_numeric($value) ? $value : serialize($value);
return is_numeric($value) && ! in_array($value, [INF, -INF]) && ! is_nan($value) ? $value : serialize($value);
}
/**

View File

@@ -21,7 +21,7 @@ class RedisTaggedCache extends TaggedCache
* Store an item in the cache.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @param \DateTimeInterface|\DateInterval|int|null $ttl
* @return bool
*/
@@ -179,7 +179,7 @@ class RedisTaggedCache extends TaggedCache
if (count($values) > 0) {
foreach (array_chunk($values, 1000) as $valuesChunk) {
call_user_func_array([$this->store->connection(), 'del'], $valuesChunk);
$this->store->connection()->del(...$valuesChunk);
}
}
}

View File

@@ -2,25 +2,25 @@
namespace Illuminate\Cache;
use Closure;
use ArrayAccess;
use DateTimeInterface;
use BadMethodCallException;
use Illuminate\Support\Carbon;
use Closure;
use DateTimeInterface;
use Illuminate\Cache\Events\CacheHit;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Cache\Events\KeyWritten;
use Illuminate\Cache\Events\CacheMissed;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Cache\Events\KeyForgotten;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Cache\Events\KeyWritten;
use Illuminate\Contracts\Cache\Repository as CacheContract;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Carbon;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Support\Traits\Macroable;
/**
* @mixin \Illuminate\Contracts\Cache\Store
*/
class Repository implements CacheContract, ArrayAccess
class Repository implements ArrayAccess, CacheContract
{
use InteractsWithTime;
use Macroable {
@@ -504,7 +504,7 @@ class Repository implements CacheContract, ArrayAccess
/**
* Get the default cache time.
*
* @return int
* @return int|null
*/
public function getDefaultCacheTime()
{
@@ -547,6 +547,16 @@ class Repository implements CacheContract, ArrayAccess
}
}
/**
* Get the event dispatcher instance.
*
* @return \Illuminate\Contracts\Events\Dispatcher
*/
public function getEventDispatcher()
{
return $this->events;
}
/**
* Set the event dispatcher instance.
*
@@ -584,7 +594,7 @@ class Repository implements CacheContract, ArrayAccess
* Store an item in the cache for the default time.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return void
*/
public function offsetSet($key, $value)

View File

@@ -14,9 +14,9 @@
}
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*"
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/support": "^6.0"
},
"autoload": {
"psr-4": {
@@ -25,13 +25,15 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.8-dev"
"dev-master": "6.x-dev"
}
},
"suggest": {
"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.*)."
"ext-memcached": "Required to use the memcache cache driver.",
"illuminate/database": "Required to use the database cache driver (^6.0).",
"illuminate/filesystem": "Required to use the file cache driver (^6.0).",
"illuminate/redis": "Required to use the redis cache driver (^6.0).",
"symfony/cache": "Required to PSR-6 cache bridge (^4.3.4)."
},
"config": {
"sort-packages": true

View File

@@ -3,8 +3,8 @@
namespace Illuminate\Config;
use ArrayAccess;
use Illuminate\Support\Arr;
use Illuminate\Contracts\Config\Repository as ConfigContract;
use Illuminate\Support\Arr;
class Repository implements ArrayAccess, ConfigContract
{
@@ -41,7 +41,7 @@ class Repository implements ArrayAccess, ConfigContract
* Get the specified configuration value.
*
* @param array|string $key
* @param mixed $default
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null)
@@ -78,7 +78,7 @@ class Repository implements ArrayAccess, ConfigContract
* Set a given configuration value.
*
* @param array|string $key
* @param mixed $value
* @param mixed $value
* @return void
*/
public function set($key, $value = null)

View File

@@ -14,9 +14,9 @@
}
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*"
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/support": "^6.0"
},
"autoload": {
"psr-4": {
@@ -25,7 +25,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.8-dev"
"dev-master": "6.x-dev"
}
},
"config": {

View File

@@ -3,22 +3,25 @@
namespace Illuminate\Console;
use Closure;
use Illuminate\Support\ProcessUtils;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Console\Events\ArtisanStarting;
use Illuminate\Console\Events\CommandFinished;
use Illuminate\Console\Events\CommandStarting;
use Illuminate\Contracts\Console\Application as ApplicationContract;
use Illuminate\Contracts\Container\Container;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\ProcessUtils;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Illuminate\Contracts\Console\Application as ApplicationContract;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\PhpExecutableFinder;
class Application extends SymfonyApplication implements ApplicationContract
{
@@ -67,7 +70,7 @@ class Application extends SymfonyApplication implements ApplicationContract
$this->setAutoExit(false);
$this->setCatchExceptions(false);
$this->events->dispatch(new Events\ArtisanStarting($this));
$this->events->dispatch(new ArtisanStarting($this));
$this->bootstrap();
}
@@ -82,7 +85,7 @@ class Application extends SymfonyApplication implements ApplicationContract
);
$this->events->dispatch(
new Events\CommandStarting(
new CommandStarting(
$commandName, $input, $output = $output ?: new ConsoleOutput
)
);
@@ -90,7 +93,7 @@ class Application extends SymfonyApplication implements ApplicationContract
$exitCode = parent::run($input, $output);
$this->events->dispatch(
new Events\CommandFinished($commandName, $input, $output, $exitCode)
new CommandFinished($commandName, $input, $output, $exitCode)
);
return $exitCode;
@@ -113,7 +116,7 @@ class Application extends SymfonyApplication implements ApplicationContract
*/
public static function artisanBinary()
{
return defined('ARTISAN_BINARY') ? ProcessUtils::escapeArgument(ARTISAN_BINARY) : 'artisan';
return ProcessUtils::escapeArgument(defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan');
}
/**

View File

@@ -2,22 +2,17 @@
namespace Illuminate\Console;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Support\Arrayable;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
class Command extends SymfonyCommand
{
use Macroable;
use Concerns\CallsCommands,
Concerns\HasParameters,
Concerns\InteractsWithIO,
Macroable;
/**
* The Laravel application instance.
@@ -26,20 +21,6 @@ class Command extends SymfonyCommand
*/
protected $laravel;
/**
* The input interface implementation.
*
* @var \Symfony\Component\Console\Input\InputInterface
*/
protected $input;
/**
* The output interface implementation.
*
* @var \Illuminate\Console\OutputStyle
*/
protected $output;
/**
* The name and signature of the console command.
*
@@ -57,10 +38,17 @@ class Command extends SymfonyCommand
/**
* The console command description.
*
* @var string
* @var string|null
*/
protected $description;
/**
* The console command help text.
*
* @var string|null
*/
protected $help;
/**
* Indicates whether the command should be shown in the Artisan command list.
*
@@ -68,26 +56,6 @@ class Command extends SymfonyCommand
*/
protected $hidden = false;
/**
* The default verbosity of output commands.
*
* @var int
*/
protected $verbosity = OutputInterface::VERBOSITY_NORMAL;
/**
* The mapping between human readable verbosity levels and Symfony's OutputInterface.
*
* @var array
*/
protected $verbosityMap = [
'v' => OutputInterface::VERBOSITY_VERBOSE,
'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE,
'vvv' => OutputInterface::VERBOSITY_DEBUG,
'quiet' => OutputInterface::VERBOSITY_QUIET,
'normal' => OutputInterface::VERBOSITY_NORMAL,
];
/**
* Create a new console command instance.
*
@@ -107,7 +75,9 @@ class Command extends SymfonyCommand
// Once we have constructed the command, we'll set the description and other
// related properties of the command. If a signature wasn't used to build
// the command we'll set the arguments and the options on this command.
$this->setDescription($this->description);
$this->setDescription((string) $this->description);
$this->setHelp((string) $this->help);
$this->setHidden($this->isHidden());
@@ -134,25 +104,6 @@ class Command extends SymfonyCommand
$this->getDefinition()->addOptions($options);
}
/**
* Specify the arguments and options on the command.
*
* @return void
*/
protected function specifyParameters()
{
// We will loop through all of the arguments and options for the command and
// set them all on the base command instance. This specifies what can get
// passed into these commands as "parameters" to control the execution.
foreach ($this->getArguments() as $arguments) {
call_user_func_array([$this, 'addArgument'], $arguments);
}
foreach ($this->getOptions() as $options) {
call_user_func_array([$this, 'addOption'], $options);
}
}
/**
* Run the console command.
*
@@ -184,381 +135,28 @@ class Command extends SymfonyCommand
}
/**
* Call another console command.
* Resolve the console command instance for the given command.
*
* @param string $command
* @param array $arguments
* @return int
* @param \Symfony\Component\Console\Command\Command|string $command
* @return \Symfony\Component\Console\Command\Command
*/
public function call($command, array $arguments = [])
protected function resolveCommand($command)
{
$arguments['command'] = $command;
return $this->getApplication()->find($command)->run(
$this->createInputFromArguments($arguments), $this->output
);
}
/**
* Call another console command silently.
*
* @param string $command
* @param array $arguments
* @return int
*/
public function callSilent($command, array $arguments = [])
{
$arguments['command'] = $command;
return $this->getApplication()->find($command)->run(
$this->createInputFromArguments($arguments), new NullOutput
);
}
/**
* Create an input instance from the given arguments.
*
* @param array $arguments
* @return \Symfony\Component\Console\Input\ArrayInput
*/
protected function createInputFromArguments(array $arguments)
{
return tap(new ArrayInput(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.
*
* @param string|int $name
* @return bool
*/
public function hasArgument($name)
{
return $this->input->hasArgument($name);
}
/**
* Get the value of a command argument.
*
* @param string|null $key
* @return string|array|null
*/
public function argument($key = null)
{
if (is_null($key)) {
return $this->input->getArguments();
if (! class_exists($command)) {
return $this->getApplication()->find($command);
}
return $this->input->getArgument($key);
}
$command = $this->laravel->make($command);
/**
* Get all of the arguments passed to the command.
*
* @return array
*/
public function arguments()
{
return $this->argument();
}
/**
* Determine if the given option is present.
*
* @param string $name
* @return bool
*/
public function hasOption($name)
{
return $this->input->hasOption($name);
}
/**
* Get the value of a command option.
*
* @param string|null $key
* @return string|array|bool|null
*/
public function option($key = null)
{
if (is_null($key)) {
return $this->input->getOptions();
if ($command instanceof SymfonyCommand) {
$command->setApplication($this->getApplication());
}
return $this->input->getOption($key);
}
/**
* Get all of the options passed to the command.
*
* @return array
*/
public function options()
{
return $this->option();
}
/**
* Confirm a question with the user.
*
* @param string $question
* @param bool $default
* @return bool
*/
public function confirm($question, $default = false)
{
return $this->output->confirm($question, $default);
}
/**
* Prompt the user for input.
*
* @param string $question
* @param string|null $default
* @return mixed
*/
public function ask($question, $default = null)
{
return $this->output->ask($question, $default);
}
/**
* Prompt the user for input with auto completion.
*
* @param string $question
* @param array $choices
* @param string|null $default
* @return mixed
*/
public function anticipate($question, array $choices, $default = null)
{
return $this->askWithCompletion($question, $choices, $default);
}
/**
* Prompt the user for input with auto completion.
*
* @param string $question
* @param array $choices
* @param string|null $default
* @return mixed
*/
public function askWithCompletion($question, array $choices, $default = null)
{
$question = new Question($question, $default);
$question->setAutocompleterValues($choices);
return $this->output->askQuestion($question);
}
/**
* Prompt the user for input but hide the answer from the console.
*
* @param string $question
* @param bool $fallback
* @return mixed
*/
public function secret($question, $fallback = true)
{
$question = new Question($question);
$question->setHidden(true)->setHiddenFallback($fallback);
return $this->output->askQuestion($question);
}
/**
* Give the user a single choice from an array of answers.
*
* @param string $question
* @param array $choices
* @param string|null $default
* @param mixed|null $attempts
* @param bool|null $multiple
* @return string
*/
public function choice($question, array $choices, $default = null, $attempts = null, $multiple = null)
{
$question = new ChoiceQuestion($question, $choices, $default);
$question->setMaxAttempts($attempts)->setMultiselect($multiple);
return $this->output->askQuestion($question);
}
/**
* Format input to textual table.
*
* @param array $headers
* @param \Illuminate\Contracts\Support\Arrayable|array $rows
* @param string $tableStyle
* @param array $columnStyles
* @return void
*/
public function table($headers, $rows, $tableStyle = 'default', array $columnStyles = [])
{
$table = new Table($this->output);
if ($rows instanceof Arrayable) {
$rows = $rows->toArray();
if ($command instanceof self) {
$command->setLaravel($this->getLaravel());
}
$table->setHeaders((array) $headers)->setRows($rows)->setStyle($tableStyle);
foreach ($columnStyles as $columnIndex => $columnStyle) {
$table->setColumnStyle($columnIndex, $columnStyle);
}
$table->render();
}
/**
* Write a string as information output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function info($string, $verbosity = null)
{
$this->line($string, 'info', $verbosity);
}
/**
* Write a string as standard output.
*
* @param string $string
* @param string|null $style
* @param int|string|null $verbosity
* @return void
*/
public function line($string, $style = null, $verbosity = null)
{
$styled = $style ? "<$style>$string</$style>" : $string;
$this->output->writeln($styled, $this->parseVerbosity($verbosity));
}
/**
* Write a string as comment output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function comment($string, $verbosity = null)
{
$this->line($string, 'comment', $verbosity);
}
/**
* Write a string as question output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function question($string, $verbosity = null)
{
$this->line($string, 'question', $verbosity);
}
/**
* Write a string as error output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function error($string, $verbosity = null)
{
$this->line($string, 'error', $verbosity);
}
/**
* Write a string as warning output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function warn($string, $verbosity = null)
{
if (! $this->output->getFormatter()->hasStyle('warning')) {
$style = new OutputFormatterStyle('yellow');
$this->output->getFormatter()->setStyle('warning', $style);
}
$this->line($string, 'warning', $verbosity);
}
/**
* Write a string in an alert box.
*
* @param string $string
* @return void
*/
public function alert($string)
{
$length = Str::length(strip_tags($string)) + 12;
$this->comment(str_repeat('*', $length));
$this->comment('* '.$string.' *');
$this->comment(str_repeat('*', $length));
$this->output->newLine();
}
/**
* Set the verbosity level.
*
* @param string|int $level
* @return void
*/
protected function setVerbosity($level)
{
$this->verbosity = $this->parseVerbosity($level);
}
/**
* Get the verbosity level in terms of Symfony's OutputInterface level.
*
* @param string|int|null $level
* @return int
*/
protected function parseVerbosity($level = null)
{
if (isset($this->verbosityMap[$level])) {
$level = $this->verbosityMap[$level];
} elseif (! is_int($level)) {
$level = $this->verbosity;
}
return $level;
return $command;
}
/**
@@ -579,36 +177,6 @@ class Command extends SymfonyCommand
return $this;
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [];
}
/**
* Get the output implementation.
*
* @return \Illuminate\Console\OutputStyle
*/
public function getOutput()
{
return $this->output;
}
/**
* Get the Laravel application instance.
*

View File

@@ -0,0 +1,92 @@
<?php
namespace Illuminate\Console\Concerns;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\OutputInterface;
trait CallsCommands
{
/**
* Resolve the console command instance for the given command.
*
* @param \Symfony\Component\Console\Command\Command|string $command
* @return \Symfony\Component\Console\Command\Command
*/
abstract protected function resolveCommand($command);
/**
* Call another console command.
*
* @param \Symfony\Component\Console\Command\Command|string $command
* @param array $arguments
* @return int
*/
public function call($command, array $arguments = [])
{
return $this->runCommand($command, $arguments, $this->output);
}
/**
* Call another console command silently.
*
* @param \Symfony\Component\Console\Command\Command|string $command
* @param array $arguments
* @return int
*/
public function callSilent($command, array $arguments = [])
{
return $this->runCommand($command, $arguments, new NullOutput);
}
/**
* Run the given the console command.
*
* @param \Symfony\Component\Console\Command\Command|string $command
* @param array $arguments
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return int
*/
protected function runCommand($command, array $arguments, OutputInterface $output)
{
$arguments['command'] = $command;
return $this->resolveCommand($command)->run(
$this->createInputFromArguments($arguments), $output
);
}
/**
* Create an input instance from the given arguments.
*
* @param array $arguments
* @return \Symfony\Component\Console\Input\ArrayInput
*/
protected function createInputFromArguments(array $arguments)
{
return tap(new ArrayInput(array_merge($this->context(), $arguments)), function ($input) {
if ($input->getParameterOption('--no-interaction')) {
$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();
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Illuminate\Console\Concerns;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
trait HasParameters
{
/**
* Specify the arguments and options on the command.
*
* @return void
*/
protected function specifyParameters()
{
// We will loop through all of the arguments and options for the command and
// set them all on the base command instance. This specifies what can get
// passed into these commands as "parameters" to control the execution.
foreach ($this->getArguments() as $arguments) {
if ($arguments instanceof InputArgument) {
$this->getDefinition()->addArgument($arguments);
} else {
$this->addArgument(...array_values($arguments));
}
}
foreach ($this->getOptions() as $options) {
if ($options instanceof InputOption) {
$this->getDefinition()->addOption($options);
} else {
$this->addOption(...array_values($options));
}
}
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [];
}
}

View File

@@ -0,0 +1,397 @@
<?php
namespace Illuminate\Console\Concerns;
use Illuminate\Console\OutputStyle;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Str;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
trait InteractsWithIO
{
/**
* The input interface implementation.
*
* @var \Symfony\Component\Console\Input\InputInterface
*/
protected $input;
/**
* The output interface implementation.
*
* @var \Illuminate\Console\OutputStyle
*/
protected $output;
/**
* The default verbosity of output commands.
*
* @var int
*/
protected $verbosity = OutputInterface::VERBOSITY_NORMAL;
/**
* The mapping between human readable verbosity levels and Symfony's OutputInterface.
*
* @var array
*/
protected $verbosityMap = [
'v' => OutputInterface::VERBOSITY_VERBOSE,
'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE,
'vvv' => OutputInterface::VERBOSITY_DEBUG,
'quiet' => OutputInterface::VERBOSITY_QUIET,
'normal' => OutputInterface::VERBOSITY_NORMAL,
];
/**
* Determine if the given argument is present.
*
* @param string|int $name
* @return bool
*/
public function hasArgument($name)
{
return $this->input->hasArgument($name);
}
/**
* Get the value of a command argument.
*
* @param string|null $key
* @return string|array|null
*/
public function argument($key = null)
{
if (is_null($key)) {
return $this->input->getArguments();
}
return $this->input->getArgument($key);
}
/**
* Get all of the arguments passed to the command.
*
* @return array
*/
public function arguments()
{
return $this->argument();
}
/**
* Determine if the given option is present.
*
* @param string $name
* @return bool
*/
public function hasOption($name)
{
return $this->input->hasOption($name);
}
/**
* Get the value of a command option.
*
* @param string|null $key
* @return string|array|bool|null
*/
public function option($key = null)
{
if (is_null($key)) {
return $this->input->getOptions();
}
return $this->input->getOption($key);
}
/**
* Get all of the options passed to the command.
*
* @return array
*/
public function options()
{
return $this->option();
}
/**
* Confirm a question with the user.
*
* @param string $question
* @param bool $default
* @return bool
*/
public function confirm($question, $default = false)
{
return $this->output->confirm($question, $default);
}
/**
* Prompt the user for input.
*
* @param string $question
* @param string|null $default
* @return mixed
*/
public function ask($question, $default = null)
{
return $this->output->ask($question, $default);
}
/**
* Prompt the user for input with auto completion.
*
* @param string $question
* @param array|callable $choices
* @param string|null $default
* @return mixed
*/
public function anticipate($question, $choices, $default = null)
{
return $this->askWithCompletion($question, $choices, $default);
}
/**
* Prompt the user for input with auto completion.
*
* @param string $question
* @param array|callable $choices
* @param string|null $default
* @return mixed
*/
public function askWithCompletion($question, $choices, $default = null)
{
$question = new Question($question, $default);
is_callable($choices)
? $question->setAutocompleterCallback($choices)
: $question->setAutocompleterValues($choices);
return $this->output->askQuestion($question);
}
/**
* Prompt the user for input but hide the answer from the console.
*
* @param string $question
* @param bool $fallback
* @return mixed
*/
public function secret($question, $fallback = true)
{
$question = new Question($question);
$question->setHidden(true)->setHiddenFallback($fallback);
return $this->output->askQuestion($question);
}
/**
* Give the user a single choice from an array of answers.
*
* @param string $question
* @param array $choices
* @param string|null $default
* @param mixed|null $attempts
* @param bool|null $multiple
* @return string
*/
public function choice($question, array $choices, $default = null, $attempts = null, $multiple = null)
{
$question = new ChoiceQuestion($question, $choices, $default);
$question->setMaxAttempts($attempts)->setMultiselect($multiple);
return $this->output->askQuestion($question);
}
/**
* Format input to textual table.
*
* @param array $headers
* @param \Illuminate\Contracts\Support\Arrayable|array $rows
* @param string $tableStyle
* @param array $columnStyles
* @return void
*/
public function table($headers, $rows, $tableStyle = 'default', array $columnStyles = [])
{
$table = new Table($this->output);
if ($rows instanceof Arrayable) {
$rows = $rows->toArray();
}
$table->setHeaders((array) $headers)->setRows($rows)->setStyle($tableStyle);
foreach ($columnStyles as $columnIndex => $columnStyle) {
$table->setColumnStyle($columnIndex, $columnStyle);
}
$table->render();
}
/**
* Write a string as information output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function info($string, $verbosity = null)
{
$this->line($string, 'info', $verbosity);
}
/**
* Write a string as standard output.
*
* @param string $string
* @param string|null $style
* @param int|string|null $verbosity
* @return void
*/
public function line($string, $style = null, $verbosity = null)
{
$styled = $style ? "<$style>$string</$style>" : $string;
$this->output->writeln($styled, $this->parseVerbosity($verbosity));
}
/**
* Write a string as comment output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function comment($string, $verbosity = null)
{
$this->line($string, 'comment', $verbosity);
}
/**
* Write a string as question output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function question($string, $verbosity = null)
{
$this->line($string, 'question', $verbosity);
}
/**
* Write a string as error output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function error($string, $verbosity = null)
{
$this->line($string, 'error', $verbosity);
}
/**
* Write a string as warning output.
*
* @param string $string
* @param int|string|null $verbosity
* @return void
*/
public function warn($string, $verbosity = null)
{
if (! $this->output->getFormatter()->hasStyle('warning')) {
$style = new OutputFormatterStyle('yellow');
$this->output->getFormatter()->setStyle('warning', $style);
}
$this->line($string, 'warning', $verbosity);
}
/**
* Write a string in an alert box.
*
* @param string $string
* @return void
*/
public function alert($string)
{
$length = Str::length(strip_tags($string)) + 12;
$this->comment(str_repeat('*', $length));
$this->comment('* '.$string.' *');
$this->comment(str_repeat('*', $length));
$this->output->newLine();
}
/**
* Set the input interface implementation.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @return void
*/
public function setInput(InputInterface $input)
{
$this->input = $input;
}
/**
* Set the output interface implementation.
*
* @param \Illuminate\Console\OutputStyle $output
* @return void
*/
public function setOutput(OutputStyle $output)
{
$this->output = $output;
}
/**
* Set the verbosity level.
*
* @param string|int $level
* @return void
*/
protected function setVerbosity($level)
{
$this->verbosity = $this->parseVerbosity($level);
}
/**
* Get the verbosity level in terms of Symfony's OutputInterface level.
*
* @param string|int|null $level
* @return int
*/
protected function parseVerbosity($level = null)
{
if (isset($this->verbosityMap[$level])) {
$level = $this->verbosityMap[$level];
} elseif (! is_int($level)) {
$level = $this->verbosity;
}
return $level;
}
/**
* Get the output implementation.
*
* @return \Illuminate\Console\OutputStyle
*/
public function getOutput()
{
return $this->output;
}
}

View File

@@ -2,8 +2,6 @@
namespace Illuminate\Console;
use Closure;
trait ConfirmableTrait
{
/**
@@ -19,7 +17,7 @@ trait ConfirmableTrait
{
$callback = is_null($callback) ? $this->getDefaultConfirmCallback() : $callback;
$shouldConfirm = $callback instanceof Closure ? call_user_func($callback) : $callback;
$shouldConfirm = value($callback);
if ($shouldConfirm) {
if ($this->hasOption('force') && $this->option('force')) {
@@ -31,7 +29,7 @@ trait ConfirmableTrait
$confirmed = $this->confirm('Do you really wish to run this command?');
if (! $confirmed) {
$this->comment('Command Cancelled!');
$this->comment('Command Canceled!');
return false;
}

View File

@@ -4,6 +4,9 @@ namespace Illuminate\Console;
use Illuminate\Container\Container;
/**
* @deprecated Usage of this trait is deprecated and it will be removed in Laravel 7.0.
*/
trait DetectsApplicationNamespace
{
/**

View File

@@ -0,0 +1,35 @@
<?php
namespace Illuminate\Console\Events;
use Illuminate\Console\Scheduling\Event;
class ScheduledTaskFinished
{
/**
* The scheduled event that ran.
*
* @var \Illuminate\Console\Scheduling\Event
*/
public $task;
/**
* The runtime of the scheduled event.
*
* @var float
*/
public $runtime;
/**
* Create a new event instance.
*
* @param \Illuminate\Console\Scheduling\Event $task
* @param float $runtime
* @return void
*/
public function __construct(Event $task, $runtime)
{
$this->task = $task;
$this->runtime = $runtime;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Illuminate\Console\Events;
use Illuminate\Console\Scheduling\Event;
class ScheduledTaskSkipped
{
/**
* The scheduled event being run.
*
* @var \Illuminate\Console\Scheduling\Event
*/
public $task;
/**
* Create a new event instance.
*
* @param \Illuminate\Console\Scheduling\Event $task
* @return void
*/
public function __construct(Event $task)
{
$this->task = $task;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Illuminate\Console\Events;
use Illuminate\Console\Scheduling\Event;
class ScheduledTaskStarting
{
/**
* The scheduled event being run.
*
* @var \Illuminate\Console\Scheduling\Event
*/
public $task;
/**
* Create a new event instance.
*
* @param \Illuminate\Console\Scheduling\Event $task
* @return void
*/
public function __construct(Event $task)
{
$this->task = $task;
}
}

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Console;
use Illuminate\Support\Str;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use Symfony\Component\Console\Input\InputArgument;
abstract class GeneratorCommand extends Command
@@ -46,6 +46,7 @@ abstract class GeneratorCommand extends Command
* Execute the console command.
*
* @return bool|null
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function handle()
@@ -70,7 +71,7 @@ abstract class GeneratorCommand extends Command
// stub files so that it gets the correctly formatted namespace and class name.
$this->makeDirectory($path);
$this->files->put($path, $this->buildClass($name));
$this->files->put($path, $this->sortImports($this->buildClass($name)));
$this->info($this->type.' created successfully.');
}
@@ -153,6 +154,7 @@ abstract class GeneratorCommand extends Command
*
* @param string $name
* @return string
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function buildClass($name)
@@ -205,6 +207,25 @@ abstract class GeneratorCommand extends Command
return str_replace('DummyClass', $class, $stub);
}
/**
* Alphabetically sorts the imports for the given stub.
*
* @param string $stub
* @return string
*/
protected function sortImports($stub)
{
if (preg_match('/(?P<imports>(?:use [^;]+;$\n?)+)/m', $stub, $match)) {
$imports = explode("\n", trim($match['imports']));
sort($imports);
return str_replace(trim($match['imports']), implode("\n", $imports), $stub);
}
return $stub;
}
/**
* Get the desired class name from the input.
*
@@ -232,11 +253,11 @@ abstract class GeneratorCommand extends Command
*/
protected function userProviderModel()
{
$guard = config('auth.defaults.guard');
$config = $this->laravel['config'];
$provider = config("auth.guards.{$guard}.provider");
$provider = $config->get('auth.guards.'.$config->get('auth.defaults.guard').'.provider');
return config("auth.providers.{$provider}.model");
return $config->get("auth.providers.{$provider}.model");
}
/**

View File

@@ -2,9 +2,9 @@
namespace Illuminate\Console;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class OutputStyle extends SymfonyStyle
{

View File

@@ -4,8 +4,8 @@ namespace Illuminate\Console;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class Parser
{
@@ -40,10 +40,6 @@ class Parser
*/
protected static function name($expression)
{
if (trim($expression) === '') {
throw new InvalidArgumentException('Console command definition is empty.');
}
if (! preg_match('/[^\s]+/', $expression, $matches)) {
throw new InvalidArgumentException('Unable to determine command name from signature.');
}

View File

@@ -2,9 +2,10 @@
namespace Illuminate\Console\Scheduling;
use LogicException;
use InvalidArgumentException;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Reflector;
use InvalidArgumentException;
use LogicException;
class CallbackEvent extends Event
{
@@ -28,13 +29,14 @@ class CallbackEvent extends Event
* @param \Illuminate\Console\Scheduling\EventMutex $mutex
* @param string $callback
* @param array $parameters
* @param \DateTimeZone|string|null $timezone
* @return void
*
* @throws \InvalidArgumentException
*/
public function __construct(EventMutex $mutex, $callback, array $parameters = [])
public function __construct(EventMutex $mutex, $callback, array $parameters = [], $timezone = null)
{
if (! is_string($callback) && ! is_callable($callback)) {
if (! is_string($callback) && ! Reflector::isCallable($callback)) {
throw new InvalidArgumentException(
'Invalid scheduled callback event. Must be a string or callable.'
);
@@ -43,6 +45,7 @@ class CallbackEvent extends Event
$this->mutex = $mutex;
$this->callback = $callback;
$this->parameters = $parameters;
$this->timezone = $timezone;
}
/**
@@ -161,6 +164,6 @@ class CallbackEvent extends Event
return $this->description;
}
return is_string($this->callback) ? $this->callback : 'Closure';
return is_string($this->callback) ? $this->callback : 'Callback';
}
}

View File

@@ -51,8 +51,12 @@ class CommandBuilder
$finished = Application::formatCommandString('schedule:finish').' "'.$event->mutexName().'"';
if (windows_os()) {
return 'start /b cmd /c "('.$event->command.' & '.$finished.' "%errorlevel%")'.$redirect.$output.' 2>&1"';
}
return $this->ensureCorrectUser($event,
'('.$event->command.$redirect.$output.' 2>&1 '.(windows_os() ? '&' : ';').' '.$finished.') > '
'('.$event->command.$redirect.$output.' 2>&1 ; '.$finished.' "$?") > '
.ProcessUtils::escapeArgument($event->getDefaultOutput()).' 2>&1 &'
);
}

View File

@@ -4,14 +4,17 @@ namespace Illuminate\Console\Scheduling;
use Closure;
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;
use GuzzleHttp\Exception\TransferException;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Reflector;
use Illuminate\Support\Traits\Macroable;
use Psr\Http\Client\ClientExceptionInterface;
use Symfony\Component\Process\Process;
class Event
{
@@ -155,7 +158,7 @@ class Event
*
* @param \Illuminate\Console\Scheduling\EventMutex $mutex
* @param string $command
* @param \DateTimeZone|string|null $timezone
* @param \DateTimeZone|string|null $timezone
* @return void
*/
public function __construct(EventMutex $mutex, $command, $timezone = null)
@@ -259,6 +262,20 @@ class Event
}
}
/**
* Call all of the "after" callbacks for the event.
*
* @param \Illuminate\Contracts\Container\Container $container
* @param int $exitCode
* @return void
*/
public function callAfterCallbacksWithExitCode(Container $container, $exitCode)
{
$this->exitCode = (int) $exitCode;
$this->callAfterCallbacks($container);
}
/**
* Build the command string.
*
@@ -302,10 +319,10 @@ class Event
*/
protected function expressionPasses()
{
$date = Carbon::now();
$date = Date::now();
if ($this->timezone) {
$date->setTimezone($this->timezone);
$date = $date->setTimezone($this->timezone);
}
return CronExpression::factory($this->expression)->isDue($date->toDateTimeString());
@@ -489,9 +506,7 @@ class Event
*/
public function pingBefore($url)
{
return $this->before(function () use ($url) {
(new HttpClient)->get($url);
});
return $this->before($this->pingCallback($url));
}
/**
@@ -514,9 +529,7 @@ class Event
*/
public function thenPing($url)
{
return $this->then(function () use ($url) {
(new HttpClient)->get($url);
});
return $this->then($this->pingCallback($url));
}
/**
@@ -539,9 +552,7 @@ class Event
*/
public function pingOnSuccess($url)
{
return $this->onSuccess(function () use ($url) {
(new HttpClient)->get($url);
});
return $this->onSuccess($this->pingCallback($url));
}
/**
@@ -552,9 +563,24 @@ class Event
*/
public function pingOnFailure($url)
{
return $this->onFailure(function () use ($url) {
(new HttpClient)->get($url);
});
return $this->onFailure($this->pingCallback($url));
}
/**
* Get the callback that pings the given URL.
*
* @param string $url
* @return \Closure
*/
protected function pingCallback($url)
{
return function (Container $container, HttpClient $http) use ($url) {
try {
$http->get($url);
} catch (ClientExceptionInterface|TransferException $e) {
$container->make(ExceptionHandler::class)->report($e);
}
};
}
/**
@@ -646,7 +672,7 @@ class Event
*/
public function when($callback)
{
$this->filters[] = is_callable($callback) ? $callback : function () use ($callback) {
$this->filters[] = Reflector::isCallable($callback) ? $callback : function () use ($callback) {
return $callback;
};
@@ -661,7 +687,7 @@ class Event
*/
public function skip($callback)
{
$this->rejects[] = is_callable($callback) ? $callback : function () use ($callback) {
$this->rejects[] = Reflector::isCallable($callback) ? $callback : function () use ($callback) {
return $callback;
};

View File

@@ -52,12 +52,22 @@ trait ManagesFrequencies
*/
private function inTimeInterval($startTime, $endTime)
{
return function () use ($startTime, $endTime) {
return Carbon::now($this->timezone)->between(
Carbon::parse($startTime, $this->timezone),
Carbon::parse($endTime, $this->timezone),
true
);
[$now, $startTime, $endTime] = [
Carbon::now($this->timezone),
Carbon::parse($startTime, $this->timezone),
Carbon::parse($endTime, $this->timezone),
];
if ($endTime->lessThan($startTime)) {
if ($startTime->greaterThan($now)) {
$startTime->subDay(1);
} else {
$endTime->addDay(1);
}
}
return function () use ($now, $startTime, $endTime) {
return $now->between($startTime, $endTime);
};
}

View File

@@ -2,14 +2,23 @@
namespace Illuminate\Console\Scheduling;
use Closure;
use DateTimeInterface;
use Illuminate\Console\Application;
use Illuminate\Container\Container;
use Illuminate\Support\ProcessUtils;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\CallQueuedClosure;
use Illuminate\Support\ProcessUtils;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use RuntimeException;
class Schedule
{
use Macroable;
/**
* All of the events on the schedule.
*
@@ -38,6 +47,13 @@ class Schedule
*/
protected $timezone;
/**
* The job dispatcher implementation.
*
* @var \Illuminate\Contracts\Bus\Dispatcher
*/
protected $dispatcher;
/**
* Create a new schedule instance.
*
@@ -48,6 +64,12 @@ class Schedule
{
$this->timezone = $timezone;
if (! class_exists(Container::class)) {
throw new RuntimeException(
'A container implementation is required to use the scheduler. Please install the illuminate/container package.'
);
}
$container = Container::getInstance();
$this->eventMutex = $container->bound(EventMutex::class)
@@ -69,7 +91,7 @@ class Schedule
public function call($callback, array $parameters = [])
{
$this->events[] = $event = new CallbackEvent(
$this->eventMutex, $callback, $parameters
$this->eventMutex, $callback, $parameters, $this->timezone
);
return $event;
@@ -104,18 +126,52 @@ class Schedule
public function job($job, $queue = null, $connection = null)
{
return $this->call(function () use ($job, $queue, $connection) {
$job = is_string($job) ? resolve($job) : $job;
$job = is_string($job) ? Container::getInstance()->make($job) : $job;
if ($job instanceof ShouldQueue) {
dispatch($job)
->onConnection($connection ?? $job->connection)
->onQueue($queue ?? $job->queue);
$this->dispatchToQueue($job, $queue ?? $job->queue, $connection ?? $job->connection);
} else {
dispatch_now($job);
$this->dispatchNow($job);
}
})->name(is_string($job) ? $job : get_class($job));
}
/**
* Dispatch the given job to the queue.
*
* @param object $job
* @param string|null $queue
* @param string|null $connection
* @return void
*/
protected function dispatchToQueue($job, $queue, $connection)
{
if ($job instanceof Closure) {
if (! class_exists(CallQueuedClosure::class)) {
throw new RuntimeException(
'To enable support for closure jobs, please install the illuminate/queue package.'
);
}
$job = CallQueuedClosure::create($job);
}
$this->getDispatcher()->dispatch(
$job->onConnection($connection)->onQueue($queue)
);
}
/**
* Dispatch the given job right now.
*
* @param object $job
* @return void
*/
protected function dispatchNow($job)
{
$this->getDispatcher()->dispatchNow($job);
}
/**
* Add a new command event to the schedule.
*
@@ -144,10 +200,10 @@ class Schedule
{
return collect($parameters)->map(function ($value, $key) {
if (is_array($value)) {
$value = collect($value)->map(function ($value) {
return ProcessUtils::escapeArgument($value);
})->implode(' ');
} elseif (! is_numeric($value) && ! preg_match('/^(-.$|--.*)/i', $value)) {
return $this->compileArrayInput($key, $value);
}
if (! is_numeric($value) && ! preg_match('/^(-.$|--.*)/i', $value)) {
$value = ProcessUtils::escapeArgument($value);
}
@@ -155,6 +211,32 @@ class Schedule
})->implode(' ');
}
/**
* Compile array input for a command.
*
* @param string|int $key
* @param array $value
* @return string
*/
public function compileArrayInput($key, $value)
{
$value = collect($value)->map(function ($value) {
return ProcessUtils::escapeArgument($value);
});
if (Str::startsWith($key, '--')) {
$value = $value->map(function ($value) use ($key) {
return "{$key}={$value}";
});
} elseif (Str::startsWith($key, '-')) {
$value = $value->map(function ($value) use ($key) {
return "{$key} {$value}";
});
}
return $value->implode(' ');
}
/**
* Determine if the server is allowed to run this event.
*
@@ -206,4 +288,25 @@ class Schedule
return $this;
}
/**
* Get the job dispatcher, if available.
*
* @return \Illuminate\Contracts\Bus\Dispatcher
*/
protected function getDispatcher()
{
if ($this->dispatcher === null) {
try {
$this->dispatcher = Container::getInstance()->make(Dispatcher::class);
} catch (BindingResolutionException $e) {
throw new RuntimeException(
'Unable to resolve the dispatcher from the service container. Please bind it or install the illuminate/bus package.',
$e->getCode(), $e
);
}
}
return $this->dispatcher;
}
}

View File

@@ -11,7 +11,7 @@ class ScheduleFinishCommand extends Command
*
* @var string
*/
protected $signature = 'schedule:finish {id}';
protected $signature = 'schedule:finish {id} {code=0}';
/**
* The console command description.
@@ -28,34 +28,15 @@ class ScheduleFinishCommand extends Command
protected $hidden = true;
/**
* The schedule instance.
*
* @var \Illuminate\Console\Scheduling\Schedule
*/
protected $schedule;
/**
* Create a new command instance.
* Execute the console command.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
public function __construct(Schedule $schedule)
public function handle(Schedule $schedule)
{
$this->schedule = $schedule;
parent::__construct();
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
collect($this->schedule->events())->filter(function ($value) {
collect($schedule->events())->filter(function ($value) {
return $value->mutexName() == $this->argument('id');
})->each->callAfterCallbacks($this->laravel);
})->each->callAfterCallbacksWithExitCode($this->laravel, $this->argument('code'));
}
}

View File

@@ -3,6 +3,10 @@
namespace Illuminate\Console\Scheduling;
use Illuminate\Console\Command;
use Illuminate\Console\Events\ScheduledTaskFinished;
use Illuminate\Console\Events\ScheduledTaskSkipped;
use Illuminate\Console\Events\ScheduledTaskStarting;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\Date;
class ScheduleRunCommand extends Command
@@ -31,7 +35,7 @@ class ScheduleRunCommand extends Command
/**
* The 24 hour timestamp this scheduler command started running.
*
* @var \Illuminate\Support\Carbon;
* @var \Illuminate\Support\Carbon
*/
protected $startedAt;
@@ -42,16 +46,20 @@ class ScheduleRunCommand extends Command
*/
protected $eventsRan = false;
/**
* The event dispatcher.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $dispatcher;
/**
* Create a new command instance.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
public function __construct(Schedule $schedule)
public function __construct()
{
$this->schedule = $schedule;
$this->startedAt = Date::now();
parent::__construct();
@@ -60,12 +68,19 @@ class ScheduleRunCommand extends Command
/**
* Execute the console command.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
* @return void
*/
public function handle()
public function handle(Schedule $schedule, Dispatcher $dispatcher)
{
$this->schedule = $schedule;
$this->dispatcher = $dispatcher;
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
$this->dispatcher->dispatch(new ScheduledTaskSkipped($event));
continue;
}
@@ -108,8 +123,17 @@ class ScheduleRunCommand extends Command
{
$this->line('<info>Running scheduled command:</info> '.$event->getSummaryForDisplay());
$this->dispatcher->dispatch(new ScheduledTaskStarting($event));
$start = microtime(true);
$event->run($this->laravel);
$this->dispatcher->dispatch(new ScheduledTaskFinished(
$event,
round(microtime(true) - $start, 2)
));
$this->eventsRan = true;
}
}

View File

@@ -14,11 +14,11 @@
}
],
"require": {
"php": "^7.1.3",
"illuminate/contracts": "5.8.*",
"illuminate/support": "5.8.*",
"symfony/console": "^4.2",
"symfony/process": "^4.2"
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/support": "^6.0",
"symfony/console": "^4.3.4",
"symfony/process": "^4.3.4"
},
"autoload": {
"psr-4": {
@@ -27,13 +27,16 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.8-dev"
"dev-master": "6.x-dev"
}
},
"suggest": {
"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.*)"
"dragonmantank/cron-expression": "Required to use scheduler (^2.3.1).",
"guzzlehttp/guzzle": "Required to use the ping methods on schedules (^6.3.1|^7.0.1).",
"illuminate/bus": "Required to use the scheduled job dispatcher (^6.0)",
"illuminate/container": "Required to use the scheduler (^6.0)",
"illuminate/filesystem": "Required to use the generator command (^6.0)",
"illuminate/queue": "Required to use closures for scheduled jobs (^6.0)"
},
"config": {
"sort-packages": true

View File

@@ -3,9 +3,10 @@
namespace Illuminate\Container;
use Closure;
use ReflectionMethod;
use ReflectionFunction;
use Illuminate\Contracts\Container\BindingResolutionException;
use InvalidArgumentException;
use ReflectionFunction;
use ReflectionMethod;
class BoundMethod
{
@@ -23,14 +24,16 @@ class BoundMethod
*/
public static function call($container, $callback, array $parameters = [], $defaultMethod = null)
{
if (is_string($callback) && ! $defaultMethod && method_exists($callback, '__invoke')) {
$defaultMethod = '__invoke';
}
if (static::isCallableWithAtSign($callback) || $defaultMethod) {
return static::callClass($container, $callback, $parameters, $defaultMethod);
}
return static::callBoundMethod($container, $callback, function () use ($container, $callback, $parameters) {
return call_user_func_array(
$callback, static::getMethodDependencies($container, $callback, $parameters)
);
return $callback(...array_values(static::getMethodDependencies($container, $callback, $parameters)));
});
}
@@ -75,7 +78,7 @@ class BoundMethod
protected static function callBoundMethod($container, $callback, $default)
{
if (! is_array($callback)) {
return $default instanceof Closure ? $default() : $default;
return Util::unwrapIfClosure($default);
}
// Here we need to turn the array callable into a Class@method string we can use to
@@ -87,7 +90,7 @@ class BoundMethod
return $container->callMethodBinding($method, $callback[0]);
}
return $default instanceof Closure ? $default() : $default;
return Util::unwrapIfClosure($default);
}
/**
@@ -121,13 +124,13 @@ class BoundMethod
static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies);
}
return array_merge($dependencies, $parameters);
return array_merge($dependencies, array_values($parameters));
}
/**
* Get the proper reflection instance for the given callback.
*
* @param callable|string $callback
* @param callable|string $callback
* @return \ReflectionFunctionAbstract
*
* @throws \ReflectionException
@@ -136,6 +139,8 @@ class BoundMethod
{
if (is_string($callback) && strpos($callback, '::') !== false) {
$callback = explode('::', $callback);
} elseif (is_object($callback) && ! $callback instanceof Closure) {
$callback = [$callback, '__invoke'];
}
return is_array($callback)
@@ -155,18 +160,24 @@ class BoundMethod
protected static function addDependencyForCallParameter($container, $parameter,
array &$parameters, &$dependencies)
{
if (array_key_exists($parameter->name, $parameters)) {
$dependencies[] = $parameters[$parameter->name];
if (array_key_exists($paramName = $parameter->getName(), $parameters)) {
$dependencies[] = $parameters[$paramName];
unset($parameters[$parameter->name]);
} elseif ($parameter->getClass() && array_key_exists($parameter->getClass()->name, $parameters)) {
$dependencies[] = $parameters[$parameter->getClass()->name];
unset($parameters[$paramName]);
} elseif (! is_null($className = Util::getParameterClassName($parameter))) {
if (array_key_exists($className, $parameters)) {
$dependencies[] = $parameters[$className];
unset($parameters[$parameter->getClass()->name]);
} elseif ($parameter->getClass()) {
$dependencies[] = $container->make($parameter->getClass()->name);
unset($parameters[$className]);
} else {
$dependencies[] = $container->make($className);
}
} elseif ($parameter->isDefaultValueAvailable()) {
$dependencies[] = $parameter->getDefaultValue();
} elseif (! $parameter->isOptional() && ! array_key_exists($paramName, $parameters)) {
$message = "Unable to resolve dependency [{$parameter}] in class {$parameter->getDeclaringClass()->getName()}";
throw new BindingResolutionException($message);
}
}

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