<?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;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Log\LoggerInterface;
use Slim\Factory\ServerRequestCreatorFactory;
use Slim\Interfaces\CallableResolverInterface;
use Slim\Interfaces\MiddlewareDispatcherInterface;
use Slim\Interfaces\RouteCollectorInterface;
use Slim\Interfaces\RouteResolverInterface;
use Slim\Middleware\BodyParsingMiddleware;
use Slim\Middleware\ErrorMiddleware;
use Slim\Middleware\RoutingMiddleware;
use Slim\Routing\RouteCollectorProxy;
use Slim\Routing\RouteResolver;
use Slim\Routing\RouteRunner;
use function strtoupper;
class App extends RouteCollectorProxy implements RequestHandlerInterface
{
/**
* Current version
*
* @var string
*/
public const VERSION = '4.9.0';
/**
* @var RouteResolverInterface
*/
protected $routeResolver;
/**
* @var MiddlewareDispatcherInterface
*/
protected $middlewareDispatcher;
/**
* @param ResponseFactoryInterface $responseFactory
* @param ContainerInterface|null $container
* @param CallableResolverInterface|null $callableResolver
* @param RouteCollectorInterface|null $routeCollector
* @param RouteResolverInterface|null $routeResolver
* @param MiddlewareDispatcherInterface|null $middlewareDispatcher
*/
public function __construct(
ResponseFactoryInterface $responseFactory,
?ContainerInterface $container = null,
?CallableResolverInterface $callableResolver = null,
?RouteCollectorInterface $routeCollector = null,
?RouteResolverInterface $routeResolver = null,
?MiddlewareDispatcherInterface $middlewareDispatcher = null
) {
parent::__construct(
$responseFactory,
$callableResolver ?? new CallableResolver($container),
$container,
$routeCollector
);
$this->routeResolver = $routeResolver ?? new RouteResolver($this->routeCollector);
$routeRunner = new RouteRunner($this->routeResolver, $this->routeCollector->getRouteParser(), $this);
if (!$middlewareDispatcher) {
$middlewareDispatcher = new MiddlewareDispatcher($routeRunner, $this->callableResolver, $container);
} else {
$middlewareDispatcher->seedMiddlewareStack($routeRunner);
}
$this->middlewareDispatcher = $middlewareDispatcher;
}
/**
* @return RouteResolverInterface
*/
public function getRouteResolver(): RouteResolverInterface
{
return $this->routeResolver;
}
/**
* @return MiddlewareDispatcherInterface
*/
public function getMiddlewareDispatcher(): MiddlewareDispatcherInterface
{
return $this->middlewareDispatcher;
}
/**
* @param MiddlewareInterface|string|callable $middleware
* @return self
*/
public function add($middleware): self
{
$this->middlewareDispatcher->add($middleware);
return $this;
}
/**
* @param MiddlewareInterface $middleware
* @return self
*/
public function addMiddleware(MiddlewareInterface $middleware): self
{
$this->middlewareDispatcher->addMiddleware($middleware);
return $this;
}
/**
* Add the Slim built-in routing middleware to the app middleware stack
*
* This method can be used to control middleware order and is not required for default routing operation.
*
* @return RoutingMiddleware
*/
public function addRoutingMiddleware(): RoutingMiddleware
{
$routingMiddleware = new RoutingMiddleware(
$this->getRouteResolver(),
$this->getRouteCollector()->getRouteParser()
);
$this->add($routingMiddleware);
return $routingMiddleware;
}
/**
* Add the Slim built-in error middleware to the app middleware stack
*
* @param bool $displayErrorDetails
* @param bool $logErrors
* @param bool $logErrorDetails
* @param LoggerInterface|null $logger
*
* @return ErrorMiddleware
*/
public function addErrorMiddleware(
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails,
?LoggerInterface $logger = null
): ErrorMiddleware {
$errorMiddleware = new ErrorMiddleware(
$this->getCallableResolver(),
$this->getResponseFactory(),
$displayErrorDetails,
$logErrors,
$logErrorDetails,
$logger
);
$this->add($errorMiddleware);
return $errorMiddleware;
}
/**
* Add the Slim body parsing middleware to the app middleware stack
*
* @param callable[] $bodyParsers
*
* @return BodyParsingMiddleware
*/
public function addBodyParsingMiddleware(array $bodyParsers = []): BodyParsingMiddleware
{
$bodyParsingMiddleware = new BodyParsingMiddleware($bodyParsers);
$this->add($bodyParsingMiddleware);
return $bodyParsingMiddleware;
}
/**
* Run application
*
* This method traverses the application middleware stack and then sends the
* resultant Response object to the HTTP client.
*
* @param ServerRequestInterface|null $request
* @return void
*/
public function run(?ServerRequestInterface $request = null): void
{
if (!$request) {
$serverRequestCreator = ServerRequestCreatorFactory::create();
$request = $serverRequestCreator->createServerRequestFromGlobals();
}
$response = $this->handle($request);
$responseEmitter = new ResponseEmitter();
$responseEmitter->emit($response);
}
/**
* Handle a request
*
* This method traverses the application middleware stack and then returns the
* resultant Response object.
*
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$response = $this->middlewareDispatcher->handle($request);
/**
* This is to be in compliance with RFC 2616, Section 9.
* If the incoming request method is HEAD, we need to ensure that the response body
* is empty as the request may fall back on a GET route handler due to FastRoute's
* routing logic which could potentially append content to the response body
* https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
*/
$method = strtoupper($request->getMethod());
if ($method === 'HEAD') {
$emptyBody = $this->responseFactory->createResponse()->getBody();
return $response->withBody($emptyBody);
}
return $response;
}
}