Skip to content

Commit

Permalink
Merge pull request #2640 from l0gicgate/4.x-DecoupleFastRouteRouteParser
Browse files Browse the repository at this point in the history
4.x - Decouple FastRoute RouteParser
  • Loading branch information
l0gicgate authored Apr 21, 2019
2 parents f1b45ef + 0c267ec commit e65a4dc
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 479 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
## 4.0.0 - 2019-07-03

### Added
- [#2640](https://github.com/slimphp/Slim/pull/2640) Add `RouteParserInterface` and decouple FastRoute route parser entirely from core. The methods `relativePathFor()`, `urlFor()` and `fullUrlFor()` are now located on this interface.
- [#2639](https://github.com/slimphp/Slim/pull/2639) Add `DispatcherInterface` and decouple FastRoute dispatcher entirely from core. This enables us to swap out our router implementation for any other router.
- [#2638](https://github.com/slimphp/Slim/pull/2638) Add `RouteCollector::fullUrlFor()` to give the ability to generate fully qualified URLs
- [#2634](https://github.com/slimphp/Slim/pull/2634) Added ability to set invocation strategy on a per-route basis.
Expand Down
6 changes: 6 additions & 0 deletions Slim/Interfaces/DispatcherInterface.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
<?php
/**
* Slim Framework (https://slimframework.com)
*
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
*/

declare(strict_types=1);

namespace Slim\Interfaces;
Expand Down
64 changes: 7 additions & 57 deletions Slim/Interfaces/RouteCollectorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@
namespace Slim\Interfaces;

use InvalidArgumentException;
use Psr\Http\Message\UriInterface;
use RuntimeException;

interface RouteCollectorInterface
{
/**
* Get the route parser
*
* @return RouteParserInterface
*/
public function getRouteParser(): RouteParserInterface;

/**
* Get default route invocation strategy
*
Expand Down Expand Up @@ -127,60 +133,4 @@ public function popGroup(): ?RouteGroupInterface;
* @return RouteInterface
*/
public function map(array $methods, string $pattern, $handler): RouteInterface;

/**
* Build the path for a named route excluding the base path
*
* @param string $name Route name
* @param array $data Named argument replacement data
* @param array $queryParams Optional query string parameters
*
* @return string
*
* @throws RuntimeException If named route does not exist
* @throws InvalidArgumentException If required data not provided
*/
public function relativePathFor(string $name, array $data = [], array $queryParams = []): string;

/**
* Build the path for a named route including the base path
*
* This method is deprecated. Use urlFor() from now on.
*
* @param string $name Route name
* @param array $data Named argument replacement data
* @param array $queryParams Optional query string parameters
*
* @return string
*
* @throws RuntimeException If named route does not exist
* @throws InvalidArgumentException If required data not provided
*/
public function pathFor(string $name, array $data = [], array $queryParams = []): string;

/**
* Build the path for a named route including the base path
*
* @param string $name Route name
* @param array $data Named argument replacement data
* @param array $queryParams Optional query string parameters
*
* @return string
*
* @throws RuntimeException If named route does not exist
* @throws InvalidArgumentException If required data not provided
*/
public function urlFor(string $name, array $data = [], array $queryParams = []): string;

/**
* Get fully qualified URL for named route
*
* @param UriInterface $uri
* @param string $routeName Route name
* @param array $data Named argument replacement data
* @param array $queryParams Optional query string parameters
*
* @return string
*/
public function fullUrlFor(UriInterface $uri, string $routeName, array $data = [], array $queryParams = []): string;
}
78 changes: 78 additions & 0 deletions Slim/Interfaces/RouteParserInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php
/**
* Slim Framework (https://slimframework.com)
*
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
*/

declare(strict_types=1);

namespace Slim\Interfaces;

use InvalidArgumentException;
use Psr\Http\Message\UriInterface;
use RuntimeException;

/**
* Interface RouteParserInterface
*
* @package Slim\Interfaces
*/
interface RouteParserInterface
{
/**
* Build the path for a named route excluding the base path
*
* @param string $name Route name
* @param array $data Named argument replacement data
* @param array $queryParams Optional query string parameters
*
* @return string
*
* @throws RuntimeException If named route does not exist
* @throws InvalidArgumentException If required data not provided
*/
public function relativePathFor(string $name, array $data = [], array $queryParams = []): string;

/**
* Build the path for a named route including the base path
*
* This method is deprecated. Use urlFor() from now on.
*
* @param string $name Route name
* @param array $data Named argument replacement data
* @param array $queryParams Optional query string parameters
*
* @return string
*
* @throws RuntimeException If named route does not exist
* @throws InvalidArgumentException If required data not provided
*/
public function pathFor(string $name, array $data = [], array $queryParams = []): string;

/**
* Build the path for a named route including the base path
*
* @param string $name Route name
* @param array $data Named argument replacement data
* @param array $queryParams Optional query string parameters
*
* @return string
*
* @throws RuntimeException If named route does not exist
* @throws InvalidArgumentException If required data not provided
*/
public function urlFor(string $name, array $data = [], array $queryParams = []): string;

/**
* Get fully qualified URL for named route
*
* @param UriInterface $uri
* @param string $routeName Route name
* @param array $data Named argument replacement data
* @param array $queryParams Optional query string parameters
*
* @return string
*/
public function fullUrlFor(UriInterface $uri, string $routeName, array $data = [], array $queryParams = []): string;
}
116 changes: 8 additions & 108 deletions Slim/Routing/RouteCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

namespace Slim\Routing;

use FastRoute\RouteParser;
use FastRoute\RouteParser\Std as StdParser;
use InvalidArgumentException;
use Psr\Container\ContainerInterface;
Expand All @@ -22,6 +21,7 @@
use Slim\Interfaces\RouteCollectorInterface;
use Slim\Interfaces\RouteGroupInterface;
use Slim\Interfaces\RouteInterface;
use Slim\Interfaces\RouteParserInterface;

/**
* RouteCollector is used to collect routes and route groups
Expand All @@ -30,9 +30,7 @@
class RouteCollector implements RouteCollectorInterface
{
/**
* Parser
*
* @var RouteParser
* @var RouteParserInterface
*/
protected $routeParser;

Expand Down Expand Up @@ -98,28 +96,28 @@ class RouteCollector implements RouteCollectorInterface
* @param CallableResolverInterface $callableResolver
* @param ContainerInterface|null $container
* @param InvocationStrategyInterface $defaultInvocationStrategy
* @param RouteParser $parser
* @param RouteParserInterface $routeParser
*/
public function __construct(
ResponseFactoryInterface $responseFactory,
CallableResolverInterface $callableResolver,
ContainerInterface $container = null,
InvocationStrategyInterface $defaultInvocationStrategy = null,
RouteParser $parser = null
RouteParserInterface $routeParser = null
) {
$this->responseFactory = $responseFactory;
$this->callableResolver = $callableResolver;
$this->container = $container;
$this->defaultInvocationStrategy = $defaultInvocationStrategy ?? new RequestResponse();
$this->routeParser = $parser ?? new StdParser;
$this->routeParser = $routeParser ?? new RouteParser($this);
}

/**
* @return CallableResolverInterface
* @return RouteParserInterface
*/
public function getCallableResolver(): CallableResolverInterface
public function getRouteParser(): RouteParserInterface
{
return $this->callableResolver;
return $this->routeParser;
}

/**
Expand Down Expand Up @@ -309,102 +307,4 @@ protected function createRoute(array $methods, string $pattern, $callable): Rout
$this->routeCounter
);
}

/**
* {@inheritdoc}
*/
public function relativePathFor(string $name, array $data = [], array $queryParams = []): string
{
$route = $this->getNamedRoute($name);
$pattern = $route->getPattern();

$segments = [];
$segmentName = '';

/*
* $routes is an associative array of expressions representing a route as multiple segments
* There is an expression for each optional parameter plus one without the optional parameters
* The most specific is last, hence why we reverse the array before iterating over it
*/
$expressions = array_reverse($this->routeParser->parse($pattern));
foreach ($expressions as $expression) {
foreach ($expression as $segment) {
/*
* Each $segment is either a string or an array of strings
* containing optional parameters of an expression
*/
if (is_string($segment)) {
$segments[] = $segment;
continue;
}

/*
* If we don't have a data element for this segment in the provided $data
* we cancel testing to move onto the next expression with a less specific item
*/
if (!array_key_exists($segment[0], $data)) {
$segments = [];
$segmentName = $segment[0];
break;
}

$segments[] = $data[$segment[0]];
}

/*
* If we get to this logic block we have found all the parameters
* for the provided $data which means we don't need to continue testing
* less specific expressions
*/
if (!empty($segments)) {
break;
}
}

if (empty($segments)) {
throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName);
}

$url = implode('', $segments);
if ($queryParams) {
$url .= '?' . http_build_query($queryParams);
}

return $url;
}

/**
* {@inheritdoc}
*/
public function pathFor(string $name, array $data = [], array $queryParams = []): string
{
trigger_error('pathFor() is deprecated. Use urlFor() instead.', E_USER_DEPRECATED);
return $this->urlFor($name, $data, $queryParams);
}

/**
* {@inheritdoc}
*/
public function urlFor(string $name, array $data = [], array $queryParams = []): string
{
$url = $this->relativePathFor($name, $data, $queryParams);

if ($this->basePath) {
$url = $this->basePath . $url;
}

return $url;
}

/**
* {@inheritdoc}
*/
public function fullUrlFor(UriInterface $uri, string $routeName, array $data = [], array $queryParams = []): string
{
$path = $this->urlFor($routeName, $data, $queryParams);
$scheme = $uri->getScheme();
$authority = $uri->getAuthority();
$protocol = ($scheme ? $scheme . ':' : '') . ($authority ? '//' . $authority : '');
return $protocol . $path;
}
}
Loading

0 comments on commit e65a4dc

Please sign in to comment.