Skip to content

Commit

Permalink
Extract signature parameter extraction.
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorotwell committed Dec 21, 2016
1 parent c906ed9 commit 0f7985c
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ class UrlGenerationException extends Exception
*/
public static function forMissingParameters($route)
{
return new static("Missing required parameters for [Route: {$route->getName()}] [URI: {$route->getPath()}].");
return new static("Missing required parameters for [Route: {$route->getName()}] [URI: {$route->uri()}].");
}
}
239 changes: 75 additions & 164 deletions src/Illuminate/Routing/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@ public function __construct($methods, $uri, $action)
}
}

/**
* Parse the route action into a standard array.
*
* @param callable|array|null $action
* @return array
*
* @throws \UnexpectedValueException
*/
protected function parseAction($action)
{
return RouteAction::parse($this->uri, $action);
}

/**
* Run the route action and return the response.
*
Expand Down Expand Up @@ -250,6 +263,22 @@ protected function compileRoute()
return $this->compiled;
}

/**
* Bind the route to a given request for execution.
*
* @param \Illuminate\Http\Request $request
* @return $this
*/
public function bind(Request $request)
{
$this->compileRoute();

$this->parameters = (new RouteParameterBinder($this))
->parameters($request);

return $this;
}

/**
* Determine if the route has parameters.
*
Expand Down Expand Up @@ -378,125 +407,7 @@ protected function compileParameterNames()
*/
public function signatureParameters($subClass = null)
{
$action = $this->getAction();

if (is_string($action['uses'])) {
list($class, $method) = explode('@', $action['uses']);

$parameters = (new ReflectionMethod($class, $method))->getParameters();
} else {
$parameters = (new ReflectionFunction($action['uses']))->getParameters();
}

return is_null($subClass) ? $parameters : array_filter($parameters, function ($p) use ($subClass) {
return $p->getClass() && $p->getClass()->isSubclassOf($subClass);
});
}

/**
* Bind the route to a given request for execution.
*
* @param \Illuminate\Http\Request $request
* @return $this
*/
public function bind(Request $request)
{
$this->compileRoute();

$this->parameters = (new RouteParameterBinder($this))
->parameters($request);

return $this;
}

/**
* Parse the route action into a standard array.
*
* @param callable|array|null $action
* @return array
*
* @throws \UnexpectedValueException
*/
protected function parseAction($action)
{
// If no action is passed in right away, we assume the user will make use of
// fluent routing. In that case, we set a default closure, to be executed
// if the user never explicitly sets an action to handle the given uri.
if (is_null($action)) {
return ['uses' => function () {
throw new LogicException("Route for [{$this->uri}] has no action.");
}];
}

// If the action is already a Closure instance, we will just set that instance
// as the "uses" property, because there is nothing else we need to do when
// it is available. Otherwise we will need to find it in the action list.
if (is_callable($action)) {
return ['uses' => $action];
}

// If no "uses" property has been set, we will dig through the array to find a
// Closure instance within this list. We will set the first Closure we come
// across into the "uses" property that will get fired off by this route.
elseif (! isset($action['uses'])) {
$action['uses'] = $this->findCallable($action);
}

if (is_string($action['uses']) && ! Str::contains($action['uses'], '@')) {
$action['uses'] = $this->makeInvokableAction($action['uses']);
}

return $action;
}

/**
* Find the callable in an action array.
*
* @param array $action
* @return callable
*/
protected function findCallable(array $action)
{
return Arr::first($action, function ($value, $key) {
return is_callable($value) && is_numeric($key);
});
}

/**
* Make an action for an invokable controller.
*
* @param string $action
* @return string
*/
protected function makeInvokableAction($action)
{
if (! method_exists($action, '__invoke')) {
throw new UnexpectedValueException(sprintf(
'Invalid route action: [%s]', $action
));
}

return $action.'@__invoke';
}

/**
* Get the route validators for the instance.
*
* @return array
*/
public static function getValidators()
{
if (isset(static::$validators)) {
return static::$validators;
}

// To match the route, we will use a chain of responsibility pattern with the
// validator implementations. We will spin through each one making sure it
// passes and then we will know if the route as a whole matches request.
return static::$validators = [
new UriValidator, new MethodValidator,
new SchemeValidator, new HostValidator,
];
return RouteSignatureParameters::fromAction($this->action, $subClass);
}

