Commaaa2
This commit is contained in:
@@ -146,6 +146,7 @@ abstract class AbstractRouteCollection implements Countable, IteratorAggregate,
|
||||
'bindingFields' => $route->bindingFields(),
|
||||
'lockSeconds' => $route->locksFor(),
|
||||
'waitSeconds' => $route->waitsFor(),
|
||||
'withTrashed' => $route->allowsTrashedBindings(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -194,13 +195,18 @@ abstract class AbstractRouteCollection implements Countable, IteratorAggregate,
|
||||
* @param \Symfony\Component\Routing\RouteCollection $symfonyRoutes
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @return \Symfony\Component\Routing\RouteCollection
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
protected function addToSymfonyRoutesCollection(SymfonyRouteCollection $symfonyRoutes, Route $route)
|
||||
{
|
||||
$name = $route->getName();
|
||||
|
||||
if (Str::endsWith($name, '.') &&
|
||||
! is_null($symfonyRoutes->get($name))) {
|
||||
if (
|
||||
! is_null($name)
|
||||
&& Str::endsWith($name, '.')
|
||||
&& ! is_null($symfonyRoutes->get($name))
|
||||
) {
|
||||
$name = null;
|
||||
}
|
||||
|
||||
@@ -232,6 +238,7 @@ abstract class AbstractRouteCollection implements Countable, IteratorAggregate,
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->getRoutes());
|
||||
@@ -242,6 +249,7 @@ abstract class AbstractRouteCollection implements Countable, IteratorAggregate,
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->getRoutes());
|
||||
|
||||
@@ -152,7 +152,7 @@ class CompiledRouteCollection extends AbstractRouteCollection
|
||||
*/
|
||||
protected function requestWithoutTrailingSlash(Request $request)
|
||||
{
|
||||
$trimmedRequest = Request::createFromBase($request);
|
||||
$trimmedRequest = $request->duplicate();
|
||||
|
||||
$parts = explode('?', $request->server->get('REQUEST_URI'), 2);
|
||||
|
||||
@@ -252,7 +252,7 @@ class CompiledRouteCollection extends AbstractRouteCollection
|
||||
})
|
||||
->map(function (Collection $routes) {
|
||||
return $routes->mapWithKeys(function (Route $route) {
|
||||
return [$route->uri => $route];
|
||||
return [$route->getDomain().$route->uri => $route];
|
||||
})->all();
|
||||
})
|
||||
->all();
|
||||
@@ -293,12 +293,13 @@ class CompiledRouteCollection extends AbstractRouteCollection
|
||||
), '/');
|
||||
}
|
||||
|
||||
return $this->router->newRoute($attributes['methods'], $baseUri == '' ? '/' : $baseUri, $attributes['action'])
|
||||
return $this->router->newRoute($attributes['methods'], $baseUri === '' ? '/' : $baseUri, $attributes['action'])
|
||||
->setFallback($attributes['fallback'])
|
||||
->setDefaults($attributes['defaults'])
|
||||
->setWheres($attributes['wheres'])
|
||||
->setBindingFields($attributes['bindingFields'])
|
||||
->block($attributes['lockSeconds'] ?? null, $attributes['waitSeconds'] ?? null);
|
||||
->block($attributes['lockSeconds'] ?? null, $attributes['waitSeconds'] ?? null)
|
||||
->withTrashed($attributes['withTrashed'] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
82
vendor/laravel/framework/src/Illuminate/Routing/Console/ControllerMakeCommand.php
vendored
Normal file → Executable file
82
vendor/laravel/framework/src/Illuminate/Routing/Console/ControllerMakeCommand.php
vendored
Normal file → Executable file
@@ -2,13 +2,15 @@
|
||||
|
||||
namespace Illuminate\Routing\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class ControllerMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
@@ -39,7 +41,9 @@ class ControllerMakeCommand extends GeneratorCommand
|
||||
{
|
||||
$stub = null;
|
||||
|
||||
if ($this->option('parent')) {
|
||||
if ($type = $this->option('type')) {
|
||||
$stub = "/stubs/controller.{$type}.stub";
|
||||
} elseif ($this->option('parent')) {
|
||||
$stub = '/stubs/controller.nested.stub';
|
||||
} elseif ($this->option('model')) {
|
||||
$stub = '/stubs/controller.model.stub';
|
||||
@@ -87,7 +91,7 @@ class ControllerMakeCommand extends GeneratorCommand
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* Remove the base controller import if we are already in base namespace.
|
||||
* Remove the base controller import if we are already in the base namespace.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
@@ -157,6 +161,8 @@ class ControllerMakeCommand extends GeneratorCommand
|
||||
}
|
||||
}
|
||||
|
||||
$replace = $this->buildFormRequestReplacements($replace, $modelClass);
|
||||
|
||||
return array_merge($replace, [
|
||||
'DummyFullModelClass' => $modelClass,
|
||||
'{{ namespacedModel }}' => $modelClass,
|
||||
@@ -184,13 +190,73 @@ class ControllerMakeCommand extends GeneratorCommand
|
||||
throw new InvalidArgumentException('Model name contains invalid characters.');
|
||||
}
|
||||
|
||||
$model = trim(str_replace('/', '\\', $model), '\\');
|
||||
return $this->qualifyModel($model);
|
||||
}
|
||||
|
||||
if (! Str::startsWith($model, $rootNamespace = $this->laravel->getNamespace())) {
|
||||
$model = $rootNamespace.$model;
|
||||
/**
|
||||
* Build the model replacement values.
|
||||
*
|
||||
* @param array $replace
|
||||
* @param string $modelClass
|
||||
* @return array
|
||||
*/
|
||||
protected function buildFormRequestReplacements(array $replace, $modelClass)
|
||||
{
|
||||
[$namespace, $storeRequestClass, $updateRequestClass] = [
|
||||
'Illuminate\\Http', 'Request', 'Request',
|
||||
];
|
||||
|
||||
if ($this->option('requests')) {
|
||||
$namespace = 'App\\Http\\Requests';
|
||||
|
||||
[$storeRequestClass, $updateRequestClass] = $this->generateFormRequests(
|
||||
$modelClass, $storeRequestClass, $updateRequestClass
|
||||
);
|
||||
}
|
||||
|
||||
return $model;
|
||||
$namespacedRequests = $namespace.'\\'.$storeRequestClass.';';
|
||||
|
||||
if ($storeRequestClass !== $updateRequestClass) {
|
||||
$namespacedRequests .= PHP_EOL.'use '.$namespace.'\\'.$updateRequestClass.';';
|
||||
}
|
||||
|
||||
return array_merge($replace, [
|
||||
'{{ storeRequest }}' => $storeRequestClass,
|
||||
'{{storeRequest}}' => $storeRequestClass,
|
||||
'{{ updateRequest }}' => $updateRequestClass,
|
||||
'{{updateRequest}}' => $updateRequestClass,
|
||||
'{{ namespacedStoreRequest }}' => $namespace.'\\'.$storeRequestClass,
|
||||
'{{namespacedStoreRequest}}' => $namespace.'\\'.$storeRequestClass,
|
||||
'{{ namespacedUpdateRequest }}' => $namespace.'\\'.$updateRequestClass,
|
||||
'{{namespacedUpdateRequest}}' => $namespace.'\\'.$updateRequestClass,
|
||||
'{{ namespacedRequests }}' => $namespacedRequests,
|
||||
'{{namespacedRequests}}' => $namespacedRequests,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the form requests for the given model and classes.
|
||||
*
|
||||
* @param string $modelName
|
||||
* @param string $storeRequestClass
|
||||
* @param string $updateRequestClass
|
||||
* @return array
|
||||
*/
|
||||
protected function generateFormRequests($modelClass, $storeRequestClass, $updateRequestClass)
|
||||
{
|
||||
$storeRequestClass = 'Store'.class_basename($modelClass).'Request';
|
||||
|
||||
$this->call('make:request', [
|
||||
'name' => $storeRequestClass,
|
||||
]);
|
||||
|
||||
$updateRequestClass = 'Update'.class_basename($modelClass).'Request';
|
||||
|
||||
$this->call('make:request', [
|
||||
'name' => $updateRequestClass,
|
||||
]);
|
||||
|
||||
return [$storeRequestClass, $updateRequestClass];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,11 +268,13 @@ class ControllerMakeCommand extends GeneratorCommand
|
||||
{
|
||||
return [
|
||||
['api', null, InputOption::VALUE_NONE, 'Exclude the create and edit methods from the controller.'],
|
||||
['type', null, InputOption::VALUE_REQUIRED, 'Manually specify the controller stub file to use.'],
|
||||
['force', null, InputOption::VALUE_NONE, 'Create the class even if the controller already exists'],
|
||||
['invokable', 'i', InputOption::VALUE_NONE, 'Generate a single method, invokable controller class.'],
|
||||
['model', 'm', InputOption::VALUE_OPTIONAL, 'Generate a resource controller for the given model.'],
|
||||
['parent', 'p', InputOption::VALUE_OPTIONAL, 'Generate a nested resource controller class.'],
|
||||
['resource', 'r', InputOption::VALUE_NONE, 'Generate a resource controller class.'],
|
||||
['requests', 'R', InputOption::VALUE_NONE, 'Generate FormRequest classes for store and update.'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
namespace Illuminate\Routing\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
|
||||
class MiddlewareMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace {{ namespace }};
|
||||
|
||||
use {{ namespacedModel }};
|
||||
use {{ rootNamespace }}Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use {{ namespacedRequests }}
|
||||
|
||||
class {{ class }} extends Controller
|
||||
{
|
||||
@@ -21,10 +21,10 @@ class {{ class }} extends Controller
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \{{ namespacedStoreRequest }} $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store({{ storeRequest }} $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -43,11 +43,11 @@ class {{ class }} extends Controller
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \{{ namespacedUpdateRequest }} $request
|
||||
* @param \{{ namespacedModel }} ${{ modelVariable }}
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, {{ model }} ${{ modelVariable }})
|
||||
public function update({{ updateRequest }} $request, {{ model }} ${{ modelVariable }})
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace {{ namespace }};
|
||||
|
||||
use {{ namespacedModel }};
|
||||
use {{ rootNamespace }}Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use {{ namespacedRequests }}
|
||||
|
||||
class {{ class }} extends Controller
|
||||
{
|
||||
@@ -31,10 +31,10 @@ class {{ class }} extends Controller
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \{{ namespacedStoreRequest }} $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store({{ storeRequest }} $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -64,11 +64,11 @@ class {{ class }} extends Controller
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \{{ namespacedUpdateRequest }} $request
|
||||
* @param \{{ namespacedModel }} ${{ modelVariable }}
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, {{ model }} ${{ modelVariable }})
|
||||
public function update({{ updateRequest }} $request, {{ model }} ${{ modelVariable }})
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class {{ class }}
|
||||
{
|
||||
@@ -10,10 +11,10 @@ class {{ class }}
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace Illuminate\Routing\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class UrlGenerationException extends Exception
|
||||
{
|
||||
@@ -10,10 +12,26 @@ class UrlGenerationException extends Exception
|
||||
* Create a new exception for missing route parameters.
|
||||
*
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @param array $parameters
|
||||
* @return static
|
||||
*/
|
||||
public static function forMissingParameters($route)
|
||||
public static function forMissingParameters(Route $route, array $parameters = [])
|
||||
{
|
||||
return new static("Missing required parameters for [Route: {$route->getName()}] [URI: {$route->uri()}].");
|
||||
$parameterLabel = Str::plural('parameter', count($parameters));
|
||||
|
||||
$message = sprintf(
|
||||
'Missing required %s for [Route: %s] [URI: %s]',
|
||||
$parameterLabel,
|
||||
$route->getName(),
|
||||
$route->uri()
|
||||
);
|
||||
|
||||
if (count($parameters) > 0) {
|
||||
$message .= sprintf(' [Missing %s: %s]', $parameterLabel, implode(', ', $parameters));
|
||||
}
|
||||
|
||||
$message .= '.';
|
||||
|
||||
return new static($message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Illuminate\Routing;
|
||||
|
||||
use Illuminate\Contracts\Routing\UrlRoutable;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Reflector;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@@ -37,13 +38,21 @@ class ImplicitRouteBinding
|
||||
|
||||
$parent = $route->parentOfParameter($parameterName);
|
||||
|
||||
if ($parent instanceof UrlRoutable && in_array($parameterName, array_keys($route->bindingFields()))) {
|
||||
if (! $model = $parent->resolveChildRouteBinding(
|
||||
$routeBindingMethod = $route->allowsTrashedBindings() && in_array(SoftDeletes::class, class_uses_recursive($instance))
|
||||
? 'resolveSoftDeletableRouteBinding'
|
||||
: 'resolveRouteBinding';
|
||||
|
||||
if ($parent instanceof UrlRoutable && ($route->enforcesScopedBindings() || array_key_exists($parameterName, $route->bindingFields()))) {
|
||||
$childRouteBindingMethod = $route->allowsTrashedBindings() && in_array(SoftDeletes::class, class_uses_recursive($instance))
|
||||
? 'resolveSoftDeletableChildRouteBinding'
|
||||
: 'resolveChildRouteBinding';
|
||||
|
||||
if (! $model = $parent->{$childRouteBindingMethod}(
|
||||
$parameterName, $parameterValue, $route->bindingFieldFor($parameterName)
|
||||
)) {
|
||||
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
|
||||
}
|
||||
} elseif (! $model = $instance->resolveRouteBinding($parameterValue, $route->bindingFieldFor($parameterName))) {
|
||||
} elseif (! $model = $instance->{$routeBindingMethod}($parameterValue, $route->bindingFieldFor($parameterName))) {
|
||||
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Illuminate\Routing\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Routing\Registrar;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class SubstituteBindings
|
||||
{
|
||||
@@ -34,9 +35,17 @@ class SubstituteBindings
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$this->router->substituteBindings($route = $request->route());
|
||||
try {
|
||||
$this->router->substituteBindings($route = $request->route());
|
||||
|
||||
$this->router->substituteImplicitBindings($route);
|
||||
$this->router->substituteImplicitBindings($route);
|
||||
} catch (ModelNotFoundException $exception) {
|
||||
if ($route->getMissing()) {
|
||||
return $route->getMissing()($request, $exception);
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ namespace Illuminate\Routing\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Cache\RateLimiter;
|
||||
use Illuminate\Cache\RateLimiting\Unlimited;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Http\Exceptions\ThrottleRequestsException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\InteractsWithTime;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
@@ -46,22 +49,92 @@ class ThrottleRequests
|
||||
*/
|
||||
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1, $prefix = '')
|
||||
{
|
||||
$key = $prefix.$this->resolveRequestSignature($request);
|
||||
|
||||
$maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
|
||||
|
||||
if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
|
||||
throw $this->buildException($key, $maxAttempts);
|
||||
if (is_string($maxAttempts)
|
||||
&& func_num_args() === 3
|
||||
&& ! is_null($limiter = $this->limiter->limiter($maxAttempts))) {
|
||||
return $this->handleRequestUsingNamedLimiter($request, $next, $maxAttempts, $limiter);
|
||||
}
|
||||
|
||||
$this->limiter->hit($key, $decayMinutes * 60);
|
||||
return $this->handleRequest(
|
||||
$request,
|
||||
$next,
|
||||
[
|
||||
(object) [
|
||||
'key' => $prefix.$this->resolveRequestSignature($request),
|
||||
'maxAttempts' => $this->resolveMaxAttempts($request, $maxAttempts),
|
||||
'decayMinutes' => $decayMinutes,
|
||||
'responseCallback' => null,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string $limiterName
|
||||
* @param \Closure $limiter
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
protected function handleRequestUsingNamedLimiter($request, Closure $next, $limiterName, Closure $limiter)
|
||||
{
|
||||
$limiterResponse = call_user_func($limiter, $request);
|
||||
|
||||
if ($limiterResponse instanceof Response) {
|
||||
return $limiterResponse;
|
||||
} elseif ($limiterResponse instanceof Unlimited) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return $this->handleRequest(
|
||||
$request,
|
||||
$next,
|
||||
collect(Arr::wrap($limiterResponse))->map(function ($limit) use ($limiterName) {
|
||||
return (object) [
|
||||
'key' => md5($limiterName.$limit->key),
|
||||
'maxAttempts' => $limit->maxAttempts,
|
||||
'decayMinutes' => $limit->decayMinutes,
|
||||
'responseCallback' => $limit->responseCallback,
|
||||
];
|
||||
})->all()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param array $limits
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
protected function handleRequest($request, Closure $next, array $limits)
|
||||
{
|
||||
foreach ($limits as $limit) {
|
||||
if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
|
||||
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
||||
}
|
||||
|
||||
$this->limiter->hit($limit->key, $limit->decayMinutes * 60);
|
||||
}
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
return $this->addHeaders(
|
||||
$response, $maxAttempts,
|
||||
$this->calculateRemainingAttempts($key, $maxAttempts)
|
||||
);
|
||||
foreach ($limits as $limit) {
|
||||
$response = $this->addHeaders(
|
||||
$response,
|
||||
$limit->maxAttempts,
|
||||
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,9 +169,7 @@ class ThrottleRequests
|
||||
{
|
||||
if ($user = $request->user()) {
|
||||
return sha1($user->getAuthIdentifier());
|
||||
}
|
||||
|
||||
if ($route = $request->route()) {
|
||||
} elseif ($route = $request->route()) {
|
||||
return sha1($route->getDomain().'|'.$request->ip());
|
||||
}
|
||||
|
||||
@@ -108,11 +179,13 @@ class ThrottleRequests
|
||||
/**
|
||||
* Create a 'too many attempts' exception.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $key
|
||||
* @param int $maxAttempts
|
||||
* @param callable|null $responseCallback
|
||||
* @return \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
protected function buildException($key, $maxAttempts)
|
||||
protected function buildException($request, $key, $maxAttempts, $responseCallback = null)
|
||||
{
|
||||
$retryAfter = $this->getTimeUntilNextRetry($key);
|
||||
|
||||
@@ -122,9 +195,9 @@ class ThrottleRequests
|
||||
$retryAfter
|
||||
);
|
||||
|
||||
return new ThrottleRequestsException(
|
||||
'Too Many Attempts.', null, $headers
|
||||
);
|
||||
return is_callable($responseCallback)
|
||||
? new HttpResponseException($responseCallback($request, $headers))
|
||||
: new ThrottleRequestsException('Too Many Attempts.', null, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +223,7 @@ class ThrottleRequests
|
||||
protected function addHeaders(Response $response, $maxAttempts, $remainingAttempts, $retryAfter = null)
|
||||
{
|
||||
$response->headers->add(
|
||||
$this->getHeaders($maxAttempts, $remainingAttempts, $retryAfter)
|
||||
$this->getHeaders($maxAttempts, $remainingAttempts, $retryAfter, $response)
|
||||
);
|
||||
|
||||
return $response;
|
||||
@@ -162,10 +235,20 @@ class ThrottleRequests
|
||||
* @param int $maxAttempts
|
||||
* @param int $remainingAttempts
|
||||
* @param int|null $retryAfter
|
||||
* @param \Symfony\Component\HttpFoundation\Response|null $response
|
||||
* @return array
|
||||
*/
|
||||
protected function getHeaders($maxAttempts, $remainingAttempts, $retryAfter = null)
|
||||
protected function getHeaders($maxAttempts,
|
||||
$remainingAttempts,
|
||||
$retryAfter = null,
|
||||
?Response $response = null)
|
||||
{
|
||||
if ($response &&
|
||||
! is_null($response->headers->get('X-RateLimit-Remaining')) &&
|
||||
(int) $response->headers->get('X-RateLimit-Remaining') <= (int) $remainingAttempts) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'X-RateLimit-Limit' => $maxAttempts,
|
||||
'X-RateLimit-Remaining' => $remainingAttempts,
|
||||
@@ -189,10 +272,6 @@ class ThrottleRequests
|
||||
*/
|
||||
protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
|
||||
{
|
||||
if (is_null($retryAfter)) {
|
||||
return $this->limiter->retriesLeft($key, $maxAttempts);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return is_null($retryAfter) ? $this->limiter->retriesLeft($key, $maxAttempts) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Illuminate\Routing\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Cache\RateLimiter;
|
||||
use Illuminate\Contracts\Redis\Factory as Redis;
|
||||
use Illuminate\Redis\Limiters\DurationLimiter;
|
||||
|
||||
@@ -16,27 +17,30 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* The timestamp of the end of the current duration.
|
||||
* The timestamp of the end of the current duration by key.
|
||||
*
|
||||
* @var int
|
||||
* @var array
|
||||
*/
|
||||
public $decaysAt;
|
||||
public $decaysAt = [];
|
||||
|
||||
/**
|
||||
* The number of remaining slots.
|
||||
* The number of remaining slots by key.
|
||||
*
|
||||
* @var int
|
||||
* @var array
|
||||
*/
|
||||
public $remaining;
|
||||
public $remaining = [];
|
||||
|
||||
/**
|
||||
* Create a new request throttler.
|
||||
*
|
||||
* @param \Illuminate\Cache\RateLimiter $limiter
|
||||
* @param \Illuminate\Contracts\Redis\Factory $redis
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Redis $redis)
|
||||
public function __construct(RateLimiter $limiter, Redis $redis)
|
||||
{
|
||||
parent::__construct($limiter);
|
||||
|
||||
$this->redis = $redis;
|
||||
}
|
||||
|
||||
@@ -45,29 +49,30 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param int|string $maxAttempts
|
||||
* @param float|int $decayMinutes
|
||||
* @param string $prefix
|
||||
* @return mixed
|
||||
* @param array $limits
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
|
||||
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1, $prefix = '')
|
||||
protected function handleRequest($request, Closure $next, array $limits)
|
||||
{
|
||||
$key = $prefix.$this->resolveRequestSignature($request);
|
||||
|
||||
$maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
|
||||
|
||||
if ($this->tooManyAttempts($key, $maxAttempts, $decayMinutes)) {
|
||||
throw $this->buildException($key, $maxAttempts);
|
||||
foreach ($limits as $limit) {
|
||||
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decayMinutes)) {
|
||||
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
||||
}
|
||||
}
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
return $this->addHeaders(
|
||||
$response, $maxAttempts,
|
||||
$this->calculateRemainingAttempts($key, $maxAttempts)
|
||||
);
|
||||
foreach ($limits as $limit) {
|
||||
$response = $this->addHeaders(
|
||||
$response,
|
||||
$limit->maxAttempts,
|
||||
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,8 +89,8 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
$this->redis, $key, $maxAttempts, $decayMinutes * 60
|
||||
);
|
||||
|
||||
return tap(! $limiter->acquire(), function () use ($limiter) {
|
||||
[$this->decaysAt, $this->remaining] = [
|
||||
return tap(! $limiter->acquire(), function () use ($key, $limiter) {
|
||||
[$this->decaysAt[$key], $this->remaining[$key]] = [
|
||||
$limiter->decaysAt, $limiter->remaining,
|
||||
];
|
||||
});
|
||||
@@ -101,11 +106,7 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
*/
|
||||
protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
|
||||
{
|
||||
if (is_null($retryAfter)) {
|
||||
return $this->remaining;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return is_null($retryAfter) ? $this->remaining[$key] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,6 +117,6 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
*/
|
||||
protected function getTimeUntilNextRetry($key)
|
||||
{
|
||||
return $this->decaysAt - $this->currentTime();
|
||||
return $this->decaysAt[$key] - $this->currentTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@ class ValidateSignature
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $relative
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Illuminate\Routing\Exceptions\InvalidSignatureException
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
public function handle($request, Closure $next, $relative = null)
|
||||
{
|
||||
if ($request->hasValidSignature()) {
|
||||
if ($request->hasValidSignature($relative !== 'relative')) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use Illuminate\Support\Traits\Macroable;
|
||||
|
||||
class PendingResourceRegistration
|
||||
{
|
||||
use Macroable;
|
||||
use CreatesRegularExpressionRouteConstraints, Macroable;
|
||||
|
||||
/**
|
||||
* The resource registrar.
|
||||
@@ -149,6 +149,12 @@ class PendingResourceRegistration
|
||||
*/
|
||||
public function middleware($middleware)
|
||||
{
|
||||
$middleware = Arr::wrap($middleware);
|
||||
|
||||
foreach ($middleware as $key => $value) {
|
||||
$middleware[$key] = (string) $value;
|
||||
}
|
||||
|
||||
$this->options['middleware'] = $middleware;
|
||||
|
||||
return $this;
|
||||
@@ -195,6 +201,19 @@ class PendingResourceRegistration
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the callable that should be invoked on a missing model exception.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function missing($callback)
|
||||
{
|
||||
$this->options['missing'] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the resource routes should be scoped using the given binding fields.
|
||||
*
|
||||
|
||||
0
vendor/laravel/framework/src/Illuminate/Routing/Redirector.php
vendored
Normal file → Executable file
0
vendor/laravel/framework/src/Illuminate/Routing/Redirector.php
vendored
Normal file → Executable file
@@ -16,7 +16,7 @@ class ResourceRegistrar
|
||||
/**
|
||||
* The default actions for a resourceful controller.
|
||||
*
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
protected $resourceDefaults = ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy'];
|
||||
|
||||
@@ -184,6 +184,8 @@ class ResourceRegistrar
|
||||
{
|
||||
$uri = $this->getResourceUri($name);
|
||||
|
||||
unset($options['missing']);
|
||||
|
||||
$action = $this->getResourceAction($name, $controller, 'index', $options);
|
||||
|
||||
return $this->router->get($uri, $action);
|
||||
@@ -202,6 +204,8 @@ class ResourceRegistrar
|
||||
{
|
||||
$uri = $this->getResourceUri($name).'/'.static::$verbs['create'];
|
||||
|
||||
unset($options['missing']);
|
||||
|
||||
$action = $this->getResourceAction($name, $controller, 'create', $options);
|
||||
|
||||
return $this->router->get($uri, $action);
|
||||
@@ -220,6 +224,8 @@ class ResourceRegistrar
|
||||
{
|
||||
$uri = $this->getResourceUri($name);
|
||||
|
||||
unset($options['missing']);
|
||||
|
||||
$action = $this->getResourceAction($name, $controller, 'store', $options);
|
||||
|
||||
return $this->router->post($uri, $action);
|
||||
@@ -421,6 +427,10 @@ class ResourceRegistrar
|
||||
$action['where'] = $options['wheres'];
|
||||
}
|
||||
|
||||
if (isset($options['missing'])) {
|
||||
$action['missing'] = $options['missing'];
|
||||
}
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class ResponseFactory implements FactoryContract
|
||||
/**
|
||||
* Create a new response instance.
|
||||
*
|
||||
* @param string $content
|
||||
* @param mixed $content
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @return \Illuminate\Http\Response
|
||||
|
||||
156
vendor/laravel/framework/src/Illuminate/Routing/Route.php
vendored
Normal file → Executable file
156
vendor/laravel/framework/src/Illuminate/Routing/Route.php
vendored
Normal file → Executable file
@@ -14,13 +14,15 @@ use Illuminate\Routing\Matching\UriValidator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use Laravel\SerializableClosure\SerializableClosure;
|
||||
use LogicException;
|
||||
use Opis\Closure\SerializableClosure as OpisSerializableClosure;
|
||||
use ReflectionFunction;
|
||||
use Symfony\Component\Routing\Route as SymfonyRoute;
|
||||
|
||||
class Route
|
||||
{
|
||||
use Macroable, RouteDependencyResolverTrait;
|
||||
use CreatesRegularExpressionRouteConstraints, Macroable, RouteDependencyResolverTrait;
|
||||
|
||||
/**
|
||||
* The URI pattern the route responds to.
|
||||
@@ -92,6 +94,13 @@ class Route
|
||||
*/
|
||||
protected $originalParameters;
|
||||
|
||||
/**
|
||||
* Indicates "trashed" models can be retrieved when resolving implicit model bindings for this route.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $withTrashedBindings = false;
|
||||
|
||||
/**
|
||||
* Indicates the maximum number of seconds the route should acquire a session lock for.
|
||||
*
|
||||
@@ -209,7 +218,7 @@ class Route
|
||||
*/
|
||||
protected function isControllerAction()
|
||||
{
|
||||
return is_string($this->action['uses']);
|
||||
return is_string($this->action['uses']) && ! $this->isSerializedClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,11 +230,25 @@ class Route
|
||||
{
|
||||
$callable = $this->action['uses'];
|
||||
|
||||
if ($this->isSerializedClosure()) {
|
||||
$callable = unserialize($this->action['uses'])->getClosure();
|
||||
}
|
||||
|
||||
return $callable(...array_values($this->resolveMethodDependencies(
|
||||
$this->parametersWithoutNulls(), new ReflectionFunction($this->action['uses'])
|
||||
$this->parametersWithoutNulls(), new ReflectionFunction($callable)
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the route action is a serialized Closure.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSerializedClosure()
|
||||
{
|
||||
return RouteAction::containsSerializedClosure($this->action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the route action and return the response.
|
||||
*
|
||||
@@ -276,6 +299,17 @@ class Route
|
||||
return Str::parseCallback($this->action['uses']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the cached container instance on the route.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function flushController()
|
||||
{
|
||||
$this->computedMiddleware = null;
|
||||
$this->controller = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the route matches a given request.
|
||||
*
|
||||
@@ -287,7 +321,7 @@ class Route
|
||||
{
|
||||
$this->compileRoute();
|
||||
|
||||
foreach ($this->getValidators() as $validator) {
|
||||
foreach (self::getValidators() as $validator) {
|
||||
if (! $includingMethod && $validator instanceof MethodValidator) {
|
||||
continue;
|
||||
}
|
||||
@@ -544,6 +578,29 @@ class Route
|
||||
return array_values($this->parameters)[$key - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow "trashed" models to be retrieved when resolving implicit model bindings for this route.
|
||||
*
|
||||
* @param bool $withTrashed
|
||||
* @return $this
|
||||
*/
|
||||
public function withTrashed($withTrashed = true)
|
||||
{
|
||||
$this->withTrashedBindings = $withTrashed;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the route allows "trashed" models to be retrieved when resolving implicit model bindings.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function allowsTrashedBindings()
|
||||
{
|
||||
return $this->withTrashedBindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a default value for the route.
|
||||
*
|
||||
@@ -731,6 +788,8 @@ class Route
|
||||
*/
|
||||
public function prefix($prefix)
|
||||
{
|
||||
$prefix = $prefix ?? '';
|
||||
|
||||
$this->updatePrefixOnAction($prefix);
|
||||
|
||||
$uri = rtrim($prefix, '/').'/'.ltrim($this->uri, '/');
|
||||
@@ -836,11 +895,15 @@ class Route
|
||||
/**
|
||||
* Set the handler for the route.
|
||||
*
|
||||
* @param \Closure|string $action
|
||||
* @param \Closure|array|string $action
|
||||
* @return $this
|
||||
*/
|
||||
public function uses($action)
|
||||
{
|
||||
if (is_array($action)) {
|
||||
$action = $action[0].'@'.$action[1];
|
||||
}
|
||||
|
||||
$action = is_string($action) ? $this->addGroupNamespaceToStringUses($action) : $action;
|
||||
|
||||
return $this->setAction(array_merge($this->action, $this->parseAction([
|
||||
@@ -914,6 +977,35 @@ class Route
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the action that should be taken on a missing model exception.
|
||||
*
|
||||
* @return \Closure|null
|
||||
*/
|
||||
public function getMissing()
|
||||
{
|
||||
$missing = $this->action['missing'] ?? null;
|
||||
|
||||
return is_string($missing) &&
|
||||
Str::startsWith($missing, [
|
||||
'C:32:"Opis\\Closure\\SerializableClosure',
|
||||
'O:47:"Laravel\\SerializableClosure\\SerializableClosure',
|
||||
]) ? unserialize($missing) : $missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the callable that should be invoked on a missing model exception.
|
||||
*
|
||||
* @param \Closure $missing
|
||||
* @return $this
|
||||
*/
|
||||
public function missing($missing)
|
||||
{
|
||||
$this->action['missing'] = $missing;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all middleware, including the ones from the controller.
|
||||
*
|
||||
@@ -944,10 +1036,14 @@ class Route
|
||||
return (array) ($this->action['middleware'] ?? []);
|
||||
}
|
||||
|
||||
if (is_string($middleware)) {
|
||||
if (! is_array($middleware)) {
|
||||
$middleware = func_get_args();
|
||||
}
|
||||
|
||||
foreach ($middleware as $index => $value) {
|
||||
$middleware[$index] = (string) $value;
|
||||
}
|
||||
|
||||
$this->action['middleware'] = array_merge(
|
||||
(array) ($this->action['middleware'] ?? []), $middleware
|
||||
);
|
||||
@@ -955,6 +1051,20 @@ class Route
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that the "Authorize" / "can" middleware should be applied to the route with the given options.
|
||||
*
|
||||
* @param string $ability
|
||||
* @param array|string $models
|
||||
* @return $this
|
||||
*/
|
||||
public function can($ability, $models = [])
|
||||
{
|
||||
return empty($models)
|
||||
? $this->middleware(['can:'.$ability])
|
||||
: $this->middleware(['can:'.$ability.','.implode(',', Arr::wrap($models))]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the middleware for the route's controller.
|
||||
*
|
||||
@@ -996,6 +1106,28 @@ class Route
|
||||
return (array) ($this->action['excluded_middleware'] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the route should enforce scoping of multiple implicit Eloquent bindings.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function scopeBindings()
|
||||
{
|
||||
$this->action['scope_bindings'] = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the route should enforce scoping of multiple implicit Eloquent bindings.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function enforcesScopedBindings()
|
||||
{
|
||||
return (bool) ($this->action['scope_bindings'] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that the route should not allow concurrent requests from the same session.
|
||||
*
|
||||
@@ -1147,7 +1279,17 @@ class Route
|
||||
public function prepareForSerialization()
|
||||
{
|
||||
if ($this->action['uses'] instanceof Closure) {
|
||||
throw new LogicException("Unable to prepare route [{$this->uri}] for serialization. Uses Closure.");
|
||||
$this->action['uses'] = serialize(\PHP_VERSION_ID < 70400
|
||||
? new OpisSerializableClosure($this->action['uses'])
|
||||
: new SerializableClosure($this->action['uses'])
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($this->action['missing']) && $this->action['missing'] instanceof Closure) {
|
||||
$this->action['missing'] = serialize(\PHP_VERSION_ID < 70400
|
||||
? new OpisSerializableClosure($this->action['missing'])
|
||||
: new SerializableClosure($this->action['missing'])
|
||||
);
|
||||
}
|
||||
|
||||
$this->compileRoute();
|
||||
|
||||
@@ -43,7 +43,7 @@ class RouteAction
|
||||
$action['uses'] = static::findCallable($action);
|
||||
}
|
||||
|
||||
if (is_string($action['uses']) && ! Str::contains($action['uses'], '@')) {
|
||||
if (! static::containsSerializedClosure($action) && is_string($action['uses']) && ! Str::contains($action['uses'], '@')) {
|
||||
$action['uses'] = static::makeInvokable($action['uses']);
|
||||
}
|
||||
|
||||
@@ -94,4 +94,18 @@ class RouteAction
|
||||
|
||||
return $action.'@__invoke';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given array actions contain a serialized Closure.
|
||||
*
|
||||
* @param array $action
|
||||
* @return bool
|
||||
*/
|
||||
public static function containsSerializedClosure(array $action)
|
||||
{
|
||||
return is_string($action['uses']) && Str::startsWith($action['uses'], [
|
||||
'C:32:"Opis\\Closure\\SerializableClosure',
|
||||
'O:47:"Laravel\\SerializableClosure\\SerializableClosure',
|
||||
]) !== false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ class RouteGroup
|
||||
unset($old['domain']);
|
||||
}
|
||||
|
||||
if (isset($new['controller'])) {
|
||||
unset($old['controller']);
|
||||
}
|
||||
|
||||
$new = array_merge(static::formatAs($new, $old), [
|
||||
'namespace' => static::formatNamespace($new, $old),
|
||||
'prefix' => static::formatPrefix($new, $old, $prependExistingPrefix),
|
||||
@@ -59,7 +63,7 @@ class RouteGroup
|
||||
*/
|
||||
protected static function formatPrefix($new, $old, $prependExistingPrefix = true)
|
||||
{
|
||||
$old = $old['prefix'] ?? null;
|
||||
$old = $old['prefix'] ?? '';
|
||||
|
||||
if ($prependExistingPrefix) {
|
||||
return isset($new['prefix']) ? trim($old, '/').'/'.trim($new['prefix'], '/') : $old;
|
||||
|
||||
@@ -17,12 +17,15 @@ use InvalidArgumentException;
|
||||
* @method \Illuminate\Routing\Route options(string $uri, \Closure|array|string|null $action = null)
|
||||
* @method \Illuminate\Routing\Route any(string $uri, \Closure|array|string|null $action = null)
|
||||
* @method \Illuminate\Routing\RouteRegistrar as(string $value)
|
||||
* @method \Illuminate\Routing\RouteRegistrar controller(string $controller)
|
||||
* @method \Illuminate\Routing\RouteRegistrar domain(string $value)
|
||||
* @method \Illuminate\Routing\RouteRegistrar middleware(array|string|null $middleware)
|
||||
* @method \Illuminate\Routing\RouteRegistrar name(string $value)
|
||||
* @method \Illuminate\Routing\RouteRegistrar namespace(string $value)
|
||||
* @method \Illuminate\Routing\RouteRegistrar namespace(string|null $value)
|
||||
* @method \Illuminate\Routing\RouteRegistrar prefix(string $prefix)
|
||||
* @method \Illuminate\Routing\RouteRegistrar scopeBindings()
|
||||
* @method \Illuminate\Routing\RouteRegistrar where(array $where)
|
||||
* @method \Illuminate\Routing\RouteRegistrar withoutMiddleware(array|string $middleware)
|
||||
*/
|
||||
class RouteRegistrar
|
||||
{
|
||||
@@ -43,7 +46,7 @@ class RouteRegistrar
|
||||
/**
|
||||
* The methods to dynamically pass through to the router.
|
||||
*
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
protected $passthru = [
|
||||
'get', 'post', 'put', 'patch', 'delete', 'options', 'any',
|
||||
@@ -52,10 +55,19 @@ class RouteRegistrar
|
||||
/**
|
||||
* The attributes that can be set through this class.
|
||||
*
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
protected $allowedAttributes = [
|
||||
'as', 'domain', 'middleware', 'name', 'namespace', 'prefix', 'where',
|
||||
'as',
|
||||
'controller',
|
||||
'domain',
|
||||
'middleware',
|
||||
'name',
|
||||
'namespace',
|
||||
'prefix',
|
||||
'scopeBindings',
|
||||
'where',
|
||||
'withoutMiddleware',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -65,6 +77,8 @@ class RouteRegistrar
|
||||
*/
|
||||
protected $aliases = [
|
||||
'name' => 'as',
|
||||
'scopeBindings' => 'scope_bindings',
|
||||
'withoutMiddleware' => 'excluded_middleware',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -93,7 +107,21 @@ class RouteRegistrar
|
||||
throw new InvalidArgumentException("Attribute [{$key}] does not exist.");
|
||||
}
|
||||
|
||||
$this->attributes[Arr::get($this->aliases, $key, $key)] = $value;
|
||||
if ($key === 'middleware') {
|
||||
foreach ($value as $index => $middleware) {
|
||||
$value[$index] = (string) $middleware;
|
||||
}
|
||||
}
|
||||
|
||||
$attributeKey = Arr::get($this->aliases, $key, $key);
|
||||
|
||||
if ($key === 'withoutMiddleware') {
|
||||
$value = array_merge(
|
||||
(array) ($this->attributes[$attributeKey] ?? []), Arr::wrap($value)
|
||||
);
|
||||
}
|
||||
|
||||
$this->attributes[$attributeKey] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -184,6 +212,9 @@ class RouteRegistrar
|
||||
if (is_array($action) &&
|
||||
! Arr::isAssoc($action) &&
|
||||
Reflector::isCallable($action)) {
|
||||
if (strncmp($action[0], '\\', 1)) {
|
||||
$action[0] = '\\'.$action[0];
|
||||
}
|
||||
$action = [
|
||||
'uses' => $action[0].'@'.$action[1],
|
||||
'controller' => $action[0].'@'.$action[1],
|
||||
@@ -213,7 +244,7 @@ class RouteRegistrar
|
||||
return $this->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
|
||||
}
|
||||
|
||||
return $this->attribute($method, $parameters[0]);
|
||||
return $this->attribute($method, array_key_exists(0, $parameters) ? $parameters[0] : true);
|
||||
}
|
||||
|
||||
throw new BadMethodCallException(sprintf(
|
||||
|
||||
@@ -18,9 +18,13 @@ class RouteSignatureParameters
|
||||
*/
|
||||
public static function fromAction(array $action, $subClass = null)
|
||||
{
|
||||
$parameters = is_string($action['uses'])
|
||||
? static::fromClassMethodString($action['uses'])
|
||||
: (new ReflectionFunction($action['uses']))->getParameters();
|
||||
$callback = RouteAction::containsSerializedClosure($action)
|
||||
? unserialize($action['uses'])->getClosure()
|
||||
: $action['uses'];
|
||||
|
||||
$parameters = is_string($callback)
|
||||
? static::fromClassMethodString($callback)
|
||||
: (new ReflectionFunction($callback))->getParameters();
|
||||
|
||||
return is_null($subClass) ? $parameters : array_filter($parameters, function ($p) use ($subClass) {
|
||||
return Reflector::isParameterSubclassOf($p, $subClass);
|
||||
|
||||
@@ -87,8 +87,8 @@ class RouteUrlGenerator
|
||||
$route
|
||||
), $parameters);
|
||||
|
||||
if (preg_match('/\{.*?\}/', $uri)) {
|
||||
throw UrlGenerationException::forMissingParameters($route);
|
||||
if (preg_match_all('/{(.*?)}/', $uri, $matchedMissingParameters)) {
|
||||
throw UrlGenerationException::forMissingParameters($route, $matchedMissingParameters[1]);
|
||||
}
|
||||
|
||||
// Once we have ensured that there are no missing parameters in the URI we will encode
|
||||
|
||||
@@ -18,9 +18,11 @@ use Illuminate\Http\Response;
|
||||
use Illuminate\Routing\Events\RouteMatched;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Stringable;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use JsonSerializable;
|
||||
use Psr\Http\Message\ResponseInterface as PsrResponseInterface;
|
||||
use ReflectionClass;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
|
||||
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
|
||||
|
||||
@@ -115,7 +117,7 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
/**
|
||||
* All of the verbs supported by the router.
|
||||
*
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
public static $verbs = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
|
||||
|
||||
@@ -265,13 +267,19 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
* @param string $uri
|
||||
* @param string $view
|
||||
* @param array $data
|
||||
* @param int|array $status
|
||||
* @param array $headers
|
||||
* @return \Illuminate\Routing\Route
|
||||
*/
|
||||
public function view($uri, $view, $data = [])
|
||||
public function view($uri, $view, $data = [], $status = 200, array $headers = [])
|
||||
{
|
||||
return $this->match(['GET', 'HEAD'], $uri, '\Illuminate\Routing\ViewController')
|
||||
->defaults('view', $view)
|
||||
->defaults('data', $data);
|
||||
->setDefaults([
|
||||
'view' => $view,
|
||||
'data' => $data,
|
||||
'status' => is_array($status) ? 200 : $status,
|
||||
'headers' => is_array($status) ? $status : $headers,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,10 +515,11 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
$action = ['uses' => $action];
|
||||
}
|
||||
|
||||
// Here we'll merge any group "uses" statement if necessary so that the action
|
||||
// has the proper clause for this property. Then we can simply set the name
|
||||
// of the controller on the action and return the action array for usage.
|
||||
// Here we'll merge any group "controller" and "uses" statements if necessary so that
|
||||
// the action has the proper clause for this property. Then, we can simply set the
|
||||
// name of this controller on the action plus return the action array for usage.
|
||||
if ($this->hasGroupStack()) {
|
||||
$action['uses'] = $this->prependGroupController($action['uses']);
|
||||
$action['uses'] = $this->prependGroupNamespace($action['uses']);
|
||||
}
|
||||
|
||||
@@ -536,6 +545,31 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
? $group['namespace'].'\\'.$class : $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend the last group controller onto the use clause.
|
||||
*
|
||||
* @param string $class
|
||||
* @return string
|
||||
*/
|
||||
protected function prependGroupController($class)
|
||||
{
|
||||
$group = end($this->groupStack);
|
||||
|
||||
if (! isset($group['controller'])) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
if (class_exists($class)) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
if (strpos($class, '@') !== false) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
return $group['controller'].'@'.$class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Route object.
|
||||
*
|
||||
@@ -638,6 +672,8 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
{
|
||||
$this->current = $route = $this->routes->match($request);
|
||||
|
||||
$route->setContainer($this->container);
|
||||
|
||||
$this->container->instance(Route::class, $route);
|
||||
|
||||
return $route;
|
||||
@@ -695,14 +731,36 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
*/
|
||||
public function gatherRouteMiddleware(Route $route)
|
||||
{
|
||||
$computedMiddleware = $route->gatherMiddleware();
|
||||
|
||||
$excluded = collect($route->excludedMiddleware())->map(function ($name) {
|
||||
return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
|
||||
})->flatten()->values()->all();
|
||||
|
||||
$middleware = collect($route->gatherMiddleware())->map(function ($name) {
|
||||
$middleware = collect($computedMiddleware)->map(function ($name) {
|
||||
return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
|
||||
})->flatten()->reject(function ($name) use ($excluded) {
|
||||
return in_array($name, $excluded, true);
|
||||
if (empty($excluded)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($name instanceof Closure) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_array($name, $excluded, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! class_exists($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$reflection = new ReflectionClass($name);
|
||||
|
||||
return collect($excluded)->contains(function ($exclude) use ($reflection) {
|
||||
return class_exists($exclude) && $reflection->isSubclassOf($exclude);
|
||||
});
|
||||
})->values();
|
||||
|
||||
return $this->sortMiddleware($middleware);
|
||||
@@ -748,11 +806,14 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
$response = (new HttpFoundationFactory)->createResponse($response);
|
||||
} elseif ($response instanceof Model && $response->wasRecentlyCreated) {
|
||||
$response = new JsonResponse($response, 201);
|
||||
} elseif ($response instanceof Stringable) {
|
||||
$response = new Response($response->__toString(), 200, ['Content-Type' => 'text/html']);
|
||||
} elseif (! $response instanceof SymfonyResponse &&
|
||||
($response instanceof Arrayable ||
|
||||
$response instanceof Jsonable ||
|
||||
$response instanceof ArrayObject ||
|
||||
$response instanceof JsonSerializable ||
|
||||
$response instanceof \stdClass ||
|
||||
is_array($response))) {
|
||||
$response = new JsonResponse($response);
|
||||
} elseif (! $response instanceof SymfonyResponse) {
|
||||
@@ -923,6 +984,18 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the router's middleware groups.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function flushMiddlewareGroups()
|
||||
{
|
||||
$this->middlewareGroups = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new route parameter binder.
|
||||
*
|
||||
@@ -1043,7 +1116,7 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
/**
|
||||
* Get the currently dispatched route instance.
|
||||
*
|
||||
* @return \Illuminate\Routing\Route
|
||||
* @return \Illuminate\Routing\Route|null
|
||||
*/
|
||||
public function getCurrentRoute()
|
||||
{
|
||||
@@ -1249,6 +1322,19 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the container instance used by the router.
|
||||
*
|
||||
* @param \Illuminate\Container\Container $container
|
||||
* @return $this
|
||||
*/
|
||||
public function setContainer(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically handle calls into the router instance.
|
||||
*
|
||||
@@ -1266,6 +1352,6 @@ class Router implements BindingRegistrar, RegistrarContract
|
||||
return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
|
||||
}
|
||||
|
||||
return (new RouteRegistrar($this))->attribute($method, $parameters[0]);
|
||||
return (new RouteRegistrar($this))->attribute($method, array_key_exists(0, $parameters) ? $parameters[0] : true);
|
||||
}
|
||||
}
|
||||
|
||||
4
vendor/laravel/framework/src/Illuminate/Routing/RoutingServiceProvider.php
vendored
Normal file → Executable file
4
vendor/laravel/framework/src/Illuminate/Routing/RoutingServiceProvider.php
vendored
Normal file → Executable file
@@ -126,6 +126,8 @@ class RoutingServiceProvider extends ServiceProvider
|
||||
* Register a binding for the PSR-7 request implementation.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException
|
||||
*/
|
||||
protected function registerPsrRequest()
|
||||
{
|
||||
@@ -145,6 +147,8 @@ class RoutingServiceProvider extends ServiceProvider
|
||||
* Register a binding for the PSR-7 response implementation.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException
|
||||
*/
|
||||
protected function registerPsrResponse()
|
||||
{
|
||||
|
||||
52
vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php
vendored
Normal file → Executable file
52
vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php
vendored
Normal file → Executable file
@@ -239,9 +239,7 @@ class UrlGenerator implements UrlGeneratorContract
|
||||
// Once we get the root URL, we will check to see if it contains an index.php
|
||||
// file in the paths. If it does, we will remove it since it is not needed
|
||||
// for asset paths, but only for routes to endpoints in the application.
|
||||
$root = $this->assetRoot
|
||||
? $this->assetRoot
|
||||
: $this->formatRoot($this->formatScheme($secure));
|
||||
$root = $this->assetRoot ?: $this->formatRoot($this->formatScheme($secure));
|
||||
|
||||
return $this->removeIndex($root).'/'.trim($path, '/');
|
||||
}
|
||||
@@ -320,13 +318,9 @@ class UrlGenerator implements UrlGeneratorContract
|
||||
*/
|
||||
public function signedRoute($name, $parameters = [], $expiration = null, $absolute = true)
|
||||
{
|
||||
$parameters = Arr::wrap($parameters);
|
||||
|
||||
if (array_key_exists('signature', $parameters)) {
|
||||
throw new InvalidArgumentException(
|
||||
'"Signature" is a reserved parameter when generating signed routes. Please rename your route parameter.'
|
||||
);
|
||||
}
|
||||
$this->ensureSignedRouteParametersAreNotReserved(
|
||||
$parameters = Arr::wrap($parameters)
|
||||
);
|
||||
|
||||
if ($expiration) {
|
||||
$parameters = $parameters + ['expires' => $this->availableAt($expiration)];
|
||||
@@ -341,6 +335,27 @@ class UrlGenerator implements UrlGeneratorContract
|
||||
], $absolute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the given signed route parameters are not reserved.
|
||||
*
|
||||
* @param mixed $parameters
|
||||
* @return void
|
||||
*/
|
||||
protected function ensureSignedRouteParametersAreNotReserved($parameters)
|
||||
{
|
||||
if (array_key_exists('signature', $parameters)) {
|
||||
throw new InvalidArgumentException(
|
||||
'"Signature" is a reserved parameter when generating signed routes. Please rename your route parameter.'
|
||||
);
|
||||
}
|
||||
|
||||
if (array_key_exists('expires', $parameters)) {
|
||||
throw new InvalidArgumentException(
|
||||
'"Expires" is a reserved parameter when generating signed routes. Please rename your route parameter.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary signed route URL for a named route.
|
||||
*
|
||||
@@ -368,6 +383,17 @@ class UrlGenerator implements UrlGeneratorContract
|
||||
&& $this->signatureHasNotExpired($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request has a valid signature for a relative URL.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function hasValidRelativeSignature(Request $request)
|
||||
{
|
||||
return $this->hasValidSignature($request, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the signature from the given request matches the URL.
|
||||
*
|
||||
@@ -379,11 +405,9 @@ class UrlGenerator implements UrlGeneratorContract
|
||||
{
|
||||
$url = $absolute ? $request->url() : '/'.$request->path();
|
||||
|
||||
$original = rtrim($url.'?'.Arr::query(
|
||||
Arr::except($request->query(), 'signature')
|
||||
), '?');
|
||||
$queryString = ltrim(preg_replace('/(^|&)signature=[^&]+/', '', $request->server->get('QUERY_STRING')), '&');
|
||||
|
||||
$signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));
|
||||
$signature = hash_hmac('sha256', rtrim($url.'?'.$queryString, '?'), call_user_func($this->keyResolver));
|
||||
|
||||
return hash_equals($signature, (string) $request->query('signature', ''));
|
||||
}
|
||||
|
||||
@@ -2,38 +2,38 @@
|
||||
|
||||
namespace Illuminate\Routing;
|
||||
|
||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
|
||||
class ViewController extends Controller
|
||||
{
|
||||
/**
|
||||
* The view factory implementation.
|
||||
* The response factory implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\View\Factory
|
||||
* @var \Illuminate\Contracts\Routing\ResponseFactory
|
||||
*/
|
||||
protected $view;
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\View\Factory $view
|
||||
* @param \Illuminate\Contracts\Routing\ResponseFactory $response
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(ViewFactory $view)
|
||||
public function __construct(ResponseFactory $response)
|
||||
{
|
||||
$this->view = $view;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the controller method.
|
||||
*
|
||||
* @param array $args
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function __invoke(...$args)
|
||||
{
|
||||
[$view, $data] = array_slice($args, -2);
|
||||
[$view, $data, $status, $headers] = array_slice($args, -4);
|
||||
|
||||
return $this->view->make($view, $data);
|
||||
return $this->response->view($view, $data, $status, $headers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,19 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5|^8.0",
|
||||
"php": "^7.3|^8.0",
|
||||
"ext-json": "*",
|
||||
"illuminate/container": "^7.0",
|
||||
"illuminate/contracts": "^7.0",
|
||||
"illuminate/http": "^7.0",
|
||||
"illuminate/pipeline": "^7.0",
|
||||
"illuminate/session": "^7.0",
|
||||
"illuminate/support": "^7.0",
|
||||
"symfony/http-foundation": "^5.0",
|
||||
"symfony/http-kernel": "^5.0",
|
||||
"symfony/routing": "^5.0"
|
||||
"illuminate/collections": "^8.0",
|
||||
"illuminate/container": "^8.0",
|
||||
"illuminate/contracts": "^8.0",
|
||||
"illuminate/http": "^8.0",
|
||||
"illuminate/macroable": "^8.0",
|
||||
"illuminate/pipeline": "^8.0",
|
||||
"illuminate/session": "^8.0",
|
||||
"illuminate/support": "^8.0",
|
||||
"symfony/http-foundation": "^5.4",
|
||||
"symfony/http-kernel": "^5.4",
|
||||
"symfony/routing": "^5.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -33,11 +35,11 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.x-dev"
|
||||
"dev-master": "8.x-dev"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"illuminate/console": "Required to use the make commands (^7.0).",
|
||||
"illuminate/console": "Required to use the make commands (^8.0).",
|
||||
"nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
|
||||
"symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)."
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user