Skip to content

Commit

Permalink
Merge branch 'refactor-middleware-into-stack' into 4.x
Browse files Browse the repository at this point in the history
Closes #2591
  • Loading branch information
akrabat committed Mar 2, 2019
2 parents 5c4c416 + 093560e commit 7dc705d
Show file tree
Hide file tree
Showing 25 changed files with 767 additions and 747 deletions.
30 changes: 8 additions & 22 deletions Slim/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@
use Slim\Interfaces\RouteGroupInterface;
use Slim\Interfaces\RouteInterface;
use Slim\Interfaces\RouterInterface;
use Slim\Middleware\ClosureMiddleware;
use Slim\Middleware\DeferredResolutionMiddleware;
use Slim\Middleware\DispatchMiddleware;

/**
* App
Expand Down Expand Up @@ -57,9 +54,9 @@ class App implements RequestHandlerInterface
protected $callableResolver;

/**
* @var MiddlewareRunner
* @var MiddlewareDispatcher
*/
protected $middlewareRunner;
protected $middlewareDispatcher;

/**
* @var RouterInterface
Expand Down Expand Up @@ -92,9 +89,8 @@ public function __construct(
$this->responseFactory = $responseFactory;
$this->container = $container;
$this->callableResolver = $callableResolver ?? new CallableResolver($container);
$this->router = $router ?? new Router($responseFactory, $this->callableResolver);
$this->middlewareRunner = new MiddlewareRunner();
$this->addMiddleware(new DispatchMiddleware($this->router));
$this->router = $router ?? new Router($responseFactory, $this->callableResolver, $this->container);
$this->middlewareDispatcher = new MiddlewareDispatcher(new RouteDispatcher($this->router), $container);
}

/**
Expand All @@ -103,18 +99,8 @@ public function __construct(
*/
public function add($middleware): self
{
if (is_string($middleware)) {
$middleware = new DeferredResolutionMiddleware($middleware, $this->container);
} elseif ($middleware instanceof Closure) {
$middleware = new ClosureMiddleware($middleware);
} elseif (!($middleware instanceof MiddlewareInterface)) {
throw new RuntimeException(
'Parameter 1 of `Slim\App::add()` must be a closure or an object/class name '.
'referencing an implementation of MiddlewareInterface.'
);
}

return $this->addMiddleware($middleware);
$this->middlewareDispatcher->add($middleware);
return $this;
}

/**
Expand All @@ -123,7 +109,7 @@ public function add($middleware): self
*/
public function addMiddleware(MiddlewareInterface $middleware): self
{
$this->middlewareRunner->add($middleware);
$this->middlewareDispatcher->addMiddleware($middleware);
return $this;
}

Expand Down Expand Up @@ -347,7 +333,7 @@ public function run(ServerRequestInterface $request): void
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$response = $this->middlewareRunner->run($request);
$response = $this->middlewareDispatcher->handle($request);

/**
* This is to be in compliance with RFC 2616, Section 9.
Expand Down
8 changes: 0 additions & 8 deletions Slim/CallableResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,4 @@ public function resolve($toResolve): callable

return $resolved;
}

/**
* @return ContainerInterface|null
*/
public function getContainer(): ?ContainerInterface
{
return $this->container;
}
}
7 changes: 0 additions & 7 deletions Slim/Interfaces/CallableResolverInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

namespace Slim\Interfaces;

use Psr\Container\ContainerInterface;

/**
* Resolves a callable.
*
Expand All @@ -28,9 +26,4 @@ interface CallableResolverInterface
* @return callable
*/
public function resolve($toResolve): callable;

/**
* @return ContainerInterface|null
*/
public function getContainer(): ?ContainerInterface;
}
88 changes: 0 additions & 88 deletions Slim/Middleware/DeferredResolutionMiddleware.php

This file was deleted.

204 changes: 204 additions & 0 deletions Slim/MiddlewareDispatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
<?php
/**
* Slim Framework (https://slimframework.com)
*
* @link https://github.com/slimphp/Slim
* @copyright Copyright (c) 2011-2018 Josh Lockhart
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
*/

declare(strict_types=1);