/**
Expand Down Expand Up @@ -556,41 +467,6 @@ protected function whereArray(array $wheres)
return $this;
}

/**
* Add a prefix to the route URI.
*
* @param string $prefix
* @return $this
*/
public function prefix($prefix)
{
$uri = rtrim($prefix, '/').'/'.ltrim($this->uri, '/');

$this->uri = trim($uri, '/');

return $this;
}

/**
* Get the URI associated with the route.
*
* @return string
*/
public function getPath()
{
return $this->uri();
}

/**
* Get the HTTP verbs the route responds to.
*
* @return array
*/
public function getMethods()
{
return $this->methods();
}

/**
* Get the HTTP verbs the route responds to.
*
Expand Down Expand Up @@ -643,36 +519,51 @@ public function domain()
}

/**
* Get the URI associated with the route.
* Get the prefix of the route instance.
*
* @return string
*/
public function uri()
public function getPrefix()
{
return $this->uri;
return isset($this->action['prefix']) ? $this->action['prefix'] : null;
}

/**
* Set the URI that the route responds to.
* Add a prefix to the route URI.
*
* @param string $uri
* @param string $prefix
* @return $this
*/
public function setUri($uri)
public function prefix($prefix)
{
$this->uri = $uri;
$uri = rtrim($prefix, '/').'/'.ltrim($this->uri, '/');

$this->uri = trim($uri, '/');

return $this;
}

/**
* Get the prefix of the route instance.
* Get the URI associated with the route.
*
* @return string
*/
public function getPrefix()
public function uri()
{
return isset($this->action['prefix']) ? $this->action['prefix'] : null;
return $this->uri;
}

/**
* Set the URI that the route responds to.
*
* @param string $uri
* @return $this
*/
public function setUri($uri)
{
$this->uri = $uri;

return $this;
}

/**
Expand Down Expand Up @@ -815,6 +706,26 @@ public function controllerMiddleware()
);
}

/**
* Get the route validators for the instance.
*
* @return array
*/
public static function getValidators()
{
if (isset(static::$validators)) {
return static::$validators;
}

// To match the route, we will use a chain of responsibility pattern with the
// validator implementations. We will spin through each one making sure it
// passes and then we will know if the route as a whole matches request.
return static::$validators = [
new UriValidator, new MethodValidator,
new SchemeValidator, new HostValidator,
];
}

/**
* Get the compiled version of the route.
*
Expand Down
89 changes: 89 additions & 0 deletions src/Illuminate/Routing/RouteAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

namespace Illuminate\Routing;

use LogicException;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use UnexpectedValueException;

class RouteAction
{
/**
* Parse the given action into an array.
*
* @param string $uri
* @param mixed $action
* @return array
*/
public static function parse($uri, $action)
{
// If no action is passed in right away, we assume the user will make use of
// fluent routing. In that case, we set a default closure, to be executed
// if the user never explicitly sets an action to handle the given uri.
if (is_null($action)) {
return static::missingAction($uri);
}

// If the action is already a Closure instance, we will just set that instance
// as the "uses" property, because there is nothing else we need to do when
// it is available. Otherwise we will need to find it in the action list.
if (is_callable($action)) {
return ['uses' => $action];
}

// If no "uses" property has been set, we will dig through the array to find a
// Closure instance within this list. We will set the first Closure we come
// across into the "uses" property that will get fired off by this route.
elseif (! isset($action['uses'])) {
$action['uses'] = static::findCallable($action);
}

if (is_string($action['uses']) && ! Str::contains($action['uses'], '@')) {
$action['uses'] = static::makeInvokable($action['uses']);
}

return $action;
}

/**
* Get an action for a route that has no action.
*
* @param string $uri
* @return array
*/
protected static function missingAction($uri)
{
return ['uses' => function () use ($uri) {
throw new LogicException("Route for [{$uri}] has no action.");
}];
}

/**
* Find the callable in an action array.
*
* @param array $action
* @return callable
*/
protected static function findCallable(array $action)
{
return Arr::first($action, function ($value, $key) {
return is_callable($value) && is_numeric($key);
});
}

/**
* Make an action for an invokable controller.
*
* @param string $action
* @return string
*/
protected static function makeInvokable($action)
{
if (! method_exists($action, '__invoke')) {
throw new UnexpectedValueException("Invalid route action: [{$action}].");
}

return $action.'@__invoke';
}
}
Loading

0 comments on commit 0f7985c

Please sign in to comment.