diff --git a/docs/api/app.md b/docs/api/app.md index 709c9b7..26ae747 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -194,25 +194,60 @@ the [`ErrorHandler`](middleware.md#errorhandler) to the list of middleware used. You may also explicitly pass an [`ErrorHandler`](middleware.md#errorhandler) middleware to the `App` like this: +=== "Using middleware instances" + + ```php title="public/index.php" + run(); + ``` + +=== "Using middleware names" + + ```php title="public/index.php" + run(); + ``` + +If you do not explicitly pass an [`ErrorHandler`](middleware.md#errorhandler) or +if you pass another middleware before an [`ErrorHandler`](middleware.md#errorhandler) +to the `App`, a default error handler will be added as a first handler automatically. +You may use the [DI container configuration](../best-practices/controllers.md#container-configuration) +to configure the default error handler like this: + ```php title="public/index.php" fn () => new FrameworkX\ErrorHandler() +]); + +$app = new FrameworkX\App($container); // Register routes here, see routing… $app->run(); ``` -> ⚠️ **Feature preview** -> -> Note that the [`ErrorHandler`](middleware.md#errorhandler) may currently only -> be passed as a middleware instance and not as a middleware name to the `App`. - By default, this error message contains only few details to the client to avoid leaking too much internal information. If you want to implement custom error handling, you're recommended to either @@ -245,28 +280,68 @@ adding the [`AccessLogHandler`](middleware.md#accessloghandler) to the list of middleware used. You may also explicitly pass an [`AccessLogHandler`](middleware.md#accessloghandler) middleware to the `App` like this: -```php title="public/index.php" -run(); -``` + // Register routes here, see routing… + + $app->run(); + ``` + +=== "Using middleware names" + + ```php title="public/index.php" + run(); + ``` > ⚠️ **Feature preview** > > Note that the [`AccessLogHandler`](middleware.md#accessloghandler) may -> currently only be passed as a global middleware instance and not as a global -> middleware name to the `App` and may not be used for individual routes. +> currently only be passed as a global middleware to the `App` and may not be +> used for individual routes. If you pass an [`AccessLogHandler`](middleware.md#accessloghandler) to the `App`, it must be followed by an [`ErrorHandler`](middleware.md#errorhandler) like in the previous example. See also [error handling](#error-handling) for more details. + +If you do not explicitly pass an [`AccessLogHandler`](middleware.md#accessloghandler) +to the `App`, a default access log handler will be added as a first handler automatically. +You may use the [DI container configuration](../best-practices/controllers.md#container-configuration) +to configure the default access log handler like this: + +```php title="public/index.php" + fn () => new FrameworkX\AccessLogHandler() +]); + +$app = new FrameworkX\App($container); + +// Register routes here, see routing… + +$app->run(); +``` diff --git a/src/App.php b/src/App.php index 3283a50..94475be 100644 --- a/src/App.php +++ b/src/App.php @@ -41,28 +41,45 @@ public function __construct(...$middleware) // new MiddlewareHandler([$fiberHandler, $accessLogHandler, $errorHandler, ...$middleware, $routeHandler]) $handlers = []; + $container = $needsErrorHandler = new Container(); + // only log for built-in webserver and PHP development webserver by default, others have their own access log - $needsAccessLog = (\PHP_SAPI === 'cli' || \PHP_SAPI === 'cli-server'); + $needsAccessLog = (\PHP_SAPI === 'cli' || \PHP_SAPI === 'cli-server') ? $container : null; - $container = new Container(); if ($middleware) { $needsErrorHandlerNext = false; foreach ($middleware as $handler) { + // load AccessLogHandler and ErrorHandler instance from last Container + if ($handler === AccessLogHandler::class) { + $handler = $container->getAccessLogHandler(); + } elseif ($handler === ErrorHandler::class) { + $handler = $container->getErrorHandler(); + } + + // ensure AccessLogHandler is always followed by ErrorHandler if ($needsErrorHandlerNext && !$handler instanceof ErrorHandler) { break; } $needsErrorHandlerNext = false; if ($handler instanceof Container) { + // remember last Container to load any following class names $container = $handler; - } elseif ($handler === ErrorHandler::class || $handler === AccessLogHandler::class) { - throw new \TypeError($handler . ' may currently only be passed as a middleware instance'); + + // add default ErrorHandler from last Container before adding any other handlers, may be followed by other Container instances (unlikely) + if (!$handlers) { + $needsErrorHandler = $needsAccessLog = $container; + } } elseif (!\is_callable($handler)) { $handlers[] = $container->callable($handler); } else { + // don't need a default ErrorHandler if we're adding one as first handler or AccessLogHandler as first followed by one + if ($needsErrorHandler && ($handler instanceof ErrorHandler || $handler instanceof AccessLogHandler) && !$handlers) { + $needsErrorHandler = null; + } $handlers[] = $handler; if ($handler instanceof AccessLogHandler) { - $needsAccessLog = false; + $needsAccessLog = null; $needsErrorHandlerNext = true; } } @@ -73,13 +90,13 @@ public function __construct(...$middleware) } // add default ErrorHandler as first handler unless it is already added explicitly - if (!($handlers[0] ?? null) instanceof ErrorHandler && !($handlers[0] ?? null) instanceof AccessLogHandler) { - \array_unshift($handlers, new ErrorHandler()); + if ($needsErrorHandler instanceof Container) { + \array_unshift($handlers, $needsErrorHandler->getErrorHandler()); } // only log for built-in webserver and PHP development webserver by default, others have their own access log - if ($needsAccessLog) { - \array_unshift($handlers, new AccessLogHandler()); + if ($needsAccessLog instanceof Container) { + \array_unshift($handlers, $needsAccessLog->getAccessLogHandler()); } // automatically start new fiber for each request on PHP 8.1+ diff --git a/src/Container.php b/src/Container.php index fe48297..9d6b203 100644 --- a/src/Container.php +++ b/src/Container.php @@ -92,6 +92,32 @@ public function callable(string $class): callable }; } + /** @internal */ + public function getAccessLogHandler(): AccessLogHandler + { + if ($this->container instanceof ContainerInterface) { + if ($this->container->has(AccessLogHandler::class)) { + return $this->container->get(AccessLogHandler::class); + } else { + return new AccessLogHandler(); + } + } + return $this->load(AccessLogHandler::class); + } + + /** @internal */ + public function getErrorHandler(): ErrorHandler + { + if ($this->container instanceof ContainerInterface) { + if ($this->container->has(ErrorHandler::class)) { + return $this->container->get(ErrorHandler::class); + } else { + return new ErrorHandler(); + } + } + return $this->load(ErrorHandler::class); + } + /** * @param class-string $name * @return object diff --git a/src/RouteHandler.php b/src/RouteHandler.php index 94f5486..b920ada 100644 --- a/src/RouteHandler.php +++ b/src/RouteHandler.php @@ -56,7 +56,7 @@ public function map(array $methods, string $route, $handler, ...$handlers): void $container = $handler; unset($handlers[$i]); } elseif ($handler instanceof AccessLogHandler || $handler === AccessLogHandler::class) { - throw new \TypeError('AccessLogHandler may currently only be passed as a global middleware instance'); + throw new \TypeError('AccessLogHandler may currently only be passed as a global middleware'); } elseif (!\is_callable($handler)) { $handlers[$i] = $container->callable($handler); } diff --git a/tests/AppTest.php b/tests/AppTest.php index 03393df..24e8e0a 100644 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -87,9 +87,15 @@ public function testConstructWithMiddlewareAssignsGivenMiddleware() $this->assertInstanceOf(RouteHandler::class, $handlers[3]); } - public function testConstructWithContainerAssignsContainerForRouteHandlerOnly() + public function testConstructWithContainerAssignsDefaultHandlersAndContainerForRouteHandlerOnly() { - $container = new Container(); + $accessLogHandler = new AccessLogHandler(); + $errorHandler = new ErrorHandler(); + + $container = $this->createMock(Container::class); + $container->expects($this->once())->method('getAccessLogHandler')->willReturn($accessLogHandler); + $container->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler); + $app = new App($container); $ref = new ReflectionProperty($app, 'handler'); @@ -107,8 +113,8 @@ public function testConstructWithContainerAssignsContainerForRouteHandlerOnly() } $this->assertCount(3, $handlers); - $this->assertInstanceOf(AccessLogHandler::class, $handlers[0]); - $this->assertInstanceOf(ErrorHandler::class, $handlers[1]); + $this->assertSame($accessLogHandler, $handlers[0]); + $this->assertSame($errorHandler, $handlers[1]); $this->assertInstanceOf(RouteHandler::class, $handlers[2]); $routeHandler = $handlers[2]; @@ -178,11 +184,28 @@ public function testConstructWithErrorHandlerOnlyAssignsErrorHandlerAfterDefault $this->assertInstanceOf(RouteHandler::class, $handlers[2]); } - public function testConstructWithErrorHandlerClassThrows() + public function testConstructWithErrorHandlerClassOnlyAssignsErrorHandlerAfterDefaultAccessLogHandler() { - $this->expectException(\TypeError::class); - $this->expectExceptionMessage('ErrorHandler may currently only be passed as a middleware instance'); - new App(ErrorHandler::class); + $app = new App(ErrorHandler::class); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(3, $handlers); + $this->assertInstanceOf(AccessLogHandler::class, $handlers[0]); + $this->assertInstanceOf(ErrorHandler::class, $handlers[1]); + $this->assertInstanceOf(RouteHandler::class, $handlers[2]); } public function testConstructWithContainerAndErrorHandlerAssignsErrorHandlerAfterDefaultAccessLogHandler() @@ -211,6 +234,101 @@ public function testConstructWithContainerAndErrorHandlerAssignsErrorHandlerAfte $this->assertInstanceOf(RouteHandler::class, $handlers[2]); } + public function testConstructWithContainerAndErrorHandlerClassAssignsErrorHandlerFromContainerAfterDefaultAccessLogHandler() + { + $errorHandler = new ErrorHandler(); + + $container = $this->createMock(Container::class); + $container->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler); + + $app = new App($container, ErrorHandler::class); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(3, $handlers); + $this->assertInstanceOf(AccessLogHandler::class, $handlers[0]); + $this->assertSame($errorHandler, $handlers[1]); + $this->assertInstanceOf(RouteHandler::class, $handlers[2]); + } + + public function testConstructWithMultipleContainersAndErrorHandlerClassAssignsErrorHandlerFromLastContainerBeforeErrorHandlerAfterDefaultAccessLogHandler() + { + $errorHandler = new ErrorHandler(); + + $unused = $this->createMock(Container::class); + $unused->expects($this->never())->method('getErrorHandler'); + + $container = $this->createMock(Container::class); + $container->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler); + + $app = new App($unused, $container, ErrorHandler::class, $unused); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(3, $handlers); + $this->assertInstanceOf(AccessLogHandler::class, $handlers[0]); + $this->assertSame($errorHandler, $handlers[1]); + $this->assertInstanceOf(RouteHandler::class, $handlers[2]); + } + + public function testConstructWithMultipleContainersAndMiddlewareAssignsErrorHandlerFromLastContainerBeforeMiddlewareAfterDefaultAccessLogHandler() + { + $middleware = function (ServerRequestInterface $request, callable $next) { }; + $errorHandler = new ErrorHandler(); + + $unused = $this->createMock(Container::class); + $unused->expects($this->never())->method('getErrorHandler'); + + $container = $this->createMock(Container::class); + $container->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler); + + $app = new App($unused, $container, $middleware, $unused); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(4, $handlers); + $this->assertInstanceOf(AccessLogHandler::class, $handlers[0]); + $this->assertSame($errorHandler, $handlers[1]); + $this->assertSame($middleware, $handlers[2]); + $this->assertInstanceOf(RouteHandler::class, $handlers[3]); + } + public function testConstructWithMiddlewareAndErrorHandlerAssignsGivenErrorHandlerAfterMiddlewareAndDefaultAccessLogHandlerAndErrorHandlerFirst() { $middleware = function (ServerRequestInterface $request, callable $next) { }; @@ -241,6 +359,45 @@ public function testConstructWithMiddlewareAndErrorHandlerAssignsGivenErrorHandl $this->assertInstanceOf(RouteHandler::class, $handlers[4]); } + public function testConstructWithMultipleContainersAndMiddlewareAndErrorHandlerClassAssignsDefaultErrorHandlerFromLastContainerBeforeMiddlewareAndErrorHandlerFromLastContainerAfterDefaultAccessLogHandler() + { + $middleware = function (ServerRequestInterface $request, callable $next) { }; + + $unused = $this->createMock(Container::class); + $unused->expects($this->never())->method('getErrorHandler'); + + $errorHandler1 = new ErrorHandler(); + $container1 = $this->createMock(Container::class); + $container1->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler1); + + $errorHandler2 = new ErrorHandler(); + $container2 = $this->createMock(Container::class); + $container2->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler2); + + $app = new App($unused, $container1, $middleware, $container2, ErrorHandler::class, $unused); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(5, $handlers); + $this->assertInstanceOf(AccessLogHandler::class, $handlers[0]); + $this->assertSame($errorHandler1, $handlers[1]); + $this->assertSame($middleware, $handlers[2]); + $this->assertSame($errorHandler2, $handlers[3]); + $this->assertInstanceOf(RouteHandler::class, $handlers[4]); + } + public function testConstructWithAccessLogHandlerAndErrorHandlerAssignsHandlersAsGiven() { $accessLogHandler = new AccessLogHandler(); @@ -268,6 +425,61 @@ public function testConstructWithAccessLogHandlerAndErrorHandlerAssignsHandlersA $this->assertInstanceOf(RouteHandler::class, $handlers[2]); } + public function testConstructWithAccessLogHandlerClassAndErrorHandlerClassAssignsDefaultHandlers() + { + $app = new App(AccessLogHandler::class, ErrorHandler::class); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(3, $handlers); + $this->assertInstanceOf(AccessLogHandler::class, $handlers[0]); + $this->assertInstanceOf(ErrorHandler::class, $handlers[1]); + $this->assertInstanceOf(RouteHandler::class, $handlers[2]); + } + + public function testConstructWithContainerAndAccessLogHandlerClassAndErrorHandlerClassAssignsHandlersFromContainer() + { + $accessLogHandler = new AccessLogHandler(); + $errorHandler = new ErrorHandler(); + + $container = $this->createMock(Container::class); + $container->expects($this->once())->method('getAccessLogHandler')->willReturn($accessLogHandler); + $container->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler); + + $app = new App($container, AccessLogHandler::class, ErrorHandler::class); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(3, $handlers); + $this->assertSame($accessLogHandler, $handlers[0]); + $this->assertSame($errorHandler, $handlers[1]); + $this->assertInstanceOf(RouteHandler::class, $handlers[2]); + } + public function testConstructWithMiddlewareBeforeAccessLogHandlerAndErrorHandlerAssignsDefaultErrorHandlerAsFirstHandlerFollowedByGivenHandlers() { $middleware = static function (ServerRequestInterface $request, callable $next) { }; @@ -299,19 +511,85 @@ public function testConstructWithMiddlewareBeforeAccessLogHandlerAndErrorHandler $this->assertInstanceOf(RouteHandler::class, $handlers[4]); } - public function testConstructWithAccessLogHandlerOnlyThrows() + public function testConstructWithMultipleContainersAndAccessLogHandlerClassAndErrorHandlerClassAssignsHandlersFromLastContainer() { $accessLogHandler = new AccessLogHandler(); + $errorHandler = new ErrorHandler(); - $this->expectException(\TypeError::class); - new App($accessLogHandler); + $unused = $this->createMock(Container::class); + $unused->expects($this->never())->method('getErrorHandler'); + + $container = $this->createMock(Container::class); + $container->expects($this->once())->method('getAccessLogHandler')->willReturn($accessLogHandler); + $container->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler); + + $app = new App($unused, $container, AccessLogHandler::class, ErrorHandler::class, $unused); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(3, $handlers); + $this->assertSame($accessLogHandler, $handlers[0]); + $this->assertSame($errorHandler, $handlers[1]); + $this->assertInstanceOf(RouteHandler::class, $handlers[2]); + } + + + public function testConstructWithMultipleContainersAndMiddlewareAssignsDefaultHandlersFromLastContainerBeforeMiddleware() + { + $middleware = function (ServerRequestInterface $request, callable $next) { }; + + $accessLogHandler = new AccessLogHandler(); + $errorHandler = new ErrorHandler(); + + $unused = $this->createMock(Container::class); + $unused->expects($this->never())->method('getAccessLogHandler'); + $unused->expects($this->never())->method('getErrorHandler'); + + $container = $this->createMock(Container::class); + $container->expects($this->once())->method('getAccessLogHandler')->willReturn($accessLogHandler); + $container->expects($this->once())->method('getErrorHandler')->willReturn($errorHandler); + + $app = new App($unused, $container, $middleware, $unused); + + $ref = new ReflectionProperty($app, 'handler'); + $ref->setAccessible(true); + $handler = $ref->getValue($app); + + $this->assertInstanceOf(MiddlewareHandler::class, $handler); + $ref = new ReflectionProperty($handler, 'handlers'); + $ref->setAccessible(true); + $handlers = $ref->getValue($handler); + + if (PHP_VERSION_ID >= 80100) { + $first = array_shift($handlers); + $this->assertInstanceOf(FiberHandler::class, $first); + } + + $this->assertCount(4, $handlers); + $this->assertSame($accessLogHandler, $handlers[0]); + $this->assertSame($errorHandler, $handlers[1]); + $this->assertSame($middleware, $handlers[2]); + $this->assertInstanceOf(RouteHandler::class, $handlers[3]); } - public function testConstructWithAccessLogHandlerClassThrows() + public function testConstructWithAccessLogHandlerOnlyThrows() { + $accessLogHandler = new AccessLogHandler(); + $this->expectException(\TypeError::class); - $this->expectExceptionMessage('AccessLogHandler may currently only be passed as a middleware instance'); - new App(AccessLogHandler::class); + new App($accessLogHandler); } public function testConstructWithAccessLogHandlerFollowedByMiddlewareThrows() diff --git a/tests/ContainerTest.php b/tests/ContainerTest.php index 4c8e666..49ed022 100644 --- a/tests/ContainerTest.php +++ b/tests/ContainerTest.php @@ -2,7 +2,9 @@ namespace FrameworkX\Tests; +use FrameworkX\AccessLogHandler; use FrameworkX\Container; +use FrameworkX\ErrorHandler; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Psr\Container\NotFoundExceptionInterface; @@ -382,6 +384,106 @@ public function testCallableReturnsCallableThatThrowsWhenFactoryReturnsInvalidCl $callable($request); } + public function testGetAccessLogHandlerReturnsDefaultAccessLogHandlerInstance() + { + $container = new Container([]); + + $accessLogHandler = $container->getAccessLogHandler(); + + $this->assertInstanceOf(AccessLogHandler::class, $accessLogHandler); + } + + public function testGetAccessLogHandlerReturnsAccessLogHandlerInstanceFromMap() + { + $accessLogHandler = new AccessLogHandler(); + + $container = new Container([ + AccessLogHandler::class => $accessLogHandler + ]); + + $ret = $container->getAccessLogHandler(); + + $this->assertSame($accessLogHandler, $ret); + } + + public function testGetAccessLogHandlerReturnsAccessLogHandlerInstanceFromPsrContainer() + { + $accessLogHandler = new AccessLogHandler(); + + $psr = $this->createMock(ContainerInterface::class); + $psr->expects($this->once())->method('has')->with(AccessLogHandler::class)->willReturn(true); + $psr->expects($this->once())->method('get')->with(AccessLogHandler::class)->willReturn($accessLogHandler); + + $container = new Container($psr); + + $ret = $container->getAccessLogHandler(); + + $this->assertSame($accessLogHandler, $ret); + } + + public function testGetAccessLogHandlerReturnsDefaultAccessLogHandlerInstanceIfPsrContainerHasNoEntry() + { + $psr = $this->createMock(ContainerInterface::class); + $psr->expects($this->once())->method('has')->with(AccessLogHandler::class)->willReturn(false); + $psr->expects($this->never())->method('get'); + + $container = new Container($psr); + + $accessLogHandler = $container->getAccessLogHandler(); + + $this->assertInstanceOf(AccessLogHandler::class, $accessLogHandler); + } + + public function testGetErrorHandlerReturnsDefaultErrorHandlerInstance() + { + $container = new Container([]); + + $errorHandler = $container->getErrorHandler(); + + $this->assertInstanceOf(ErrorHandler::class, $errorHandler); + } + + public function testGetErrorHandlerReturnsErrorHandlerInstanceFromMap() + { + $errorHandler = new ErrorHandler(); + + $container = new Container([ + ErrorHandler::class => $errorHandler + ]); + + $ret = $container->getErrorHandler(); + + $this->assertSame($errorHandler, $ret); + } + + public function testGetErrorHandlerReturnsErrorHandlerInstanceFromPsrContainer() + { + $errorHandler = new ErrorHandler(); + + $psr = $this->createMock(ContainerInterface::class); + $psr->expects($this->once())->method('has')->with(ErrorHandler::class)->willReturn(true); + $psr->expects($this->once())->method('get')->with(ErrorHandler::class)->willReturn($errorHandler); + + $container = new Container($psr); + + $ret = $container->getErrorHandler(); + + $this->assertSame($errorHandler, $ret); + } + + public function testGetErrorHandlerReturnsDefaultErrorHandlerInstanceIfPsrContainerHasNoEntry() + { + $psr = $this->createMock(ContainerInterface::class); + $psr->expects($this->once())->method('has')->with(ErrorHandler::class)->willReturn(false); + $psr->expects($this->never())->method('get'); + + $container = new Container($psr); + + $errorHandler = $container->getErrorHandler(); + + $this->assertInstanceOf(ErrorHandler::class, $errorHandler); + } + public function testInvokeContainerAsMiddlewareReturnsFromNextRequestHandler() { $request = new ServerRequest('GET', 'http://example.com/');