Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Imports routing and dispatch middleware from proposed Expressive v3 branch #47

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 10 additions & 34 deletions src/DispatchMiddleware.php
Original file line number Diff line number Diff line change
@@ -1,62 +1,38 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-router for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-router/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Zend\Expressive\Router;

use Interop\Http\Middleware\ServerMiddlewareInterface as LegacyLegacyMiddlewareInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface as LegacyMiddlewareInterface;
use Interop\Http\Server\MiddlewareInterface as InteropMiddlewareInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Webimpress\HttpMiddlewareCompatibility\HandlerInterface;
use Webimpress\HttpMiddlewareCompatibility\MiddlewareInterface;

use const Webimpress\HttpMiddlewareCompatibility\HANDLER_METHOD;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

/**
* Default dispatch middleware.
*
* Checks for a composed route result in the request. If none is provided,
* delegates to the next middleware.
* delegates request processing to the handler.
*
* Otherwise, it pulls the middleware from the route result. If the middleware
* is not http-interop middleware, it raises an exception. This means that
* this middleware is incompatible with routes that store non-http-interop
* middleware instances! Make certain you only provide middleware instances
* to your router when using this middleware.
* Otherwise, it pulls the middleware from the route result and processes it
* with the provided request and handler.
*/
class DispatchMiddleware implements MiddlewareInterface
{
/**
* @param ServerRequestInterface $request
* @param HandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, HandlerInterface $handler)
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
$routeResult = $request->getAttribute(RouteResult::class, false);
if (! $routeResult) {
return $handler->{HANDLER_METHOD}($request);
return $handler->handle($request);
}

$middleware = $routeResult->getMatchedMiddleware();

if (! $middleware instanceof LegacyLegacyMiddlewareInterface
&& ! $middleware instanceof LegacyMiddlewareInterface
&& ! $middleware instanceof InteropMiddlewareInterface
) {
throw new Exception\RuntimeException(sprintf(
'Unknown middleware type stored in route; %s expects an http-interop'
. ' middleware instance; received %s',
__CLASS__,
is_object($middleware) ? get_class($middleware) : gettype($middleware)
));
}

return $middleware->process($request, $handler);
}
}
17 changes: 17 additions & 0 deletions src/Exception/DuplicateRouteException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-router for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-router/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Zend\Expressive\Router\Exception;

use DomainException;

class DuplicateRouteException extends DomainException implements
ExceptionInterface
{
}
162 changes: 162 additions & 0 deletions src/PathBasedRoutingMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-router for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-router/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Zend\Expressive\Router;

use Psr\Http\Server\MiddlewareInterface;

/**
* Routing middleware for path-based routes.
*
* This middleware extends the RouteMiddleware in order to provide additional
* methods for creating path+HTTP method-based routes:
*
* - get
* - post
* - put
* - patch
* - delete
* - any
*
* A general `route()` method allows specifying multiple request methods and/or
* arbitrary request methods when creating a path-based route.
*
* Internally, the middleware performs some checks for duplicate routes when
* attaching via one of the exposed methods, and will raise an exception when a
* collision occurs.
*/
class PathBasedRoutingMiddleware extends RouteMiddleware
{
/**
* List of all routes registered directly with the application.
*
* @var Route[]
*/
private $routes = [];

/**
* Add a route for the route middleware to match.
*
* Accepts a combination of a path and middleware, and optionally the HTTP methods allowed.
*
* @param null|array $methods HTTP method to accept; null indicates any.
* @param null|string $name The name of the route.
* @throws Exception\DuplicateRouteException if specification represents an existing route.
*/
public function route(
string $path,
MiddlewareInterface $middleware,
array $methods = null,
string $name = null
) : Route {
$this->checkForDuplicateRoute($path, $methods);

$methods = null === $methods ? Route::HTTP_METHOD_ANY : $methods;
$route = new Route($path, $middleware, $methods, $name);

$this->routes[] = $route;
$this->router->addRoute($route);

return $route;
}

/**
* @param null|string $name The name of the route.
*/
public function get(string $path, MiddlewareInterface $middleware, string $name = null) : Route
{
return $this->route($path, $middleware, ['GET'], $name);
}

/**
* @param null|string $name The name of the route.
*/
public function post(string $path, MiddlewareInterface $middleware, string $name = null) : Route
{
return $this->route($path, $middleware, ['POST'], $name);
}

/**
* @param null|string $name The name of the route.
*/
public function put(string $path, MiddlewareInterface $middleware, string $name = null) : Route
{
return $this->route($path, $middleware, ['PUT'], $name);
}

/**
* @param null|string $name The name of the route.
*/
public function patch(string $path, MiddlewareInterface $middleware, string $name = null) : Route
{
return $this->route($path, $middleware, ['PATCH'], $name);
}

/**
* @param null|string $name The name of the route.
*/
public function delete(string $path, MiddlewareInterface $middleware, string $name = null) : Route
{
return $this->route($path, $middleware, ['DELETE'], $name);
}

/**
* @param null|string $name The name of the route.
*/
public function any(string $path, MiddlewareInterface $middleware, string $name = null) : Route
{
return $this->route($path, $middleware, null, $name);
}

/**
* Retrieve all directly registered routes with the application.
*
* @return Route[]
*/
public function getRoutes() : array
{
return $this->routes;
}

/**
* Determine if the route is duplicated in the current list.
*
* Checks if a route with the same name or path exists already in the list;
* if so, and it responds to any of the $methods indicated, raises
* a DuplicateRouteException indicating a duplicate route.
*
* @throws Exception\DuplicateRouteException on duplicate route detection.
*/
private function checkForDuplicateRoute(string $path, array $methods = null) : void
{
if (null === $methods) {
$methods = Route::HTTP_METHOD_ANY;
}

$matches = array_filter($this->routes, function (Route $route) use ($path, $methods) {
if ($path !== $route->getPath()) {
return false;
}

if ($methods === Route::HTTP_METHOD_ANY) {
return true;
}

return array_reduce($methods, function ($carry, $method) use ($route) {
return ($carry || $route->allowsMethod($method));
}, false);
});

if (! empty($matches)) {
throw new Exception\DuplicateRouteException(
'Duplicate route detected; same name or path, and one or more HTTP methods intersect'
);
}
}
}
29 changes: 9 additions & 20 deletions src/RouteMiddleware.php
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-router for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-router/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Zend\Expressive\Router;

use Fig\Http\Message\StatusCodeInterface as StatusCode;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Expressive\Router\RouteResult;
use Zend\Expressive\Router\RouterInterface;
use Webimpress\HttpMiddlewareCompatibility\HandlerInterface;
use Webimpress\HttpMiddlewareCompatibility\MiddlewareInterface;

use const Webimpress\HttpMiddlewareCompatibility\HANDLER_METHOD;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

/**
* Default routing middleware.
Expand All @@ -42,24 +40,15 @@ class RouteMiddleware implements MiddlewareInterface
/**
* @var RouterInterface
*/
private $router;
protected $router;

/**
* @param RouterInterface $router
* @param ResponseInterface $responsePrototype
*/
public function __construct(RouterInterface $router, ResponseInterface $responsePrototype)
{
$this->router = $router;
$this->responsePrototype = $responsePrototype;
}

/**
* @param ServerRequestInterface $request
* @param HandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, HandlerInterface $handler)
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
$result = $this->router->match($request);

Expand All @@ -68,7 +57,7 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl
return $this->responsePrototype->withStatus(StatusCode::STATUS_METHOD_NOT_ALLOWED)
->withHeader('Allow', implode(',', $result->getAllowedMethods()));
}
return $handler->{HANDLER_METHOD}($request);
return $handler->handle($request);
}

// Inject the actual route result, as well as individual matched parameters.
Expand All @@ -77,6 +66,6 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl
$request = $request->withAttribute($param, $value);
}

return $handler->{HANDLER_METHOD}($request);
return $handler->handle($request);
}
}
Loading