namespace Slim;

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use RuntimeException;

/**
* Class MiddlewareDispatcher
* @package Slim
*/
class MiddlewareDispatcher implements RequestHandlerInterface
{
/**
* Tip of the middleware call stack
*
* @var RequestHandlerInterface
*/
protected $tip;

/**
* @var ContainerInterface|null
*/
protected $container;

/**
* @param RequestHandlerInterface $kernel
* @param ContainerInterface|null $container
*/
public function __construct(
RequestHandlerInterface $kernel,
ContainerInterface $container = null
) {
$this->seedMiddlewareStack($kernel);
$this->container = $container;
}

/**
* Invoke the middleware stack
*
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
return $this->tip->handle($request);
}

/**
* Seed the middleware stack with the inner request handler
*
* @param RequestHandlerInterface $kernel
*/
protected function seedMiddlewareStack(RequestHandlerInterface $kernel)
{
$this->tip = $kernel;
}

/**
* Add a new middleware to the stack
*
* Middleware are organized as a stack. That means middleware
* that have been added before will be executed after the newly
* added one (last in, first out).
*
* @param MiddlewareInterface|string|callable $middleware
*/
public function add($middleware)
{
if ($middleware instanceof MiddlewareInterface) {
$this->addMiddleware($middleware);
} elseif (is_string($middleware)) {
$this->addDeferred($middleware);
} elseif (is_callable($middleware)) {
$this->addCallable($middleware);
} else {
throw new RuntimeException(
'A middleware must be an object/class name referencing an implementation of ' .
'MiddlewareInterface or a callable with a matching signature.'
);
}
}

/**
* Add a new middleware to the stack
*
* Middleware are organized as a stack. That means middleware
* that have been added before will be executed after the newly
* added one (last in, first out).
*
* @param MiddlewareInterface $middleware
*/
public function addMiddleware(MiddlewareInterface $middleware)
{
$next = $this->tip;
$this->tip = new class($middleware, $next) implements RequestHandlerInterface {
private $middleware;
private $next;

public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $next)
{
$this->middleware = $middleware;
$this->next = $next;
}

public function handle(ServerRequestInterface $request): ResponseInterface
{
return $this->middleware->process($request, $this->next);
}
};
}

/**
* Add a new middleware by class name
*
* Middleware are organized as a stack. That means middleware
* that have been added before will be executed after the newly
* added one (last in, first out).
*
* @param string $middleware
*/
public function addDeferred(string $middleware)
{
$next = $this->tip;
$this->tip = new class($middleware, $next, $this->container) implements RequestHandlerInterface {
private $middleware;
private $next;
private $container;

public function __construct(
string $middleware,
RequestHandlerInterface $next,
ContainerInterface $container = null
) {
$this->middleware = $middleware;
$this->next = $next;
$this->container = $container;
}

public function handle(ServerRequestInterface $request): ResponseInterface
{
$resolved = $this->middleware;
if ($this->container && $this->container->has($this->middleware)) {
$resolved = $this->container->get($this->middleware);
if ($resolved instanceof MiddlewareInterface) {
return $resolved->process($request, $this->next);
}
}
if (is_subclass_of($resolved, MiddlewareInterface::class)) {
return (new $resolved)->process($request, $this->next);
}
if (is_callable($resolved)) {
return ($resolved)($request, $this->next);
}
throw new RuntimeException(sprintf(
'%s is not resolvable',
$this->middleware
));
}
};
}

/**
* Add a (non standard) callable middleware to the stack
*
* Middleware are organized as a stack. That means middleware
* that have been added before will be executed after the newly
* added one (last in, first out).
*
* @param callable $middleware
*/
public function addCallable(callable $middleware)
{
$next = $this->tip;
$this->tip = new class($middleware, $next) implements RequestHandlerInterface {
private $middleware;
private $next;

public function __construct(callable $middleware, RequestHandlerInterface $next)
{
$this->middleware = $middleware;
$this->next = $next;
}

public function handle(ServerRequestInterface $request): ResponseInterface
{
return ($this->middleware)($request, $this->next);
}
};
}
}

0 comments on commit 7dc705d

Please sign in to comment.