From 1a1bd4cf84a51d305aa2048881fdb4ea0ac847ae Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 18 Sep 2022 21:06:02 +0200 Subject: [PATCH 01/55] v5 --- README.md | 45 +- composer.json | 5 +- doc/Migration/4.x-5.x.md | 29 + src/Middleware/ExceptionMiddleware.php | 196 +++++- src/Middleware/RouteMatcherMiddleware.php | 79 +-- .../Exceptions/HttpExceptionInterface.php | 22 - .../Exceptions/MethodNotAllowedException.php | 46 -- .../MissingRouteByNameException.php | 12 +- src/Router/Exceptions/NotFoundException.php | 42 -- .../Exceptions/RouteGenerationException.php | 32 +- src/Router/Exceptions/RouterException.php | 2 +- src/Router/RouteMatcherInterface.php | 3 +- src/Router/{Routes.php => RoutesByName.php} | 2 +- ...nterface.php => RoutesByNameInterface.php} | 2 +- src/Router/UrlGeneratorInterface.php | 6 +- .../Middleware/ExceptionMiddlewareTest.php | 650 ++++++++++++++---- .../Middleware/RouteMatcherMiddlewareTest.php | 159 +---- .../MethodNotAllowedExceptionTest.php | 37 - .../MissingRouteByNameExceptionTest.php | 1 - .../Exceptions/NotFoundExceptionTest.php | 38 - .../RouteGenerationExceptionTest.php | 3 - .../{RoutesTest.php => RoutesByNameTest.php} | 14 +- 22 files changed, 781 insertions(+), 644 deletions(-) create mode 100644 doc/Migration/4.x-5.x.md delete mode 100644 src/Router/Exceptions/HttpExceptionInterface.php delete mode 100644 src/Router/Exceptions/MethodNotAllowedException.php delete mode 100644 src/Router/Exceptions/NotFoundException.php rename src/Router/{Routes.php => RoutesByName.php} (94%) rename src/Router/{RoutesInterface.php => RoutesByNameInterface.php} (85%) delete mode 100644 tests/Unit/Router/Exceptions/MethodNotAllowedExceptionTest.php delete mode 100644 tests/Unit/Router/Exceptions/NotFoundExceptionTest.php rename tests/Unit/Router/{RoutesTest.php => RoutesByNameTest.php} (75%) diff --git a/README.md b/README.md index a37975c..91c1c27 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,15 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements * php: ^8.0 - * [psr/container][20]: ^1.0|^2.0 - * [psr/http-factory][21]: ^1.0.1 - * [psr/http-message-implementation][22]: ^1.0 - * [psr/http-message][23]: ^1.0.1 - * [psr/http-server-handler][24]: ^1.0.1 - * [psr/http-server-middleware][25]: ^1.0.1 - * [psr/log][25]: ^1.1 + * [chubbyphp/chubbyphp-http-exception][20]: ^1.0 + * [fig/http-message-util][21]: ^1.1.5 + * [psr/container][22]: ^1.0|^2.0 + * [psr/http-factory][23]: ^1.0.1 + * [psr/http-message][24]: ^1.0.1 + * [psr/http-message-implementation][25]: ^1.0 + * [psr/http-server-handler][26]: ^1.0.1 + * [psr/http-server-middleware][27]: ^1.0.1 + * [psr/log][28]: ^1.1 ## Suggest @@ -51,7 +53,7 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi Any Router which implements `Chubbyphp\Framework\Router\RouteMatcherInterface` can be used. - * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^1.3.1 + * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.0 ### PSR 7 / PSR 17 @@ -67,8 +69,8 @@ Any Router which implements `Chubbyphp\Framework\Router\RouteMatcherInterface` c Through [Composer](http://getcomposer.org) as [chubbyphp/chubbyphp-framework][60]. ```bash -composer require chubbyphp/chubbyphp-framework "^4.2" \ - chubbyphp/chubbyphp-framework-router-fastroute "^1.3.1" \ +composer require chubbyphp/chubbyphp-framework "^5.0" \ + chubbyphp/chubbyphp-framework-router-fastroute "^2.0" \ slim/psr7 "^1.5" ``` @@ -87,7 +89,7 @@ use Chubbyphp\Framework\Middleware\RouteMatcherMiddleware; use Chubbyphp\Framework\RequestHandler\CallbackRequestHandler; use Chubbyphp\Framework\Router\FastRoute\RouteMatcher; use Chubbyphp\Framework\Router\Route; -use Chubbyphp\Framework\Router\Routes; +use Chubbyphp\Framework\Router\RoutesByName; use Psr\Http\Message\ServerRequestInterface; use Slim\Psr7\Factory\ResponseFactory; use Slim\Psr7\Factory\ServerRequestFactory; @@ -98,7 +100,7 @@ $responseFactory = new ResponseFactory(); $app = new Application([ new ExceptionMiddleware($responseFactory, true), - new RouteMatcherMiddleware(new RouteMatcher(new Routes([ + new RouteMatcherMiddleware(new RouteMatcher(new RoutesByName([ Route::get('/hello/{name:[a-z]+}', 'hello', new CallbackRequestHandler( static function (ServerRequestInterface $request) use ($responseFactory) { $response = $responseFactory->createResponse(); @@ -156,6 +158,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); ## Migration + * [4.x to 5.x][213] * [3.x to 4.x][212] * [2.x to 3.x][211] * [1.x to 2.x][210] @@ -178,13 +181,15 @@ Dominik Zogg 2022 [15]: https://travis-ci.org/chubbyphp/chubbyphp-framework -[20]: https://packagist.org/packages/psr/container -[21]: https://packagist.org/packages/psr/http-factory -[22]: https://packagist.org/packages/psr/http-message-implementation -[23]: https://packagist.org/packages/psr/http-message -[24]: https://packagist.org/packages/psr/http-server-handler -[25]: https://packagist.org/packages/psr/http-server-middleware -[26]: https://packagist.org/packages/psr/log +[20]: https://packagist.org/packages/chubbyphp/chubbyphp-http-exception +[21]: https://packagist.org/packages/fig/http-message-util +[22]: https://packagist.org/packages/psr/container +[23]: https://packagist.org/packages/psr/http-factory +[24]: https://packagist.org/packages/psr/http-message +[25]: https://packagist.org/packages/psr/http-message-implementation +[26]: https://packagist.org/packages/psr/http-server-handler +[27]: https://packagist.org/packages/psr/http-server-middleware +[28]: https://packagist.org/packages/psr/log [30]: https://github.com/chubbyphp/chubbyphp-framework-router-fastroute#usage @@ -231,4 +236,6 @@ Dominik Zogg 2022 [210]: doc/Migration/1.x-2.x.md [211]: doc/Migration/2.x-3.x.md [212]: doc/Migration/3.x-4.x.md +[213]: doc/Migration/4.x-5.x.md + [219]: doc/Migration/Slim-Chubbyphp.md diff --git a/composer.json b/composer.json index 513b544..b8421ad 100644 --- a/composer.json +++ b/composer.json @@ -23,11 +23,12 @@ ], "require": { "php": "^8.0", + "chubbyphp/chubbyphp-http-exception": "^1.0", "fig/http-message-util": "^1.1.5", "psr/container": "^1.0|^2.0", "psr/http-factory": "^1.0.1", - "psr/http-message-implementation": "^1.0", "psr/http-message": "^1.0.1", + "psr/http-message-implementation": "^1.0", "psr/http-server-handler": "^1.0.1", "psr/http-server-middleware": "^1.0.1", "psr/log": "^1.1.4|^2.0|^3.0" @@ -64,7 +65,7 @@ }, "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "5.0-dev" } }, "scripts": { diff --git a/doc/Migration/4.x-5.x.md b/doc/Migration/4.x-5.x.md new file mode 100644 index 0000000..07c9b0a --- /dev/null +++ b/doc/Migration/4.x-5.x.md @@ -0,0 +1,29 @@ +# 4.x to 5.x + +## Middleware + +### ExceptionMiddleware + +Makes use of [chubbyphp/chubbyphp-http-exception][1] for catching http exceptions. +If you use a custom exception handler, please make sure you also catch [chubbyphp/chubbyphp-http-exception][1]. + +### RouteMatcherMiddleware + +Does not catch exceptions anymore, this will be done by the ExceptionMiddleware. + +## Routing + +### HttpExceptionInterface + +Replaced by [chubbyphp/chubbyphp-http-exception][1] + +### MethodNotAllowedException + +Replaced by [chubbyphp/chubbyphp-http-exception][1] + +### NotFoundException + +Replaced by [chubbyphp/chubbyphp-http-exception][1] + + +[1]: https://packagist.org/packages/chubbyphp/chubbyphp-http-exception diff --git a/src/Middleware/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index 9bf54bd..696f8d6 100644 --- a/src/Middleware/ExceptionMiddleware.php +++ b/src/Middleware/ExceptionMiddleware.php @@ -4,6 +4,8 @@ namespace Chubbyphp\Framework\Middleware; +use Chubbyphp\HttpException\HttpException; +use Chubbyphp\HttpException\HttpExceptionInterface; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -15,40 +17,148 @@ final class ExceptionMiddleware implements MiddlewareInterface { private const HTML = <<<'EOT' + - %s + __TITLE__ - %s +
+
+
__STATUS__
+
+ __TITLE____BODY__ +
+
+
EOT; @@ -74,24 +184,44 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface private function handleException(\Throwable $exception): ResponseInterface { - $exceptionsData = $this->toExceptionArray($exception); + $exception = $exception instanceof HttpExceptionInterface ? $exception : HttpException::createInternalServerError([ + 'detail' => 'A website error has occurred. Sorry for the temporary inconvenience.', + ], $exception); - $this->logger->error('Exception', ['exceptions' => $exceptionsData]); + $data = $exception->jsonSerialize(); - $body = '

Application Error

' - .'

A website error has occurred. Sorry for the temporary inconvenience.

'; + $logMethod = $data['status'] < 500 ? 'info' : 'error'; - if ($this->debug) { - $body .= $this->addDebugToBody($exceptionsData); - } + $exceptions = $this->toExceptionsArray($exception); + + $this->logger->{$logMethod}('Http Exception', [ + 'data' => $data, + 'exceptions' => $exceptions, + ]); + + $lines = [ + ...(isset($data['detail']) ? ['

'.$data['detail'].'

'] : []), + ...(isset($data['instance']) ? ['

'.$data['instance'].'

'] : []), + ...($this->debug ? [$this->addDebugToBody($exceptions)] : []), + ]; - $response = $this->responseFactory->createResponse(500); + $response = $this->responseFactory->createResponse($data['status']); $response = $response->withHeader('Content-Type', 'text/html'); - $response->getBody()->write(sprintf( - self::HTML, - 'Application Error', - $body - )); + $response->getBody()->write( + str_replace( + '__STATUS__', + (string) $data['status'], + str_replace( + '__TITLE__', + $data['title'], + str_replace( + '__BODY__', + implode('', $lines), + self::HTML + ) + ) + ) + ); return $response; } @@ -99,7 +229,7 @@ private function handleException(\Throwable $exception): ResponseInterface /** * @return array> */ - private function toExceptionArray(\Throwable $exception): array + private function toExceptionsArray(\Throwable $exception): array { $exceptions = []; do { @@ -121,12 +251,12 @@ private function toExceptionArray(\Throwable $exception): array */ private function addDebugToBody(array $exceptionsData): string { - $body = '

Details

'; + $body = '
'; foreach ($exceptionsData as $exceptionData) { - $body .= '
'; + $body .= '
'; foreach ($exceptionData as $key => $value) { $body .= sprintf( - '
%s
%s
', + '
%s
%s
', ucfirst($key), nl2br((string) $value) ); diff --git a/src/Middleware/RouteMatcherMiddleware.php b/src/Middleware/RouteMatcherMiddleware.php index 43475a1..8502d2c 100644 --- a/src/Middleware/RouteMatcherMiddleware.php +++ b/src/Middleware/RouteMatcherMiddleware.php @@ -4,75 +4,21 @@ namespace Chubbyphp\Framework\Middleware; -use Chubbyphp\Framework\Router\Exceptions\HttpExceptionInterface; use Chubbyphp\Framework\Router\RouteMatcherInterface; -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 Psr\Log\NullLogger; final class RouteMatcherMiddleware implements MiddlewareInterface { - private const HTML = <<<'EOT' - - - - %s - - - - %s - - - EOT; - - private LoggerInterface $logger; - - public function __construct( - private RouteMatcherInterface $routeMatcher, - private ResponseFactoryInterface $responseFactory, - ?LoggerInterface $logger = null - ) { - $this->logger = $logger ?? new NullLogger(); + public function __construct(private RouteMatcherInterface $routeMatcher) + { } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { - try { - $route = $this->routeMatcher->match($request); - } catch (HttpExceptionInterface $routerException) { - return $this->routeExceptionResponse($routerException); - } - + $route = $this->routeMatcher->match($request); $request = $request->withAttribute('route', $route); foreach ($route->getAttributes() as $attribute => $value) { @@ -81,23 +27,4 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return $handler->handle($request); } - - private function routeExceptionResponse(HttpExceptionInterface $routerException): ResponseInterface - { - $this->logger->info('Route exception', [ - 'title' => $routerException->getTitle(), - 'message' => $routerException->getMessage(), - 'code' => $routerException->getCode(), - ]); - - $response = $this->responseFactory->createResponse($routerException->getCode()); - $response = $response->withHeader('Content-Type', 'text/html'); - $response->getBody()->write(sprintf( - self::HTML, - $routerException->getTitle(), - '

'.$routerException->getTitle().'

'.'

'.$routerException->getMessage().'

' - )); - - return $response; - } } diff --git a/src/Router/Exceptions/HttpExceptionInterface.php b/src/Router/Exceptions/HttpExceptionInterface.php deleted file mode 100644 index e690ddf..0000000 --- a/src/Router/Exceptions/HttpExceptionInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - $methods - */ - public static function create(string $path, string $method, array $methods): self - { - $self = new self(sprintf( - 'Method "%s" at path "%s" is not allowed. Must be one of: "%s"', - $method, - $path, - implode('", "', $methods) - ), StatusCode::STATUS_METHOD_NOT_ALLOWED); - $self->type = 'https://tools.ietf.org/html/rfc7231#section-6.5.5'; - $self->title = 'Method not allowed'; - - return $self; - } - - public function getType(): string - { - return $this->type; - } - - public function getTitle(): string - { - return $this->title; - } -} diff --git a/src/Router/Exceptions/MissingRouteByNameException.php b/src/Router/Exceptions/MissingRouteByNameException.php index a6ed851..0ec03ce 100644 --- a/src/Router/Exceptions/MissingRouteByNameException.php +++ b/src/Router/Exceptions/MissingRouteByNameException.php @@ -6,8 +6,6 @@ final class MissingRouteByNameException extends RouterException { - private string $name; - private function __construct(string $message, int $code, ?\Throwable $previous = null) { parent::__construct($message, $code, $previous); @@ -15,14 +13,6 @@ private function __construct(string $message, int $code, ?\Throwable $previous = public static function create(string $name): self { - $self = new self(sprintf('Missing route: "%s"', $name), 2); - $self->name = $name; - - return $self; - } - - public function getName(): string - { - return $this->name; + return new self(sprintf('Missing route: "%s"', $name), 2); } } diff --git a/src/Router/Exceptions/NotFoundException.php b/src/Router/Exceptions/NotFoundException.php deleted file mode 100644 index e544734..0000000 --- a/src/Router/Exceptions/NotFoundException.php +++ /dev/null @@ -1,42 +0,0 @@ -type = 'https://tools.ietf.org/html/rfc7231#section-6.5.4'; - $self->title = 'Page not found'; - - return $self; - } - - public function getType(): string - { - return $this->type; - } - - public function getTitle(): string - { - return $this->title; - } -} diff --git a/src/Router/Exceptions/RouteGenerationException.php b/src/Router/Exceptions/RouteGenerationException.php index 4d2b132..7ea647d 100644 --- a/src/Router/Exceptions/RouteGenerationException.php +++ b/src/Router/Exceptions/RouteGenerationException.php @@ -6,13 +6,6 @@ final class RouteGenerationException extends RouterException { - private string $name; - - private string $path; - - /** @var array */ - private array $attributes; - private function __construct(string $message, int $code, ?\Throwable $previous = null) { parent::__construct($message, $code, $previous); @@ -23,35 +16,12 @@ private function __construct(string $message, int $code, ?\Throwable $previous = */ public static function create(string $name, string $path, array $attributes, ?\Throwable $previous = null): self { - $self = new self(sprintf( + return new self(sprintf( 'Route generation for route "%s" with path "%s" with attributes "%s" failed.%s', $name, $path, json_encode([] !== $attributes ? $attributes : new \stdClass(), JSON_THROW_ON_ERROR), null !== $previous ? ' '.$previous->getMessage() : '', ), 3, $previous); - $self->name = $name; - $self->path = $path; - $self->attributes = $attributes; - - return $self; - } - - public function getName(): string - { - return $this->name; - } - - public function getPath(): string - { - return $this->path; - } - - /** - * @return array - */ - public function getAttributes(): array - { - return $this->attributes; } } diff --git a/src/Router/Exceptions/RouterException.php b/src/Router/Exceptions/RouterException.php index 599dfe6..a6aa337 100644 --- a/src/Router/Exceptions/RouterException.php +++ b/src/Router/Exceptions/RouterException.php @@ -4,6 +4,6 @@ namespace Chubbyphp\Framework\Router\Exceptions; -abstract class RouterException extends \RuntimeException +abstract class RouterException extends \LogicException { } diff --git a/src/Router/RouteMatcherInterface.php b/src/Router/RouteMatcherInterface.php index bf4a965..c47b00f 100644 --- a/src/Router/RouteMatcherInterface.php +++ b/src/Router/RouteMatcherInterface.php @@ -5,12 +5,13 @@ namespace Chubbyphp\Framework\Router; use Chubbyphp\Framework\Router\Exceptions\RouterException; +use Chubbyphp\HttpException\HttpException; use Psr\Http\Message\ServerRequestInterface; interface RouteMatcherInterface { /** - * @throws RouterException + * @throws HttpException|RouterException */ public function match(ServerRequestInterface $request): RouteInterface; } diff --git a/src/Router/Routes.php b/src/Router/RoutesByName.php similarity index 94% rename from src/Router/Routes.php rename to src/Router/RoutesByName.php index ee3a4ea..92e9a72 100644 --- a/src/Router/Routes.php +++ b/src/Router/RoutesByName.php @@ -4,7 +4,7 @@ namespace Chubbyphp\Framework\Router; -final class Routes implements RoutesInterface +final class RoutesByName implements RoutesByNameInterface { /** * @var array diff --git a/src/Router/RoutesInterface.php b/src/Router/RoutesByNameInterface.php similarity index 85% rename from src/Router/RoutesInterface.php rename to src/Router/RoutesByNameInterface.php index e9f1a96..8658e77 100644 --- a/src/Router/RoutesInterface.php +++ b/src/Router/RoutesByNameInterface.php @@ -4,7 +4,7 @@ namespace Chubbyphp\Framework\Router; -interface RoutesInterface +interface RoutesByNameInterface { /** * @return array diff --git a/src/Router/UrlGeneratorInterface.php b/src/Router/UrlGeneratorInterface.php index 0f39155..db4a167 100644 --- a/src/Router/UrlGeneratorInterface.php +++ b/src/Router/UrlGeneratorInterface.php @@ -18,8 +18,8 @@ interface UrlGeneratorInterface public function generateUrl( ServerRequestInterface $request, string $name, - array $attributes = [], - array $queryParams = [] + array $attributes, + array $queryParams, ): string; /** @@ -28,5 +28,5 @@ public function generateUrl( * * @throws RouterException */ - public function generatePath(string $name, array $attributes = [], array $queryParams = []): string; + public function generatePath(string $name, array $attributes, array $queryParams): string; } diff --git a/tests/Unit/Middleware/ExceptionMiddlewareTest.php b/tests/Unit/Middleware/ExceptionMiddlewareTest.php index a12b6f6..15e66e7 100644 --- a/tests/Unit/Middleware/ExceptionMiddlewareTest.php +++ b/tests/Unit/Middleware/ExceptionMiddlewareTest.php @@ -5,6 +5,7 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\ExceptionMiddleware; +use Chubbyphp\HttpException\HttpException; use Chubbyphp\Mock\Argument\ArgumentCallback; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; @@ -47,48 +48,159 @@ public function testProcess(): void self::assertSame($response, $middleware->process($request, $handler)); } - public function testProcessWithExceptionWithoutDebugWithoutLogger(): void + public function testProcessWithClientHttpExceptionWithoutLogger(): void { - $exception = new \RuntimeException('runtime exception', 418, new \LogicException('logic exception', 42)); + $httpException = HttpException::createImateapot([ + 'key1' => 'value1', + 'key2' => 'value2', + ], new \LogicException('logic exception', 42)); /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class); $expectedBody = <<<'EOT' + - Application Error + I'm a teapot -

Application Error

A website error has occurred. Sorry for the temporary inconvenience.

+
+
+
418
+
+ I'm a teapot +
+
+
EOT; @@ -106,12 +218,12 @@ public function testProcessWithExceptionWithoutDebugWithoutLogger(): void /** @var MockObject|RequestHandlerInterface $handler */ $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willThrowException($exception), + Call::create('handle')->with($request)->willThrowException($httpException), ]); /** @var MockObject|ResponseFactoryInterface $responseFactory */ $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(500, '')->willReturn($response), + Call::create('createResponse')->with(418, '')->willReturn($response), ]); $middleware = new ExceptionMiddleware($responseFactory); @@ -119,48 +231,159 @@ public function testProcessWithExceptionWithoutDebugWithoutLogger(): void self::assertSame($response, $middleware->process($request, $handler)); } - public function testProcessWithExceptionWithoutDebugWithLogger(): void + public function testProcessWithClientHttpExceptionWithLogger(): void { - $exception = new \RuntimeException('runtime exception', 418, new \LogicException('logic exception', 42)); + $httpException = HttpException::createNotFound([ + 'detail' => 'Could not found route "/unknown"', + 'instance' => 'instance-1234', + ], new \LogicException('logic exception', 42)); /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class); $expectedBody = <<<'EOT' + - Application Error + Not Found -

Application Error

A website error has occurred. Sorry for the temporary inconvenience.

+
+
+
404
+
+ Not Found

Could not found route "/unknown"

instance-1234

+
+
+
EOT; @@ -178,40 +401,49 @@ public function testProcessWithExceptionWithoutDebugWithLogger(): void /** @var MockObject|RequestHandlerInterface $handler */ $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willThrowException($exception), + Call::create('handle')->with($request)->willThrowException($httpException), ]); /** @var MockObject|ResponseFactoryInterface $responseFactory */ $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(500, '')->willReturn($response), + Call::create('createResponse')->with(404, '')->willReturn($response), ]); /** @var LoggerInterface|MockObject $logger */ $logger = $this->getMockByCalls(LoggerInterface::class, [ - Call::create('error')->with( - 'Exception', + Call::create('info')->with( + 'Http Exception', new ArgumentCallback(static function (array $context): void { + self::assertArrayHasKey('data', $context); + $data = $context['data']; + self::assertSame([ + 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.5', + 'status' => 404, + 'title' => 'Not Found', + 'detail' => 'Could not found route "/unknown"', + 'instance' => 'instance-1234', + ], $data); self::assertArrayHasKey('exceptions', $context); $exceptions = $context['exceptions']; self::assertCount(2, $exceptions); - $runtimeException = $exceptions[0]; - self::assertArrayHasKey('message', $runtimeException); - self::assertArrayHasKey('code', $runtimeException); - self::assertArrayHasKey('file', $runtimeException); - self::assertArrayHasKey('line', $runtimeException); - self::assertArrayHasKey('trace', $runtimeException); - self::assertSame('RuntimeException', $runtimeException['class']); - self::assertSame('runtime exception', $runtimeException['message']); - self::assertSame(418, $runtimeException['code']); - $logicException = $exceptions[1]; - self::assertArrayHasKey('message', $logicException); - self::assertArrayHasKey('code', $logicException); - self::assertArrayHasKey('file', $logicException); - self::assertArrayHasKey('line', $logicException); - self::assertArrayHasKey('trace', $logicException); - self::assertSame('LogicException', $logicException['class']); - self::assertSame('logic exception', $logicException['message']); - self::assertSame(42, $logicException['code']); + $exception1 = $exceptions[0]; + self::assertArrayHasKey('message', $exception1); + self::assertArrayHasKey('code', $exception1); + self::assertArrayHasKey('file', $exception1); + self::assertArrayHasKey('line', $exception1); + self::assertArrayHasKey('trace', $exception1); + self::assertSame(HttpException::class, $exception1['class']); + self::assertSame('Not Found', $exception1['message']); + self::assertSame(404, $exception1['code']); + $exception2 = $exceptions[1]; + self::assertArrayHasKey('message', $exception2); + self::assertArrayHasKey('code', $exception2); + self::assertArrayHasKey('file', $exception2); + self::assertArrayHasKey('line', $exception2); + self::assertArrayHasKey('trace', $exception2); + self::assertSame('LogicException', $exception2['class']); + self::assertSame('logic exception', $exception2['message']); + self::assertSame(42, $exception2['code']); }) ), ]); @@ -221,36 +453,163 @@ public function testProcessWithExceptionWithoutDebugWithLogger(): void self::assertSame($response, $middleware->process($request, $handler)); } - public function testProcessWithExceptionWithDebugWithoutLogger(): void + public function testProcessWithServerHttpExceptionWithDebugWithLogger(): void { - $exception = new \RuntimeException('runtime exception', 418, new \LogicException('logic exception', 42)); + $httpException = HttpException::createInternalServerError([], new \LogicException('logic exception', 42)); /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class); + $expectedBody = <<<'EOT' + + + + + Internal Server Error + + + +
+
+
500
+
+ Internal Server Error +
+
+
+ + + EOT; + /** @var MockObject|StreamInterface $responseBody */ $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write') - ->with(new ArgumentCallback(static function (string $html): void { - self::assertStringContainsString( - '

A website error has occurred. Sorry for the temporary inconvenience.

', - $html - ); - self::assertStringContainsString('div class="block"', $html); - self::assertStringContainsString('
Class
', $html); - self::assertStringContainsString('
RuntimeException
', $html); - self::assertStringContainsString('
Message
', $html); - self::assertStringContainsString('
runtime exception
', $html); - self::assertStringContainsString('
Code
', $html); - self::assertStringContainsString('
418
', $html); - - self::assertStringContainsString('
Class
', $html); - self::assertStringContainsString('
LogicException
', $html); - self::assertStringContainsString('
Message
', $html); - self::assertStringContainsString('
logic exception
', $html); - self::assertStringContainsString('
Code
', $html); - self::assertStringContainsString('
42
', $html); - })), + Call::create('write')->with($expectedBody), ]); /** @var MockObject|ResponseInterface $response */ @@ -261,7 +620,7 @@ public function testProcessWithExceptionWithDebugWithoutLogger(): void /** @var MockObject|RequestHandlerInterface $handler */ $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willThrowException($exception), + Call::create('handle')->with($request)->willThrowException($httpException), ]); /** @var MockObject|ResponseFactoryInterface $responseFactory */ @@ -269,7 +628,44 @@ public function testProcessWithExceptionWithDebugWithoutLogger(): void Call::create('createResponse')->with(500, '')->willReturn($response), ]); - $middleware = new ExceptionMiddleware($responseFactory, true); + /** @var LoggerInterface|MockObject $logger */ + $logger = $this->getMockByCalls(LoggerInterface::class, [ + Call::create('error')->with( + 'Http Exception', + new ArgumentCallback(static function (array $context): void { + self::assertArrayHasKey('data', $context); + $data = $context['data']; + self::assertSame([ + 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', + 'status' => 500, + 'title' => 'Internal Server Error', + ], $data); + self::assertArrayHasKey('exceptions', $context); + $exceptions = $context['exceptions']; + self::assertCount(2, $exceptions); + $runtimeException = $exceptions[0]; + self::assertArrayHasKey('message', $runtimeException); + self::assertArrayHasKey('code', $runtimeException); + self::assertArrayHasKey('file', $runtimeException); + self::assertArrayHasKey('line', $runtimeException); + self::assertArrayHasKey('trace', $runtimeException); + self::assertSame(HttpException::class, $runtimeException['class']); + self::assertSame('Internal Server Error', $runtimeException['message']); + self::assertSame(500, $runtimeException['code']); + $logicException = $exceptions[1]; + self::assertArrayHasKey('message', $logicException); + self::assertArrayHasKey('code', $logicException); + self::assertArrayHasKey('file', $logicException); + self::assertArrayHasKey('line', $logicException); + self::assertArrayHasKey('trace', $logicException); + self::assertSame('LogicException', $logicException['class']); + self::assertSame('logic exception', $logicException['message']); + self::assertSame(42, $logicException['code']); + }) + ), + ]); + + $middleware = new ExceptionMiddleware($responseFactory, false, $logger); self::assertSame($response, $middleware->process($request, $handler)); } @@ -289,20 +685,19 @@ public function testProcessWithExceptionWithDebugWithLogger(): void '

A website error has occurred. Sorry for the temporary inconvenience.

', $html ); - self::assertStringContainsString('div class="block"', $html); - self::assertStringContainsString('
Class
', $html); - self::assertStringContainsString('
RuntimeException
', $html); - self::assertStringContainsString('
Message
', $html); - self::assertStringContainsString('
runtime exception
', $html); - self::assertStringContainsString('
Code
', $html); - self::assertStringContainsString('
418
', $html); - - self::assertStringContainsString('
Class
', $html); - self::assertStringContainsString('
LogicException
', $html); - self::assertStringContainsString('
Message
', $html); - self::assertStringContainsString('
logic exception
', $html); - self::assertStringContainsString('
Code
', $html); - self::assertStringContainsString('
42
', $html); + self::assertStringContainsString('
Class
', $html); + self::assertStringContainsString('
RuntimeException
', $html); + self::assertStringContainsString('
Message
', $html); + self::assertStringContainsString('
runtime exception
', $html); + self::assertStringContainsString('
Code
', $html); + self::assertStringContainsString('
418
', $html); + + self::assertStringContainsString('
Class
', $html); + self::assertStringContainsString('
LogicException
', $html); + self::assertStringContainsString('
Message
', $html); + self::assertStringContainsString('
logic exception
', $html); + self::assertStringContainsString('
Code
', $html); + self::assertStringContainsString('
42
', $html); })), ]); @@ -325,29 +720,46 @@ public function testProcessWithExceptionWithDebugWithLogger(): void /** @var LoggerInterface|MockObject $logger */ $logger = $this->getMockByCalls(LoggerInterface::class, [ Call::create('error')->with( - 'Exception', + 'Http Exception', new ArgumentCallback(static function (array $context): void { + self::assertArrayHasKey('data', $context); + $data = $context['data']; + self::assertSame([ + 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', + 'status' => 500, + 'title' => 'Internal Server Error', + 'detail' => 'A website error has occurred. Sorry for the temporary inconvenience.', + ], $data); self::assertArrayHasKey('exceptions', $context); $exceptions = $context['exceptions']; - self::assertCount(2, $exceptions); - $runtimeException = $exceptions[0]; - self::assertArrayHasKey('message', $runtimeException); - self::assertArrayHasKey('code', $runtimeException); - self::assertArrayHasKey('file', $runtimeException); - self::assertArrayHasKey('line', $runtimeException); - self::assertArrayHasKey('trace', $runtimeException); - self::assertSame('RuntimeException', $runtimeException['class']); - self::assertSame('runtime exception', $runtimeException['message']); - self::assertSame(418, $runtimeException['code']); - $logicException = $exceptions[1]; - self::assertArrayHasKey('message', $logicException); - self::assertArrayHasKey('code', $logicException); - self::assertArrayHasKey('file', $logicException); - self::assertArrayHasKey('line', $logicException); - self::assertArrayHasKey('trace', $logicException); - self::assertSame('LogicException', $logicException['class']); - self::assertSame('logic exception', $logicException['message']); - self::assertSame(42, $logicException['code']); + self::assertCount(3, $exceptions); + $exception1 = $exceptions[0]; + self::assertArrayHasKey('message', $exception1); + self::assertArrayHasKey('code', $exception1); + self::assertArrayHasKey('file', $exception1); + self::assertArrayHasKey('line', $exception1); + self::assertArrayHasKey('trace', $exception1); + self::assertSame(HttpException::class, $exception1['class']); + self::assertSame('Internal Server Error', $exception1['message']); + self::assertSame(500, $exception1['code']); + $exception2 = $exceptions[1]; + self::assertArrayHasKey('message', $exception2); + self::assertArrayHasKey('code', $exception2); + self::assertArrayHasKey('file', $exception2); + self::assertArrayHasKey('line', $exception2); + self::assertArrayHasKey('trace', $exception2); + self::assertSame('RuntimeException', $exception2['class']); + self::assertSame('runtime exception', $exception2['message']); + self::assertSame(418, $exception2['code']); + $exception3 = $exceptions[2]; + self::assertArrayHasKey('message', $exception3); + self::assertArrayHasKey('code', $exception3); + self::assertArrayHasKey('file', $exception3); + self::assertArrayHasKey('line', $exception3); + self::assertArrayHasKey('trace', $exception3); + self::assertSame('LogicException', $exception3['class']); + self::assertSame('logic exception', $exception3['message']); + self::assertSame(42, $exception3['code']); }) ), ]); diff --git a/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php b/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php index c30f560..b9f64d5 100644 --- a/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php +++ b/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php @@ -5,19 +5,16 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\RouteMatcherMiddleware; -use Chubbyphp\Framework\Router\Exceptions\NotFoundException; use Chubbyphp\Framework\Router\RouteInterface; use Chubbyphp\Framework\Router\RouteMatcherInterface; +use Chubbyphp\HttpException\HttpException; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\StreamInterface; use Psr\Http\Server\RequestHandlerInterface; -use Psr\Log\LoggerInterface; /** * @covers \Chubbyphp\Framework\Middleware\RouteMatcherMiddleware @@ -54,170 +51,32 @@ public function testProcess(): void Call::create('match')->with($request)->willReturn($route), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); - - $middleware = new RouteMatcherMiddleware($router, $responseFactory); + $middleware = new RouteMatcherMiddleware($router); self::assertSame($response, $middleware->process($request, $handler)); } - public function testProcessMissingRouteWithoutLogger(): void + public function testProcessWithException(): void { - $routerException = NotFoundException::create('/'); - - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); - - $expectedBody = <<<'EOT' - - - - Page not found - - - -

Page not found

The page "/" you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly.

- - - EOT; - - /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write')->with($expectedBody), - ]); - - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class, [ - Call::create('withHeader')->with('Content-Type', 'text/html')->willReturnSelf(), - Call::create('getBody')->with()->willReturn($responseBody), - ]); - - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); - - /** @var MockObject|RouteMatcherInterface $router */ - $router = $this->getMockByCalls(RouteMatcherInterface::class, [ - Call::create('match')->with($request)->willThrowException($routerException), - ]); - - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(404, '')->willReturn($response), + $httpException = HttpException::createNotFound([ + 'detail' => 'The page "/" you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly.', ]); - $middleware = new RouteMatcherMiddleware($router, $responseFactory); - - self::assertSame($response, $middleware->process($request, $handler)); - } - - public function testProcessMissingRouteWithLogger(): void - { - $routerException = NotFoundException::create('/'); + $this->expectExceptionObject($httpException); /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class); - $expectedBody = <<<'EOT' - - - - Page not found - - - -

Page not found

The page "/" you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly.

- - - EOT; - - /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write')->with($expectedBody), - ]); - - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class, [ - Call::create('withHeader')->with('Content-Type', 'text/html')->willReturnSelf(), - Call::create('getBody')->with()->willReturn($responseBody), - ]); - /** @var MockObject|RequestHandlerInterface $handler */ $handler = $this->getMockByCalls(RequestHandlerInterface::class); /** @var MockObject|RouteMatcherInterface $router */ $router = $this->getMockByCalls(RouteMatcherInterface::class, [ - Call::create('match')->with($request)->willThrowException($routerException), - ]); - - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(404, '')->willReturn($response), - ]); - - /** @var LoggerInterface|MockObject $logger */ - $logger = $this->getMockByCalls(LoggerInterface::class, [ - Call::create('info')->with('Route exception', [ - 'title' => $routerException->getTitle(), - 'message' => $routerException->getMessage(), - 'code' => $routerException->getCode(), - ]), + Call::create('match')->with($request)->willThrowException($httpException), ]); - $middleware = new RouteMatcherMiddleware($router, $responseFactory, $logger); + $middleware = new RouteMatcherMiddleware($router); - self::assertSame($response, $middleware->process($request, $handler)); + $middleware->process($request, $handler); } } diff --git a/tests/Unit/Router/Exceptions/MethodNotAllowedExceptionTest.php b/tests/Unit/Router/Exceptions/MethodNotAllowedExceptionTest.php deleted file mode 100644 index 9504581..0000000 --- a/tests/Unit/Router/Exceptions/MethodNotAllowedExceptionTest.php +++ /dev/null @@ -1,37 +0,0 @@ -expectException(\Error::class); - $this->expectExceptionMessage('Call to private'); - - new MethodNotAllowedException('test', 0); - } - - public function testCreare(): void - { - $exception = MethodNotAllowedException::create('/', 'GET', ['POST', 'PUT']); - - self::assertSame('https://tools.ietf.org/html/rfc7231#section-6.5.5', $exception->getType()); - self::assertSame('Method not allowed', $exception->getTitle()); - self::assertSame( - 'Method "GET" at path "/" is not allowed. Must be one of: "POST", "PUT"', - $exception->getMessage() - ); - self::assertSame(405, $exception->getCode()); - } -} diff --git a/tests/Unit/Router/Exceptions/MissingRouteByNameExceptionTest.php b/tests/Unit/Router/Exceptions/MissingRouteByNameExceptionTest.php index 7385740..a3670f4 100644 --- a/tests/Unit/Router/Exceptions/MissingRouteByNameExceptionTest.php +++ b/tests/Unit/Router/Exceptions/MissingRouteByNameExceptionTest.php @@ -28,6 +28,5 @@ public function testCreate(): void self::assertSame('Missing route: "name"', $exception->getMessage()); self::assertSame(2, $exception->getCode()); - self::assertSame('name', $exception->getName()); } } diff --git a/tests/Unit/Router/Exceptions/NotFoundExceptionTest.php b/tests/Unit/Router/Exceptions/NotFoundExceptionTest.php deleted file mode 100644 index 94b716c..0000000 --- a/tests/Unit/Router/Exceptions/NotFoundExceptionTest.php +++ /dev/null @@ -1,38 +0,0 @@ -expectException(\Error::class); - $this->expectExceptionMessage('Call to private'); - - new NotFoundException('test', 0); - } - - public function testCreate(): void - { - $exception = NotFoundException::create('/'); - - self::assertSame('https://tools.ietf.org/html/rfc7231#section-6.5.4', $exception->getType()); - self::assertSame('Page not found', $exception->getTitle()); - self::assertSame( - 'The page "/" you are looking for could not be found.' - .' Check the address bar to ensure your URL is spelled correctly.', - $exception->getMessage() - ); - self::assertSame(404, $exception->getCode()); - } -} diff --git a/tests/Unit/Router/Exceptions/RouteGenerationExceptionTest.php b/tests/Unit/Router/Exceptions/RouteGenerationExceptionTest.php index eafdcca..12003a0 100644 --- a/tests/Unit/Router/Exceptions/RouteGenerationExceptionTest.php +++ b/tests/Unit/Router/Exceptions/RouteGenerationExceptionTest.php @@ -32,9 +32,6 @@ public function testCreate(): void $exception->getMessage() ); self::assertSame(3, $exception->getCode()); - self::assertSame('name', $exception->getName()); - self::assertSame('/name/{name}', $exception->getPath()); - self::assertSame(['name' => 'name'], $exception->getAttributes()); self::assertSame($previous, $exception->getPrevious()); } diff --git a/tests/Unit/Router/RoutesTest.php b/tests/Unit/Router/RoutesByNameTest.php similarity index 75% rename from tests/Unit/Router/RoutesTest.php rename to tests/Unit/Router/RoutesByNameTest.php index a7b3cc0..82fed5a 100644 --- a/tests/Unit/Router/RoutesTest.php +++ b/tests/Unit/Router/RoutesByNameTest.php @@ -5,17 +5,17 @@ namespace Chubbyphp\Tests\Framework\Unit\Router; use Chubbyphp\Framework\Router\RouteInterface; -use Chubbyphp\Framework\Router\Routes; +use Chubbyphp\Framework\Router\RoutesByName; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; use PHPUnit\Framework\TestCase; /** - * @covers \Chubbyphp\Framework\Router\Routes + * @covers \Chubbyphp\Framework\Router\RoutesByName * * @internal */ -final class RoutesTest extends TestCase +final class RoutesByNameTest extends TestCase { use MockByCallsTrait; @@ -23,13 +23,13 @@ public function testWithWrongType(): void { $this->expectException(\TypeError::class); $this->expectExceptionMessage( - 'Chubbyphp\Framework\Router\Routes::__construct() expects parameter 1' + 'Chubbyphp\Framework\Router\RoutesByName::__construct() expects parameter 1' .' at index 0 to be Chubbyphp\Framework\Router\RouteInterface, stdClass given' ); $route = new \stdClass(); - new Routes([$route]); + new RoutesByName([$route]); } public function testGetRoutes(): void @@ -42,14 +42,14 @@ public function testGetRoutes(): void Call::create('getName')->with()->willReturn('name2'), ]); - $routes = new Routes([$route1, $route2]); + $routes = new RoutesByName([$route1, $route2]); self::assertSame(['name1' => $route1, 'name2' => $route2], $routes->getRoutesByName()); } public function testWithoutRoutes(): void { - $routes = new Routes([]); + $routes = new RoutesByName([]); self::assertSame([], $routes->getRoutesByName()); } From d9398848de34703ecb1fa23a03862d4596080258 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 25 Sep 2022 19:52:23 +0200 Subject: [PATCH 02/55] Enhance migration doc --- doc/Migration/4.x-5.x.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/Migration/4.x-5.x.md b/doc/Migration/4.x-5.x.md index 07c9b0a..8554afc 100644 --- a/doc/Migration/4.x-5.x.md +++ b/doc/Migration/4.x-5.x.md @@ -25,5 +25,17 @@ Replaced by [chubbyphp/chubbyphp-http-exception][1] Replaced by [chubbyphp/chubbyphp-http-exception][1] +### RouterException + +Extends \LogicException and not \RuntimeException anymore, cause the router exceptions shouldn't be runtime related anymore. + +### Routes + +Renamed to RoutesByName + +### RoutesInterface + +Renamed to RoutesByNameInterface + [1]: https://packagist.org/packages/chubbyphp/chubbyphp-http-exception From 03dddda5b76709ff7d75f6e54d77cea1915cf6a9 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 25 Sep 2022 21:14:52 +0200 Subject: [PATCH 03/55] Revert UrlGeneratorInterface changes --- src/Router/UrlGeneratorInterface.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Router/UrlGeneratorInterface.php b/src/Router/UrlGeneratorInterface.php index db4a167..dfae686 100644 --- a/src/Router/UrlGeneratorInterface.php +++ b/src/Router/UrlGeneratorInterface.php @@ -18,8 +18,8 @@ interface UrlGeneratorInterface public function generateUrl( ServerRequestInterface $request, string $name, - array $attributes, - array $queryParams, + array $attributes = [], + array $queryParams = [], ): string; /** @@ -28,5 +28,5 @@ public function generateUrl( * * @throws RouterException */ - public function generatePath(string $name, array $attributes, array $queryParams): string; + public function generatePath(string $name, array $attributes = [], array $queryParams = []): string; } From aa9388f10b6c8996b6fc55c86380de2d01b87617 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 25 Sep 2022 21:34:03 +0200 Subject: [PATCH 04/55] RouteMatcherMiddleware does not have more than one argument anymore --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91c1c27..e5d3a85 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ $app = new Application([ return $response; } )) - ])), $responseFactory), + ]))), ]); $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); From 74f09e057e52f8e9116d42cf80b415636657f52d Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Wed, 28 Sep 2022 06:35:56 +0200 Subject: [PATCH 05/55] Encorce chubbyphp/chubbyphp-http-exception 1.0.1 --- README.md | 2 +- composer.json | 2 +- tests/Unit/Middleware/ExceptionMiddlewareTest.php | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e5d3a85..6abc2e2 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements * php: ^8.0 - * [chubbyphp/chubbyphp-http-exception][20]: ^1.0 + * [chubbyphp/chubbyphp-http-exception][20]: ^1.0.1 * [fig/http-message-util][21]: ^1.1.5 * [psr/container][22]: ^1.0|^2.0 * [psr/http-factory][23]: ^1.0.1 diff --git a/composer.json b/composer.json index b8421ad..c445054 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ ], "require": { "php": "^8.0", - "chubbyphp/chubbyphp-http-exception": "^1.0", + "chubbyphp/chubbyphp-http-exception": "^1.0.1", "fig/http-message-util": "^1.1.5", "psr/container": "^1.0|^2.0", "psr/http-factory": "^1.0.1", diff --git a/tests/Unit/Middleware/ExceptionMiddlewareTest.php b/tests/Unit/Middleware/ExceptionMiddlewareTest.php index 15e66e7..73af9dc 100644 --- a/tests/Unit/Middleware/ExceptionMiddlewareTest.php +++ b/tests/Unit/Middleware/ExceptionMiddlewareTest.php @@ -639,6 +639,8 @@ public function testProcessWithServerHttpExceptionWithDebugWithLogger(): void 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', 'status' => 500, 'title' => 'Internal Server Error', + 'detail' => null, + 'instance' => null, ], $data); self::assertArrayHasKey('exceptions', $context); $exceptions = $context['exceptions']; @@ -729,6 +731,7 @@ public function testProcessWithExceptionWithDebugWithLogger(): void 'status' => 500, 'title' => 'Internal Server Error', 'detail' => 'A website error has occurred. Sorry for the temporary inconvenience.', + 'instance' => null, ], $data); self::assertArrayHasKey('exceptions', $context); $exceptions = $context['exceptions']; From 3ebb4a856dde0c1c133ff2b553fb7ffdfd07fa53 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 1 Oct 2022 09:59:19 +0200 Subject: [PATCH 06/55] fix some code smells --- src/Emitter/Emitter.php | 16 ++++++++++------ src/Middleware/ExceptionMiddleware.php | 19 ++++++++++++++----- src/Middleware/MiddlewareDispatcher.php | 2 +- src/Middleware/SlimLazyMiddleware.php | 7 +++++-- src/RequestHandler/SlimLazyRequestHandler.php | 7 +++++-- ...issingRouteAttributeOnRequestException.php | 13 ++++++++----- .../Exceptions/RouteGenerationException.php | 18 +++++++++++------- src/Router/Group.php | 8 ++++++-- 8 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/Emitter/Emitter.php b/src/Emitter/Emitter.php index 8b32992..1a5933b 100644 --- a/src/Emitter/Emitter.php +++ b/src/Emitter/Emitter.php @@ -12,12 +12,16 @@ public function emit(ResponseInterface $response): void { $statusCode = $response->getStatusCode(); - header(sprintf( - 'HTTP/%s %s %s', - $response->getProtocolVersion(), - $statusCode, - $response->getReasonPhrase() - ), true, $statusCode); + header( + sprintf( + 'HTTP/%s %s %s', + $response->getProtocolVersion(), + $statusCode, + $response->getReasonPhrase() + ), + true, + $statusCode + ); foreach ($response->getHeaders() as $name => $values) { foreach ($values as $value) { diff --git a/src/Middleware/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index 696f8d6..1c404d3 100644 --- a/src/Middleware/ExceptionMiddleware.php +++ b/src/Middleware/ExceptionMiddleware.php @@ -184,15 +184,13 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface private function handleException(\Throwable $exception): ResponseInterface { - $exception = $exception instanceof HttpExceptionInterface ? $exception : HttpException::createInternalServerError([ - 'detail' => 'A website error has occurred. Sorry for the temporary inconvenience.', - ], $exception); + $httpException = $this->exceptionToHttpException($exception); - $data = $exception->jsonSerialize(); + $data = $httpException->jsonSerialize(); $logMethod = $data['status'] < 500 ? 'info' : 'error'; - $exceptions = $this->toExceptionsArray($exception); + $exceptions = $this->toExceptionsArray($httpException); $this->logger->{$logMethod}('Http Exception', [ 'data' => $data, @@ -226,6 +224,17 @@ private function handleException(\Throwable $exception): ResponseInterface return $response; } + private function exceptionToHttpException(\Throwable $exception): HttpExceptionInterface + { + if ($exception instanceof HttpExceptionInterface) { + return $exception; + } + + return HttpException::createInternalServerError([ + 'detail' => 'A website error has occurred. Sorry for the temporary inconvenience.', + ], $exception); + } + /** * @return array> */ diff --git a/src/Middleware/MiddlewareDispatcher.php b/src/Middleware/MiddlewareDispatcher.php index 03d40b9..4965709 100644 --- a/src/Middleware/MiddlewareDispatcher.php +++ b/src/Middleware/MiddlewareDispatcher.php @@ -21,7 +21,7 @@ public function dispatch( ): ResponseInterface { return array_reduce( array_reverse($middlewares), - static fn ($middlewareHandler, $middleware) => new MiddlewareRequestHandler($middleware, $middlewareHandler), + static fn ($handler, $middleware) => new MiddlewareRequestHandler($middleware, $handler), $handler )->handle($request); } diff --git a/src/Middleware/SlimLazyMiddleware.php b/src/Middleware/SlimLazyMiddleware.php index 832253e..d8763c3 100644 --- a/src/Middleware/SlimLazyMiddleware.php +++ b/src/Middleware/SlimLazyMiddleware.php @@ -13,8 +13,11 @@ final class SlimLazyMiddleware implements MiddlewareInterface { - public function __construct(private ContainerInterface $container, private string $id, private ResponseFactoryInterface $responseFactory) - { + public function __construct( + private ContainerInterface $container, + private string $id, + private ResponseFactoryInterface $responseFactory + ) { } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/src/RequestHandler/SlimLazyRequestHandler.php b/src/RequestHandler/SlimLazyRequestHandler.php index 354c57b..ac6ab36 100644 --- a/src/RequestHandler/SlimLazyRequestHandler.php +++ b/src/RequestHandler/SlimLazyRequestHandler.php @@ -12,8 +12,11 @@ final class SlimLazyRequestHandler implements RequestHandlerInterface { - public function __construct(private ContainerInterface $container, private string $id, private ResponseFactoryInterface $responseFactory) - { + public function __construct( + private ContainerInterface $container, + private string $id, + private ResponseFactoryInterface $responseFactory + ) { } public function handle(ServerRequestInterface $request): ResponseInterface diff --git a/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php b/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php index 29e3070..7a056cc 100644 --- a/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php +++ b/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php @@ -18,10 +18,13 @@ private function __construct(string $message, int $code, ?\Throwable $previous = */ public static function create($route): self { - return new self(sprintf( - 'Request attribute "route" missing or wrong type "%s", please add the "%s" middleware', - get_debug_type($route), - RouteMatcherMiddleware::class - ), 1); + return new self( + sprintf( + 'Request attribute "route" missing or wrong type "%s", please add the "%s" middleware', + get_debug_type($route), + RouteMatcherMiddleware::class + ), + 1 + ); } } diff --git a/src/Router/Exceptions/RouteGenerationException.php b/src/Router/Exceptions/RouteGenerationException.php index 7ea647d..e0481b7 100644 --- a/src/Router/Exceptions/RouteGenerationException.php +++ b/src/Router/Exceptions/RouteGenerationException.php @@ -16,12 +16,16 @@ private function __construct(string $message, int $code, ?\Throwable $previous = */ public static function create(string $name, string $path, array $attributes, ?\Throwable $previous = null): self { - return new self(sprintf( - 'Route generation for route "%s" with path "%s" with attributes "%s" failed.%s', - $name, - $path, - json_encode([] !== $attributes ? $attributes : new \stdClass(), JSON_THROW_ON_ERROR), - null !== $previous ? ' '.$previous->getMessage() : '', - ), 3, $previous); + return new self( + sprintf( + 'Route generation for route "%s" with path "%s" with attributes "%s" failed.%s', + $name, + $path, + json_encode([] !== $attributes ? $attributes : new \stdClass(), JSON_THROW_ON_ERROR), + null !== $previous ? ' '.$previous->getMessage() : '', + ), + 3, + $previous + ); } } diff --git a/src/Router/Group.php b/src/Router/Group.php index efebce0..84ff25e 100644 --- a/src/Router/Group.php +++ b/src/Router/Group.php @@ -24,8 +24,12 @@ final class Group implements GroupInterface * @param array $middlewares * @param array $pathOptions */ - private function __construct(private string $path, array $children = [], array $middlewares = [], private array $pathOptions = []) - { + private function __construct( + private string $path, + array $children = [], + array $middlewares = [], + private array $pathOptions = [] + ) { $this->children = (new Collection($children, [GroupInterface::class, RouteInterface::class]))->toArray(); $this->middlewares = (new Collection($middlewares, [MiddlewareInterface::class]))->toArray(); } From 5fd44d7576b74921f863c4a6eebc8566ef89d8bb Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 1 Oct 2022 10:15:54 +0200 Subject: [PATCH 07/55] Drop php-cs-fixer config overrides --- .php-cs-fixer.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 865b4bc..d81b4d7 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -12,12 +12,6 @@ /** @var array $config */ $config = require __DIR__ . '/vendor/chubbyphp/chubbyphp-dev-helper/phpcs.php'; -unset($config['rules']['final_class']); - -$config['rules']['native_function_invocation'] = false; -$config['rules']['phpdoc_to_param_type'] = ['scalar_types' => false]; -$config['rules']['phpdoc_to_return_type'] = false; - return (new PhpCsFixer\Config) ->setIndent($config['indent']) ->setLineEnding($config['lineEnding']) From 1be49d8ae675c8dd32844337714d13178a67dae9 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 1 Oct 2022 10:27:44 +0200 Subject: [PATCH 08/55] fix another smell --- src/Middleware/SlimCallbackMiddleware.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Middleware/SlimCallbackMiddleware.php b/src/Middleware/SlimCallbackMiddleware.php index b95ab5b..099c5e2 100644 --- a/src/Middleware/SlimCallbackMiddleware.php +++ b/src/Middleware/SlimCallbackMiddleware.php @@ -29,7 +29,11 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return ($this->slimCallable)( $request, $this->getResponse($request), - static fn (ServerRequestInterface $request, ResponseInterface $response) => $handler->handle($request->withAttribute(self::ATTRIBUTE_RESPONSE, $response)) + static function (ServerRequestInterface $request, ResponseInterface $response) use ($handler) { + return $handler->handle( + $request->withAttribute(self::ATTRIBUTE_RESPONSE, $response) + ); + } ); } From e9a859c90b5b3cf4599483ecdcc9b61548536f88 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 2 Oct 2022 19:42:46 +0200 Subject: [PATCH 09/55] exclude *Interface.php --- phpunit.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phpunit.xml b/phpunit.xml index 4ea36f1..4354f8b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -14,6 +14,9 @@ ./src + + ./src + From 545163b01425a62a63e84b318012cc91a54eb20e Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 26 Nov 2022 14:28:20 +0100 Subject: [PATCH 10/55] github actions upgrade --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5b415f..b3ec9ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: composer test uses: docker://chubbyphp/ci-php80:latest env: @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: composer test uses: docker://chubbyphp/ci-php81:latest env: From e0003566332dc0569dd2afa3e7171bc84e6fa704 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Wed, 28 Dec 2022 09:25:54 +0100 Subject: [PATCH 11/55] test against php 8.2 --- .github/workflows/ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3ec9ad..282ff32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,17 @@ jobs: env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} + php82: + name: PHP 8.2 + runs-on: ubuntu-20.04 + steps: + - name: checkout + uses: actions/checkout@v3 + - name: composer test + uses: docker://chubbyphp/ci-php82:latest + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - name: sonarcloud.io uses: sonarsource/sonarcloud-github-action@master env: From 302011585022f1a74e6c8a94f18cecad6a72c44a Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 1 Jan 2023 13:19:23 +0100 Subject: [PATCH 12/55] update sunrise/http-message --- README.md | 7 +++---- composer.json | 2 +- tests/Integration/MiddlewareDispatcherTest.php | 2 +- tests/Integration/RouteMatcherLessTest.php | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6abc2e2..907e808 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,8 @@ Any Router which implements `Chubbyphp\Framework\Router\RouteMatcherInterface` c * [guzzlehttp/psr7][41]: ^1.4.2 (with [http-interop/http-factory-guzzle][42]: ^1.0) * [nyholm/psr7][43]: ^1.0 * [slim/psr7][44]: ^0.5|^1.0 - * [sunrise/http-message][45]: ^1.0 (with [sunrise/http-factory][46]: ^1.0) - * [laminas/laminas-diactoros][47]: ^2.0 + * [sunrise/http-message][45]: ^3.0 + * [laminas/laminas-diactoros][46]: ^2.0 ## Installation @@ -199,8 +199,7 @@ Dominik Zogg 2022 [43]: https://packagist.org/packages/nyholm/psr7 [44]: https://packagist.org/packages/slim/psr7 [45]: https://packagist.org/packages/sunrise/http-message -[46]: https://packagist.org/packages/sunrise/http-factory -[47]: https://packagist.org/packages/laminas/laminas-diactoros +[46]: https://packagist.org/packages/laminas/laminas-diactoros [60]: https://packagist.org/packages/chubbyphp/chubbyphp-framework diff --git a/composer.json b/composer.json index c445054..8b70102 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ "phpstan/phpstan": "^1.4.6", "phpunit/phpunit": "^9.5.16", "slim/psr7": "^0.5|^1.0", - "sunrise/http-factory": "^1.0.4" + "sunrise/http-message": "^3.0" }, "autoload": { "psr-4": { "Chubbyphp\\Framework\\": "src/" } diff --git a/tests/Integration/MiddlewareDispatcherTest.php b/tests/Integration/MiddlewareDispatcherTest.php index cfaab80..80f10fe 100644 --- a/tests/Integration/MiddlewareDispatcherTest.php +++ b/tests/Integration/MiddlewareDispatcherTest.php @@ -16,7 +16,7 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Slim\Psr7\Factory\ResponseFactory as SlimResponseFactory; -use Sunrise\Http\ServerRequest\ServerRequestFactory as SunriseServerRequestFactory; +use Sunrise\Http\Message\ServerRequestFactory as SunriseServerRequestFactory; /** * @coversNothing diff --git a/tests/Integration/RouteMatcherLessTest.php b/tests/Integration/RouteMatcherLessTest.php index 3a3351b..bc590d4 100644 --- a/tests/Integration/RouteMatcherLessTest.php +++ b/tests/Integration/RouteMatcherLessTest.php @@ -22,7 +22,7 @@ use Slim\Psr7\Factory\ResponseFactory as SlimResponseFactory; use Slim\Psr7\Factory\ServerRequestFactory as SlimServerRequestFactory; use Sunrise\Http\Message\ResponseFactory as SunriseResponseFactory; -use Sunrise\Http\ServerRequest\ServerRequestFactory as SunriseServerRequestFactory; +use Sunrise\Http\Message\ServerRequestFactory as SunriseServerRequestFactory; /** * @coversNothing From 26fd03e34aab183f99ac52649731c6bfdca405be Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Fri, 6 Jan 2023 11:29:01 +0100 Subject: [PATCH 13/55] update copyright --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 7b2b327..68ee3fd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Dominik Zogg +Copyright (c) 2023 Dominik Zogg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 907e808..a892296 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); ## Copyright -Dominik Zogg 2022 +2023 Dominik Zogg [1]: https://web-frameworks-benchmark.netlify.app/result From 7423f0fe86dfa59aa478ce2d88d3ed367f85baf2 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 2 Dec 2023 18:40:47 +0100 Subject: [PATCH 14/55] php 8.3 --- .github/workflows/ci.yml | 24 ++++---- .gitignore | 1 + README.md | 58 +++++++++---------- composer.json | 49 +++++++--------- phpunit.xml | 45 ++++++-------- src/Middleware/LazyMiddleware.php | 4 +- src/Middleware/MiddlewareRequestHandler.php | 4 +- src/Middleware/RouteMatcherMiddleware.php | 4 +- src/Middleware/SlimLazyMiddleware.php | 3 +- src/RequestHandler/LazyRequestHandler.php | 4 +- src/RequestHandler/RouteRequestHandler.php | 4 +- src/RequestHandler/SlimLazyRequestHandler.php | 3 +- ...issingRouteAttributeOnRequestException.php | 5 +- src/Router/Exceptions/RouterException.php | 4 +- src/Router/Route.php | 15 +++-- .../Integration/MiddlewareDispatcherTest.php | 7 +-- tests/Integration/RouteMatcherLessTest.php | 13 +---- tests/Unit/Emitter/EmitterTest.php | 6 -- tests/Unit/Router/GroupTest.php | 7 +-- tests/Unit/Router/RouteTest.php | 39 ++++++------- 20 files changed, 124 insertions(+), 175 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 282ff32..c07380f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,36 +6,36 @@ on: - cron: '0 0 * * *' jobs: - php80: - name: PHP 8.0 - runs-on: ubuntu-20.04 + php81: + name: PHP 8.1 + runs-on: ubuntu-22.04 steps: - name: checkout uses: actions/checkout@v3 - name: composer test - uses: docker://chubbyphp/ci-php80:latest + uses: docker://chubbyphp/ci-php81:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - php81: - name: PHP 8.1 - runs-on: ubuntu-20.04 + php82: + name: PHP 8.2 + runs-on: ubuntu-22.04 steps: - name: checkout uses: actions/checkout@v3 - name: composer test - uses: docker://chubbyphp/ci-php81:latest + uses: docker://chubbyphp/ci-php82:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - php82: - name: PHP 8.2 - runs-on: ubuntu-20.04 + php83: + name: PHP 8.3 + runs-on: ubuntu-22.04 steps: - name: checkout uses: actions/checkout@v3 - name: composer test - uses: docker://chubbyphp/ci-php82:latest + uses: docker://chubbyphp/ci-php83:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} diff --git a/.gitignore b/.gitignore index 1cf303f..91ca7d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store .idea/ +.phpunit.cache .vscode/ build/ composer.lock diff --git a/README.md b/README.md index a892296..b65ac45 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![CI](https://github.com/chubbyphp/chubbyphp-framework/workflows/CI/badge.svg?branch=master)](https://github.com/chubbyphp/chubbyphp-framework/actions?query=workflow%3ACI) [![Coverage Status](https://coveralls.io/repos/github/chubbyphp/chubbyphp-framework/badge.svg?branch=master)](https://coveralls.io/github/chubbyphp/chubbyphp-framework?branch=master) -[![Infection MSI](https://badge.stryker-mutator.io/github.com/chubbyphp/chubbyphp-framework/master)](https://dashboard.stryker-mutator.io/reports/github.com/chubbyphp/chubbyphp-framework/master) +[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fchubbyphp%2Fchubbyphp-framework%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/chubbyphp/chubbyphp-framework/master)[![Latest Stable Version](https://poser.pugx.org/chubbyphp/chubbyphp-framework/v/stable.png)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) [![Latest Stable Version](https://poser.pugx.org/chubbyphp/chubbyphp-framework/v/stable.png)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) [![Total Downloads](https://poser.pugx.org/chubbyphp/chubbyphp-framework/downloads.png)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) [![Monthly Downloads](https://poser.pugx.org/chubbyphp/chubbyphp-framework/d/monthly)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) @@ -36,16 +36,15 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements - * php: ^8.0 - * [chubbyphp/chubbyphp-http-exception][20]: ^1.0.1 - * [fig/http-message-util][21]: ^1.1.5 - * [psr/container][22]: ^1.0|^2.0 - * [psr/http-factory][23]: ^1.0.1 - * [psr/http-message][24]: ^1.0.1 - * [psr/http-message-implementation][25]: ^1.0 - * [psr/http-server-handler][26]: ^1.0.1 - * [psr/http-server-middleware][27]: ^1.0.1 - * [psr/log][28]: ^1.1 + * php: ^8.1 + * [chubbyphp/chubbyphp-http-exception][20]: ^1.1 + * [psr/container][21]: ^1.1.2|^2.0.2 + * [psr/http-factory][22]: ^1.0.2 + * [psr/http-message][23]: ^1.1|^2.0 + * [psr/http-message-implementation][24]: ^1.1|^2.0 + * [psr/http-server-handler][25]: ^1.0.2 + * [psr/http-server-middleware][26]: ^1.0.2 + * [psr/log][27]: ^2.0|^3.0 ## Suggest @@ -53,24 +52,23 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi Any Router which implements `Chubbyphp\Framework\Router\RouteMatcherInterface` can be used. - * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.0 + * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.1 ### PSR 7 / PSR 17 - * [bittyphp/http][40]: ^2.0 - * [guzzlehttp/psr7][41]: ^1.4.2 (with [http-interop/http-factory-guzzle][42]: ^1.0) - * [nyholm/psr7][43]: ^1.0 - * [slim/psr7][44]: ^0.5|^1.0 + * [guzzlehttp/psr7][40]: ^2.6.1 (with [http-interop/http-factory-guzzle][41]: ^1.2) + * [laminas/laminas-diactoros][42]: ^3.3 + * [nyholm/psr7][43]: ^1.8.1 + * [slim/psr7][44]: ^1.6.1 * [sunrise/http-message][45]: ^3.0 - * [laminas/laminas-diactoros][46]: ^2.0 ## Installation Through [Composer](http://getcomposer.org) as [chubbyphp/chubbyphp-framework][60]. ```bash -composer require chubbyphp/chubbyphp-framework "^5.0" \ - chubbyphp/chubbyphp-framework-router-fastroute "^2.0" \ +composer require chubbyphp/chubbyphp-framework "^5.1" \ + chubbyphp/chubbyphp-framework-router-fastroute "^2.1" \ slim/psr7 "^1.5" ``` @@ -182,24 +180,22 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); [15]: https://travis-ci.org/chubbyphp/chubbyphp-framework [20]: https://packagist.org/packages/chubbyphp/chubbyphp-http-exception -[21]: https://packagist.org/packages/fig/http-message-util -[22]: https://packagist.org/packages/psr/container -[23]: https://packagist.org/packages/psr/http-factory -[24]: https://packagist.org/packages/psr/http-message -[25]: https://packagist.org/packages/psr/http-message-implementation -[26]: https://packagist.org/packages/psr/http-server-handler -[27]: https://packagist.org/packages/psr/http-server-middleware -[28]: https://packagist.org/packages/psr/log +[21]: https://packagist.org/packages/psr/container +[22]: https://packagist.org/packages/psr/http-factory +[23]: https://packagist.org/packages/psr/http-message +[24]: https://packagist.org/packages/psr/http-message-implementation +[25]: https://packagist.org/packages/psr/http-server-handler +[26]: https://packagist.org/packages/psr/http-server-middleware +[27]: https://packagist.org/packages/psr/log [30]: https://github.com/chubbyphp/chubbyphp-framework-router-fastroute#usage -[40]: https://packagist.org/packages/bittyphp/http -[41]: https://packagist.org/packages/guzzlehttp/psr7 -[42]: https://packagist.org/packages/http-interop/http-factory-guzzle +[40]: https://packagist.org/packages/guzzlehttp/psr7 +[41]: https://packagist.org/packages/http-interop/http-factory-guzzle +[42]: https://packagist.org/packages/laminas/laminas-diactoros [43]: https://packagist.org/packages/nyholm/psr7 [44]: https://packagist.org/packages/slim/psr7 [45]: https://packagist.org/packages/sunrise/http-message -[46]: https://packagist.org/packages/laminas/laminas-diactoros [60]: https://packagist.org/packages/chubbyphp/chubbyphp-framework diff --git a/composer.json b/composer.json index 8b70102..4babad2 100644 --- a/composer.json +++ b/composer.json @@ -22,32 +22,29 @@ } ], "require": { - "php": "^8.0", - "chubbyphp/chubbyphp-http-exception": "^1.0.1", - "fig/http-message-util": "^1.1.5", - "psr/container": "^1.0|^2.0", - "psr/http-factory": "^1.0.1", - "psr/http-message": "^1.0.1", - "psr/http-message-implementation": "^1.0", - "psr/http-server-handler": "^1.0.1", - "psr/http-server-middleware": "^1.0.1", - "psr/log": "^1.1.4|^2.0|^3.0" + "php": "^8.1", + "chubbyphp/chubbyphp-http-exception": "^1.1", + "psr/container": "^1.1.2|^2.0.2", + "psr/http-factory": "^1.0.2", + "psr/http-message": "^1.1|^2.0", + "psr/http-message-implementation": "^1.1|^2.0", + "psr/http-server-handler": "^1.0.2", + "psr/http-server-middleware": "^1.0.2", + "psr/log": "^2.0|^3.0" }, "require-dev": { - "bittyphp/http": "^2.0", "chubbyphp/chubbyphp-dev-helper": "dev-master", - "chubbyphp/chubbyphp-mock": "^1.6.1", - "guzzlehttp/psr7": "^1.4.2", - "http-interop/http-factory-guzzle": "^1.0", - "infection/infection": "^0.26.5", - "laminas/laminas-diactoros": "^2.0", - "nyholm/psr7": "^1.0", - "php-coveralls/php-coveralls": "^2.5.2", - "phploc/phploc": "^7.0.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.4.6", - "phpunit/phpunit": "^9.5.16", - "slim/psr7": "^0.5|^1.0", + "chubbyphp/chubbyphp-mock": "^1.7", + "guzzlehttp/psr7": "^2.6.1", + "http-interop/http-factory-guzzle": "^1.2", + "infection/infection": "^0.27.8", + "laminas/laminas-diactoros": "^3.3", + "nyholm/psr7": "^1.8.1", + "php-coveralls/php-coveralls": "^2.7.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.45", + "phpunit/phpunit": "^10.4.2", + "slim/psr7": "^1.6.1", "sunrise/http-message": "^3.0" }, "autoload": { @@ -65,7 +62,7 @@ }, "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.1-dev" } }, "scripts": { @@ -76,15 +73,13 @@ "@test:integration", "@test:infection", "@test:static-analysis", - "@test:loc", "@test:cs" ], "test:cs": "mkdir -p build && PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --cache-file=build/phpcs.cache", - "test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=97 --verbose --coverage=build/phpunit", + "test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=100 --verbose --coverage=build/phpunit", "test:integration": "vendor/bin/phpunit --testsuite=Integration --cache-result-file=build/phpunit/result.cache", "test:lint": "mkdir -p build && find src tests -name '*.php' -print0 | xargs -0 -n1 -P$(nproc) php -l | tee build/phplint.log", "test:static-analysis": "mkdir -p build && bash -c 'vendor/bin/phpstan analyse src --no-progress --level=8 --error-format=junit | tee build/phpstan.junit.xml; if [ ${PIPESTATUS[0]} -ne \"0\" ]; then exit 1; fi'", - "test:loc": "mkdir -p build && vendor/bin/phploc src | tee build/phploc.log", "test:unit": "vendor/bin/phpunit --testsuite=Unit --coverage-text --coverage-clover=build/phpunit/clover.xml --coverage-html=build/phpunit/coverage-html --coverage-xml=build/phpunit/coverage-xml --log-junit=build/phpunit/junit.xml --cache-result-file=build/phpunit/result.cache" } } diff --git a/phpunit.xml b/phpunit.xml index 4354f8b..5adb3df 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,29 +1,20 @@ - - - - ./src - - - ./src - - - - - ./tests/Integration - - - ./tests/Unit - - + + + + + ./tests/Integration + + + ./tests/Unit + + + + + ./src + + + ./src + + diff --git a/src/Middleware/LazyMiddleware.php b/src/Middleware/LazyMiddleware.php index 4df3b7e..0cdcc18 100644 --- a/src/Middleware/LazyMiddleware.php +++ b/src/Middleware/LazyMiddleware.php @@ -12,9 +12,7 @@ final class LazyMiddleware implements MiddlewareInterface { - public function __construct(private ContainerInterface $container, private string $id) - { - } + public function __construct(private ContainerInterface $container, private string $id) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/src/Middleware/MiddlewareRequestHandler.php b/src/Middleware/MiddlewareRequestHandler.php index cf4813a..5c6b91d 100644 --- a/src/Middleware/MiddlewareRequestHandler.php +++ b/src/Middleware/MiddlewareRequestHandler.php @@ -11,9 +11,7 @@ final class MiddlewareRequestHandler implements RequestHandlerInterface { - public function __construct(private MiddlewareInterface $middleware, private RequestHandlerInterface $handler) - { - } + public function __construct(private MiddlewareInterface $middleware, private RequestHandlerInterface $handler) {} public function handle(ServerRequestInterface $request): ResponseInterface { diff --git a/src/Middleware/RouteMatcherMiddleware.php b/src/Middleware/RouteMatcherMiddleware.php index 8502d2c..4289b66 100644 --- a/src/Middleware/RouteMatcherMiddleware.php +++ b/src/Middleware/RouteMatcherMiddleware.php @@ -12,9 +12,7 @@ final class RouteMatcherMiddleware implements MiddlewareInterface { - public function __construct(private RouteMatcherInterface $routeMatcher) - { - } + public function __construct(private RouteMatcherInterface $routeMatcher) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/src/Middleware/SlimLazyMiddleware.php b/src/Middleware/SlimLazyMiddleware.php index d8763c3..2e22475 100644 --- a/src/Middleware/SlimLazyMiddleware.php +++ b/src/Middleware/SlimLazyMiddleware.php @@ -17,8 +17,7 @@ public function __construct( private ContainerInterface $container, private string $id, private ResponseFactoryInterface $responseFactory - ) { - } + ) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/src/RequestHandler/LazyRequestHandler.php b/src/RequestHandler/LazyRequestHandler.php index e5fce3d..75538dd 100644 --- a/src/RequestHandler/LazyRequestHandler.php +++ b/src/RequestHandler/LazyRequestHandler.php @@ -11,9 +11,7 @@ final class LazyRequestHandler implements RequestHandlerInterface { - public function __construct(private ContainerInterface $container, private string $id) - { - } + public function __construct(private ContainerInterface $container, private string $id) {} public function handle(ServerRequestInterface $request): ResponseInterface { diff --git a/src/RequestHandler/RouteRequestHandler.php b/src/RequestHandler/RouteRequestHandler.php index 87a635e..7eb0464 100644 --- a/src/RequestHandler/RouteRequestHandler.php +++ b/src/RequestHandler/RouteRequestHandler.php @@ -13,9 +13,7 @@ final class RouteRequestHandler implements RequestHandlerInterface { - public function __construct(private MiddlewareDispatcherInterface $middlewareDispatcher) - { - } + public function __construct(private MiddlewareDispatcherInterface $middlewareDispatcher) {} public function handle(ServerRequestInterface $request): ResponseInterface { diff --git a/src/RequestHandler/SlimLazyRequestHandler.php b/src/RequestHandler/SlimLazyRequestHandler.php index ac6ab36..ac1d301 100644 --- a/src/RequestHandler/SlimLazyRequestHandler.php +++ b/src/RequestHandler/SlimLazyRequestHandler.php @@ -16,8 +16,7 @@ public function __construct( private ContainerInterface $container, private string $id, private ResponseFactoryInterface $responseFactory - ) { - } + ) {} public function handle(ServerRequestInterface $request): ResponseInterface { diff --git a/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php b/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php index 7a056cc..a15d728 100644 --- a/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php +++ b/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php @@ -13,10 +13,7 @@ private function __construct(string $message, int $code, ?\Throwable $previous = parent::__construct($message, $code, $previous); } - /** - * @param mixed $route - */ - public static function create($route): self + public static function create(mixed $route): self { return new self( sprintf( diff --git a/src/Router/Exceptions/RouterException.php b/src/Router/Exceptions/RouterException.php index a6aa337..135f7e4 100644 --- a/src/Router/Exceptions/RouterException.php +++ b/src/Router/Exceptions/RouterException.php @@ -4,6 +4,4 @@ namespace Chubbyphp\Framework\Router\Exceptions; -abstract class RouterException extends \LogicException -{ -} +abstract class RouterException extends \LogicException {} diff --git a/src/Router/Route.php b/src/Router/Route.php index d0fedc3..7e9e046 100644 --- a/src/Router/Route.php +++ b/src/Router/Route.php @@ -5,7 +5,6 @@ namespace Chubbyphp\Framework\Router; use Chubbyphp\Framework\Collection; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -62,7 +61,7 @@ public static function delete( array $middlewares = [], array $pathOptions = [] ): self { - return new self(RequestMethod::METHOD_DELETE, $path, $name, $requestHandler, $middlewares, $pathOptions); + return new self('DELETE', $path, $name, $requestHandler, $middlewares, $pathOptions); } /** @@ -76,7 +75,7 @@ public static function get( array $middlewares = [], array $pathOptions = [] ): self { - return new self(RequestMethod::METHOD_GET, $path, $name, $requestHandler, $middlewares, $pathOptions); + return new self('GET', $path, $name, $requestHandler, $middlewares, $pathOptions); } /** @@ -90,7 +89,7 @@ public static function head( array $middlewares = [], array $pathOptions = [] ): self { - return new self(RequestMethod::METHOD_HEAD, $path, $name, $requestHandler, $middlewares, $pathOptions); + return new self('HEAD', $path, $name, $requestHandler, $middlewares, $pathOptions); } /** @@ -104,7 +103,7 @@ public static function options( array $middlewares = [], array $pathOptions = [] ): self { - return new self(RequestMethod::METHOD_OPTIONS, $path, $name, $requestHandler, $middlewares, $pathOptions); + return new self('OPTIONS', $path, $name, $requestHandler, $middlewares, $pathOptions); } /** @@ -118,7 +117,7 @@ public static function patch( array $middlewares = [], array $pathOptions = [] ): self { - return new self(RequestMethod::METHOD_PATCH, $path, $name, $requestHandler, $middlewares, $pathOptions); + return new self('PATCH', $path, $name, $requestHandler, $middlewares, $pathOptions); } /** @@ -132,7 +131,7 @@ public static function post( array $middlewares = [], array $pathOptions = [] ): self { - return new self(RequestMethod::METHOD_POST, $path, $name, $requestHandler, $middlewares, $pathOptions); + return new self('POST', $path, $name, $requestHandler, $middlewares, $pathOptions); } /** @@ -146,7 +145,7 @@ public static function put( array $middlewares = [], array $pathOptions = [] ): self { - return new self(RequestMethod::METHOD_PUT, $path, $name, $requestHandler, $middlewares, $pathOptions); + return new self('PUT', $path, $name, $requestHandler, $middlewares, $pathOptions); } public function getName(): string diff --git a/tests/Integration/MiddlewareDispatcherTest.php b/tests/Integration/MiddlewareDispatcherTest.php index 80f10fe..e735158 100644 --- a/tests/Integration/MiddlewareDispatcherTest.php +++ b/tests/Integration/MiddlewareDispatcherTest.php @@ -10,7 +10,6 @@ use Chubbyphp\Framework\RequestHandler\CallbackRequestHandler; use Chubbyphp\Framework\RequestHandler\SlimCallbackRequestHandler; use Chubbyphp\Mock\MockByCallsTrait; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -59,7 +58,7 @@ static function (ServerRequestInterface $request) use ($responseFactory) { return $responseFactory->createResponse(); } ), - $serverRequestFactory->createServerRequest(RequestMethod::METHOD_GET, '/hello/test') + $serverRequestFactory->createServerRequest('GET', '/hello/test') ); } @@ -109,7 +108,7 @@ static function (ServerRequestInterface $req, ResponseInterface $res, array $arg }, $responseFactory ), - $serverRequestFactory->createServerRequest(RequestMethod::METHOD_GET, '/hello/test') + $serverRequestFactory->createServerRequest('GET', '/hello/test') ); } @@ -155,7 +154,7 @@ static function (ServerRequestInterface $req, ResponseInterface $res, array $arg }, $responseFactory ), - $serverRequestFactory->createServerRequest(RequestMethod::METHOD_GET, '/hello/test') + $serverRequestFactory->createServerRequest('GET', '/hello/test') ); } } diff --git a/tests/Integration/RouteMatcherLessTest.php b/tests/Integration/RouteMatcherLessTest.php index bc590d4..5f16985 100644 --- a/tests/Integration/RouteMatcherLessTest.php +++ b/tests/Integration/RouteMatcherLessTest.php @@ -4,12 +4,9 @@ namespace Chubbyphp\Tests\Framework\Integration; -use Bitty\Http\ResponseFactory as BittyResponseFactory; -use Bitty\Http\ServerRequestFactory as BittyServerRequestFactory; use Chubbyphp\Framework\Application; use Chubbyphp\Framework\Middleware\ExceptionMiddleware; use Chubbyphp\Framework\Router\Exceptions\RouterException; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; use Http\Factory\Guzzle\ResponseFactory as GuzzleResponseFactory; use Http\Factory\Guzzle\ServerRequestFactory as GuzzleServerRequestFactory; use Laminas\Diactoros\ResponseFactory as LaminasResponseFactory; @@ -31,13 +28,9 @@ */ final class RouteMatcherLessTest extends TestCase { - public function providePsr7Implementations(): array + public function providePsr7Implementations(): iterable { return [ - 'bitty' => [ - 'responseFactory' => new BittyResponseFactory(), - 'serverRequestFactory' => new BittyServerRequestFactory(), - ], 'guzzle' => [ 'responseFactory' => new GuzzleResponseFactory(), 'serverRequestFactory' => new GuzzleServerRequestFactory(), @@ -72,7 +65,7 @@ public function testMissingRouteMatcherMiddleware( new ExceptionMiddleware($responseFactory, true), ]); - $request = $serverRequestFactory->createServerRequest(RequestMethod::METHOD_GET, '/hello/test'); + $request = $serverRequestFactory->createServerRequest('GET', '/hello/test'); $response = $app->handle($request); @@ -102,7 +95,7 @@ public function testMissingRouteMatcherMiddlewareWithoutExceptionMiddleware( $app = new Application([]); - $request = $serverRequestFactory->createServerRequest(RequestMethod::METHOD_GET, '/hello/test'); + $request = $serverRequestFactory->createServerRequest('GET', '/hello/test'); $app->handle($request); } diff --git a/tests/Unit/Emitter/EmitterTest.php b/tests/Unit/Emitter/EmitterTest.php index fde811d..d0de91f 100644 --- a/tests/Unit/Emitter/EmitterTest.php +++ b/tests/Unit/Emitter/EmitterTest.php @@ -8,9 +8,6 @@ final class TestHeader { private static array $headers = []; - /** - * @param int $http_response_code - */ public static function add(string $header, bool $replace = true, ?int $http_response_code = null): void { self::$headers[] = [ @@ -31,9 +28,6 @@ public static function reset(): void } } - /** - * @param int $http_response_code - */ function header(string $header, bool $replace = true, ?int $http_response_code = null): void { TestHeader::add($header, $replace, $http_response_code); diff --git a/tests/Unit/Router/GroupTest.php b/tests/Unit/Router/GroupTest.php index 1356ebe..a924382 100644 --- a/tests/Unit/Router/GroupTest.php +++ b/tests/Unit/Router/GroupTest.php @@ -8,7 +8,6 @@ use Chubbyphp\Framework\Router\Route; use Chubbyphp\Framework\Router\RouteInterface; use Chubbyphp\Mock\MockByCallsTrait; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Server\MiddlewareInterface; @@ -77,7 +76,7 @@ public function testMaximal(): void $route1 = $routes[0]; self::assertSame('element_read', $route1->getName()); - self::assertSame(RequestMethod::METHOD_GET, $route1->getMethod()); + self::assertSame('GET', $route1->getMethod()); self::assertSame('/{id}/{slug}', $route1->getPath()); self::assertSame(['tokens' => ['id' => '\d+', 'slug' => '[a-z]+']], $route1->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route1->getMiddlewares()); @@ -87,7 +86,7 @@ public function testMaximal(): void $route2 = $routes[1]; self::assertSame('another_route', $route2->getName()); - self::assertSame(RequestMethod::METHOD_GET, $route2->getMethod()); + self::assertSame('GET', $route2->getMethod()); self::assertSame('/{id}/{slug}/{key}', $route2->getPath()); self::assertSame( ['tokens' => ['id' => '\d+', 'slug' => '[a-z]+', 'key' => '[a-z]+']], @@ -100,7 +99,7 @@ public function testMaximal(): void $route3 = $routes[2]; self::assertSame('yet_another_route', $route3->getName()); - self::assertSame(RequestMethod::METHOD_GET, $route3->getMethod()); + self::assertSame('GET', $route3->getMethod()); self::assertSame('/{id}/{slug}/{key}/{subKey}', $route3->getPath()); self::assertSame( ['tokens' => ['id' => '\d+', 'slug' => '[a-z]+', 'key' => '[a-z]+', 'subKey' => '[a-z]+']], diff --git a/tests/Unit/Router/RouteTest.php b/tests/Unit/Router/RouteTest.php index ec2519b..d138318 100644 --- a/tests/Unit/Router/RouteTest.php +++ b/tests/Unit/Router/RouteTest.php @@ -6,7 +6,6 @@ use Chubbyphp\Framework\Router\Route; use Chubbyphp\Mock\MockByCallsTrait; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Server\MiddlewareInterface; @@ -26,10 +25,10 @@ public function testMinimal(): void /** @var MockObject|RequestHandlerInterface $handler */ $handler = $this->getMockByCalls(RequestHandlerInterface::class); - $route = Route::create(RequestMethod::METHOD_GET, '/{id}', 'read', $handler); + $route = Route::create('GET', '/{id}', 'read', $handler); self::assertSame('read', $route->getName()); - self::assertSame(RequestMethod::METHOD_GET, $route->getMethod()); + self::assertSame('GET', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame([], $route->getPathOptions()); self::assertSame([], $route->getMiddlewares()); @@ -49,7 +48,7 @@ public function testMaximal(): void $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); $route = Route::create( - RequestMethod::METHOD_GET, + 'GET', '/{id}', 'read', $handler, @@ -58,7 +57,7 @@ public function testMaximal(): void ); self::assertSame('read', $route->getName()); - self::assertSame(RequestMethod::METHOD_GET, $route->getMethod()); + self::assertSame('GET', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame(['tokens' => ['id' => '\d+']], $route->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route->getMiddlewares()); @@ -74,7 +73,7 @@ public function testDeleteMinimal(): void $route = Route::delete('/{id}', 'delete', $handler); self::assertSame('delete', $route->getName()); - self::assertSame(RequestMethod::METHOD_DELETE, $route->getMethod()); + self::assertSame('DELETE', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame([], $route->getPathOptions()); self::assertSame([], $route->getMiddlewares()); @@ -96,7 +95,7 @@ public function testDeleteMaximal(): void $route = Route::delete('/{id}', 'delete', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); self::assertSame('delete', $route->getName()); - self::assertSame(RequestMethod::METHOD_DELETE, $route->getMethod()); + self::assertSame('DELETE', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame(['tokens' => ['id' => '\d+']], $route->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route->getMiddlewares()); @@ -112,7 +111,7 @@ public function testGetMinimal(): void $route = Route::get('/{id}', 'read', $handler); self::assertSame('read', $route->getName()); - self::assertSame(RequestMethod::METHOD_GET, $route->getMethod()); + self::assertSame('GET', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame([], $route->getPathOptions()); self::assertSame([], $route->getMiddlewares()); @@ -134,7 +133,7 @@ public function testGetMaximal(): void $route = Route::get('/{id}', 'get', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); self::assertSame('get', $route->getName()); - self::assertSame(RequestMethod::METHOD_GET, $route->getMethod()); + self::assertSame('GET', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame(['tokens' => ['id' => '\d+']], $route->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route->getMiddlewares()); @@ -150,7 +149,7 @@ public function testHeadMinimal(): void $route = Route::head('/{id}', 'read_header', $handler); self::assertSame('read_header', $route->getName()); - self::assertSame(RequestMethod::METHOD_HEAD, $route->getMethod()); + self::assertSame('HEAD', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame([], $route->getPathOptions()); self::assertSame([], $route->getMiddlewares()); @@ -172,7 +171,7 @@ public function testHeadMaximal(): void $route = Route::head('/{id}', 'head', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); self::assertSame('head', $route->getName()); - self::assertSame(RequestMethod::METHOD_HEAD, $route->getMethod()); + self::assertSame('HEAD', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame(['tokens' => ['id' => '\d+']], $route->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route->getMiddlewares()); @@ -188,7 +187,7 @@ public function testOptionsMinimal(): void $route = Route::options('/{id}', 'options', $handler); self::assertSame('options', $route->getName()); - self::assertSame(RequestMethod::METHOD_OPTIONS, $route->getMethod()); + self::assertSame('OPTIONS', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame([], $route->getPathOptions()); self::assertSame([], $route->getMiddlewares()); @@ -210,7 +209,7 @@ public function testOptionsMaximal(): void $route = Route::options('/{id}', 'options', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); self::assertSame('options', $route->getName()); - self::assertSame(RequestMethod::METHOD_OPTIONS, $route->getMethod()); + self::assertSame('OPTIONS', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame(['tokens' => ['id' => '\d+']], $route->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route->getMiddlewares()); @@ -226,7 +225,7 @@ public function testPatchMinimal(): void $route = Route::patch('/{id}', 'update', $handler); self::assertSame('update', $route->getName()); - self::assertSame(RequestMethod::METHOD_PATCH, $route->getMethod()); + self::assertSame('PATCH', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame([], $route->getPathOptions()); self::assertSame([], $route->getMiddlewares()); @@ -248,7 +247,7 @@ public function testPatchMaximal(): void $route = Route::patch('/{id}', 'patch', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); self::assertSame('patch', $route->getName()); - self::assertSame(RequestMethod::METHOD_PATCH, $route->getMethod()); + self::assertSame('PATCH', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame(['tokens' => ['id' => '\d+']], $route->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route->getMiddlewares()); @@ -264,7 +263,7 @@ public function testPostMinimal(): void $route = Route::post('/{id}', 'create', $handler); self::assertSame('create', $route->getName()); - self::assertSame(RequestMethod::METHOD_POST, $route->getMethod()); + self::assertSame('POST', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame([], $route->getPathOptions()); self::assertSame([], $route->getMiddlewares()); @@ -286,7 +285,7 @@ public function testPostMaximal(): void $route = Route::post('/{id}', 'post', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); self::assertSame('post', $route->getName()); - self::assertSame(RequestMethod::METHOD_POST, $route->getMethod()); + self::assertSame('POST', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame(['tokens' => ['id' => '\d+']], $route->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route->getMiddlewares()); @@ -302,7 +301,7 @@ public function testPutMinimal(): void $route = Route::put('/{id}', 'replace', $handler); self::assertSame('replace', $route->getName()); - self::assertSame(RequestMethod::METHOD_PUT, $route->getMethod()); + self::assertSame('PUT', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame([], $route->getPathOptions()); self::assertSame([], $route->getMiddlewares()); @@ -324,7 +323,7 @@ public function testPutMaximal(): void $route = Route::put('/{id}', 'put', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); self::assertSame('put', $route->getName()); - self::assertSame(RequestMethod::METHOD_PUT, $route->getMethod()); + self::assertSame('PUT', $route->getMethod()); self::assertSame('/{id}', $route->getPath()); self::assertSame(['tokens' => ['id' => '\d+']], $route->getPathOptions()); self::assertSame([$middleware1, $middleware2], $route->getMiddlewares()); @@ -337,7 +336,7 @@ public function testWithAttributes(): void /** @var MockObject|RequestHandlerInterface $handler */ $handler = $this->getMockByCalls(RequestHandlerInterface::class); - $route = Route::create(RequestMethod::METHOD_GET, '/{id}', 'read', $handler); + $route = Route::create('GET', '/{id}', 'read', $handler); $routeClone = $route->withAttributes(['id' => 5]); From d3aa30c0631de431519de2ef9a911264e535b455 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 2 Dec 2023 19:07:28 +0100 Subject: [PATCH 15/55] phpunit dataprovider should be static --- tests/Integration/RouteMatcherLessTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/RouteMatcherLessTest.php b/tests/Integration/RouteMatcherLessTest.php index 5f16985..76d973f 100644 --- a/tests/Integration/RouteMatcherLessTest.php +++ b/tests/Integration/RouteMatcherLessTest.php @@ -28,7 +28,7 @@ */ final class RouteMatcherLessTest extends TestCase { - public function providePsr7Implementations(): iterable + public static function providePsr7Implementations(): iterable { return [ 'guzzle' => [ From 7ec44a823a645d53992f5a2fe4af87b970db389b Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 2 Dec 2023 21:01:21 +0100 Subject: [PATCH 16/55] fix vendors --- README.md | 24 +++++++++++++----------- composer.json | 3 ++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b65ac45..8704ae3 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,13 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi * php: ^8.1 * [chubbyphp/chubbyphp-http-exception][20]: ^1.1 * [psr/container][21]: ^1.1.2|^2.0.2 - * [psr/http-factory][22]: ^1.0.2 - * [psr/http-message][23]: ^1.1|^2.0 - * [psr/http-message-implementation][24]: ^1.1|^2.0 - * [psr/http-server-handler][25]: ^1.0.2 - * [psr/http-server-middleware][26]: ^1.0.2 - * [psr/log][27]: ^2.0|^3.0 + * [psr/http-factory-implementation][22]: ^1.0 + * [psr/http-factory][23]: ^1.0.2 + * [psr/http-message-implementation][24]: ^1.0|^2.0 + * [psr/http-message][25]: ^1.1|^2.0 + * [psr/http-server-handler][26]: ^1.0.2 + * [psr/http-server-middleware][27]: ^1.0.2 + * [psr/log][28]: ^2.0|^3.0 ## Suggest @@ -181,12 +182,13 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); [20]: https://packagist.org/packages/chubbyphp/chubbyphp-http-exception [21]: https://packagist.org/packages/psr/container -[22]: https://packagist.org/packages/psr/http-factory -[23]: https://packagist.org/packages/psr/http-message +[22]: https://packagist.org/packages/psr/http-factory-implementation +[23]: https://packagist.org/packages/psr/http-factory [24]: https://packagist.org/packages/psr/http-message-implementation -[25]: https://packagist.org/packages/psr/http-server-handler -[26]: https://packagist.org/packages/psr/http-server-middleware -[27]: https://packagist.org/packages/psr/log +[25]: https://packagist.org/packages/psr/http-message +[26]: https://packagist.org/packages/psr/http-server-handler +[27]: https://packagist.org/packages/psr/http-server-middleware +[28]: https://packagist.org/packages/psr/log [30]: https://github.com/chubbyphp/chubbyphp-framework-router-fastroute#usage diff --git a/composer.json b/composer.json index 4babad2..766edec 100644 --- a/composer.json +++ b/composer.json @@ -25,9 +25,10 @@ "php": "^8.1", "chubbyphp/chubbyphp-http-exception": "^1.1", "psr/container": "^1.1.2|^2.0.2", + "psr/http-factory-implementation": "^1.0", "psr/http-factory": "^1.0.2", + "psr/http-message-implementation": "^1.0|^2.0", "psr/http-message": "^1.1|^2.0", - "psr/http-message-implementation": "^1.1|^2.0", "psr/http-server-handler": "^1.0.2", "psr/http-server-middleware": "^1.0.2", "psr/log": "^2.0|^3.0" From 24e239ef8c20fcdfaa5715168c1ad5195f0b3813 Mon Sep 17 00:00:00 2001 From: Raphael Stolt Date: Mon, 4 Dec 2023 07:58:05 +0100 Subject: [PATCH 17/55] Enable lean release --- .gitattributes | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..98173a6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,16 @@ +# This file was generated by the lean package validator (http://git.io/lean-package-validator). + +* text=auto eol=lf + +.gitattributes export-ignore +.github/ export-ignore +.gitignore export-ignore +.php-cs-fixer.php export-ignore +doc/ export-ignore export-ignore +infection.json export-ignore +phpstan.neon export-ignore +phpunit.xml export-ignore +LICENSE export-ignore +README.md export-ignore +tests/ export-ignore +sonar-project.properties export-ignore From a11693f67555102b0ee1a89084e68a40abbb3320 Mon Sep 17 00:00:00 2001 From: Raphael Stolt Date: Tue, 5 Dec 2023 08:19:46 +0100 Subject: [PATCH 18/55] Reduce export-ignores --- .gitattributes | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index 98173a6..9374867 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,11 +6,8 @@ .github/ export-ignore .gitignore export-ignore .php-cs-fixer.php export-ignore -doc/ export-ignore export-ignore infection.json export-ignore phpstan.neon export-ignore phpunit.xml export-ignore -LICENSE export-ignore -README.md export-ignore tests/ export-ignore -sonar-project.properties export-ignore +sonar-project.properties export-ignore From f7d9301603d14772ebad70b6990b33caae540652 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Mon, 1 Jan 2024 16:27:36 +0100 Subject: [PATCH 19/55] update year --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 68ee3fd..f71a527 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2023 Dominik Zogg +Copyright (c) 2024 Dominik Zogg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8704ae3..015fef9 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); ## Copyright -2023 Dominik Zogg +2024 Dominik Zogg [1]: https://web-frameworks-benchmark.netlify.app/result From eb8fc1b58fb68ccdcadf1a4657bdc08019abbaea Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Mon, 1 Jan 2024 17:16:35 +0100 Subject: [PATCH 20/55] remove duplicate "Latest Stable Version" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 015fef9..601e821 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![CI](https://github.com/chubbyphp/chubbyphp-framework/workflows/CI/badge.svg?branch=master)](https://github.com/chubbyphp/chubbyphp-framework/actions?query=workflow%3ACI) [![Coverage Status](https://coveralls.io/repos/github/chubbyphp/chubbyphp-framework/badge.svg?branch=master)](https://coveralls.io/github/chubbyphp/chubbyphp-framework?branch=master) -[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fchubbyphp%2Fchubbyphp-framework%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/chubbyphp/chubbyphp-framework/master)[![Latest Stable Version](https://poser.pugx.org/chubbyphp/chubbyphp-framework/v/stable.png)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) +[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fchubbyphp%2Fchubbyphp-framework%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/chubbyphp/chubbyphp-framework/master) [![Latest Stable Version](https://poser.pugx.org/chubbyphp/chubbyphp-framework/v/stable.png)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) [![Total Downloads](https://poser.pugx.org/chubbyphp/chubbyphp-framework/downloads.png)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) [![Monthly Downloads](https://poser.pugx.org/chubbyphp/chubbyphp-framework/d/monthly)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) From 6c535dd8c095053f2e0d3e53510f51548651de18 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Fri, 26 Jan 2024 20:50:57 +0100 Subject: [PATCH 21/55] actions/checkout@v4 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c07380f..a341e99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: composer test uses: docker://chubbyphp/ci-php81:latest env: @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: composer test uses: docker://chubbyphp/ci-php82:latest env: @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: composer test uses: docker://chubbyphp/ci-php83:latest env: From 2c2c896ba6c9e5f95e3d99dace19708c8ac9523a Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Wed, 13 Mar 2024 20:58:08 +0100 Subject: [PATCH 22/55] use ci with docker images on github --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a341e99..d96f1c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://chubbyphp/ci-php81:latest + uses: docker://ghcr.io/chubbyphp/ci-php81:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} @@ -24,7 +24,7 @@ jobs: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://chubbyphp/ci-php82:latest + uses: docker://ghcr.io/chubbyphp/ci-php82:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} @@ -35,7 +35,7 @@ jobs: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://chubbyphp/ci-php83:latest + uses: docker://ghcr.io/chubbyphp/ci-php83:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} From 2fef47f4d0cec2a54c096bbfa6820c7671bbde36 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 28 Jul 2024 14:04:37 +0200 Subject: [PATCH 23/55] cs-fix --- src/Collection.php | 2 +- src/Emitter/Emitter.php | 4 ++-- src/Middleware/ExceptionMiddleware.php | 2 +- src/Middleware/LazyMiddleware.php | 2 +- src/Middleware/SlimLazyMiddleware.php | 2 +- src/RequestHandler/LazyRequestHandler.php | 2 +- src/RequestHandler/SlimLazyRequestHandler.php | 2 +- .../Exceptions/MissingRouteAttributeOnRequestException.php | 2 +- src/Router/Exceptions/MissingRouteByNameException.php | 2 +- src/Router/Exceptions/RouteGenerationException.php | 2 +- src/Router/RoutesByName.php | 2 +- tests/Integration/DocumentationTest.php | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index fa2691b..76a8274 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -30,7 +30,7 @@ public function __construct(array $items, array $types) } throw new \TypeError( - sprintf( + \sprintf( '%s::__construct() expects parameter 1 at index %s to be %s, %s given', self::class, $i, diff --git a/src/Emitter/Emitter.php b/src/Emitter/Emitter.php index 1a5933b..21e35af 100644 --- a/src/Emitter/Emitter.php +++ b/src/Emitter/Emitter.php @@ -13,7 +13,7 @@ public function emit(ResponseInterface $response): void $statusCode = $response->getStatusCode(); header( - sprintf( + \sprintf( 'HTTP/%s %s %s', $response->getProtocolVersion(), $statusCode, @@ -25,7 +25,7 @@ public function emit(ResponseInterface $response): void foreach ($response->getHeaders() as $name => $values) { foreach ($values as $value) { - header(sprintf('%s: %s', $name, $value), false); + header(\sprintf('%s: %s', $name, $value), false); } } diff --git a/src/Middleware/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index 1c404d3..7e6aba2 100644 --- a/src/Middleware/ExceptionMiddleware.php +++ b/src/Middleware/ExceptionMiddleware.php @@ -264,7 +264,7 @@ private function addDebugToBody(array $exceptionsData): string foreach ($exceptionsData as $exceptionData) { $body .= '
'; foreach ($exceptionData as $key => $value) { - $body .= sprintf( + $body .= \sprintf( '
%s
%s
', ucfirst($key), nl2br((string) $value) diff --git a/src/Middleware/LazyMiddleware.php b/src/Middleware/LazyMiddleware.php index 0cdcc18..1acae5f 100644 --- a/src/Middleware/LazyMiddleware.php +++ b/src/Middleware/LazyMiddleware.php @@ -19,7 +19,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $middleware = $this->container->get($this->id); if (!$middleware instanceof MiddlewareInterface) { throw new \TypeError( - sprintf( + \sprintf( '%s() expects service with id "%s" to be %s, %s given', __METHOD__, $this->id, diff --git a/src/Middleware/SlimLazyMiddleware.php b/src/Middleware/SlimLazyMiddleware.php index 2e22475..3f81565 100644 --- a/src/Middleware/SlimLazyMiddleware.php +++ b/src/Middleware/SlimLazyMiddleware.php @@ -24,7 +24,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $middleware = $this->container->get($this->id); if (!\is_callable($middleware)) { throw new \TypeError( - sprintf( + \sprintf( '%s() expects service with id "%s" to be %s, %s given', __METHOD__, $this->id, diff --git a/src/RequestHandler/LazyRequestHandler.php b/src/RequestHandler/LazyRequestHandler.php index 75538dd..9fa3bf0 100644 --- a/src/RequestHandler/LazyRequestHandler.php +++ b/src/RequestHandler/LazyRequestHandler.php @@ -18,7 +18,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface $requestHandler = $this->container->get($this->id); if (!$requestHandler instanceof RequestHandlerInterface) { throw new \TypeError( - sprintf( + \sprintf( '%s() expects service with id "%s" to be %s, %s given', __METHOD__, $this->id, diff --git a/src/RequestHandler/SlimLazyRequestHandler.php b/src/RequestHandler/SlimLazyRequestHandler.php index ac1d301..bd0c601 100644 --- a/src/RequestHandler/SlimLazyRequestHandler.php +++ b/src/RequestHandler/SlimLazyRequestHandler.php @@ -23,7 +23,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface $requestHandler = $this->container->get($this->id); if (!\is_callable($requestHandler)) { throw new \TypeError( - sprintf( + \sprintf( '%s() expects service with id "%s" to be %s, %s given', __METHOD__, $this->id, diff --git a/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php b/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php index a15d728..762d3fb 100644 --- a/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php +++ b/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php @@ -16,7 +16,7 @@ private function __construct(string $message, int $code, ?\Throwable $previous = public static function create(mixed $route): self { return new self( - sprintf( + \sprintf( 'Request attribute "route" missing or wrong type "%s", please add the "%s" middleware', get_debug_type($route), RouteMatcherMiddleware::class diff --git a/src/Router/Exceptions/MissingRouteByNameException.php b/src/Router/Exceptions/MissingRouteByNameException.php index 0ec03ce..5bdaa11 100644 --- a/src/Router/Exceptions/MissingRouteByNameException.php +++ b/src/Router/Exceptions/MissingRouteByNameException.php @@ -13,6 +13,6 @@ private function __construct(string $message, int $code, ?\Throwable $previous = public static function create(string $name): self { - return new self(sprintf('Missing route: "%s"', $name), 2); + return new self(\sprintf('Missing route: "%s"', $name), 2); } } diff --git a/src/Router/Exceptions/RouteGenerationException.php b/src/Router/Exceptions/RouteGenerationException.php index e0481b7..0bb09cc 100644 --- a/src/Router/Exceptions/RouteGenerationException.php +++ b/src/Router/Exceptions/RouteGenerationException.php @@ -17,7 +17,7 @@ private function __construct(string $message, int $code, ?\Throwable $previous = public static function create(string $name, string $path, array $attributes, ?\Throwable $previous = null): self { return new self( - sprintf( + \sprintf( 'Route generation for route "%s" with path "%s" with attributes "%s" failed.%s', $name, $path, diff --git a/src/Router/RoutesByName.php b/src/Router/RoutesByName.php index 92e9a72..aaede6c 100644 --- a/src/Router/RoutesByName.php +++ b/src/Router/RoutesByName.php @@ -19,7 +19,7 @@ public function __construct(array $routes) foreach ($routes as $i => $route) { if (!$route instanceof RouteInterface) { throw new \TypeError( - sprintf( + \sprintf( '%s::__construct() expects parameter 1 at index %s to be %s, %s given', self::class, $i, diff --git a/tests/Integration/DocumentationTest.php b/tests/Integration/DocumentationTest.php index 5fc61a9..b2dbf5f 100644 --- a/tests/Integration/DocumentationTest.php +++ b/tests/Integration/DocumentationTest.php @@ -26,7 +26,7 @@ public function testDocumentation(): void \PhpToken::tokenize($phpBlock, TOKEN_PARSE); } catch (\Error $e) { self::fail( - sprintf( + \sprintf( 'Cannot parse the following code in file "%s", error "%s" : "%s"', $documentationFile, $e->getMessage(), From 2135da5d25e61a50837660cc406fd1605ce498f5 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 10 Aug 2024 21:55:19 +0200 Subject: [PATCH 24/55] fix badge urls --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 601e821..0f331b3 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # chubbyphp-framework -[![CI](https://github.com/chubbyphp/chubbyphp-framework/workflows/CI/badge.svg?branch=master)](https://github.com/chubbyphp/chubbyphp-framework/actions?query=workflow%3ACI) +[![CI](https://github.com/chubbyphp/chubbyphp-framework/actions/workflows/ci.yml/badge.svg)](https://github.com/chubbyphp/chubbyphp-framework/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/chubbyphp/chubbyphp-framework/badge.svg?branch=master)](https://coveralls.io/github/chubbyphp/chubbyphp-framework?branch=master) [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fchubbyphp%2Fchubbyphp-framework%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/chubbyphp/chubbyphp-framework/master) -[![Latest Stable Version](https://poser.pugx.org/chubbyphp/chubbyphp-framework/v/stable.png)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) -[![Total Downloads](https://poser.pugx.org/chubbyphp/chubbyphp-framework/downloads.png)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) +[![Latest Stable Version](https://poser.pugx.org/chubbyphp/chubbyphp-framework/v)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) +[![Total Downloads](https://poser.pugx.org/chubbyphp/chubbyphp-framework/downloads)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) [![Monthly Downloads](https://poser.pugx.org/chubbyphp/chubbyphp-framework/d/monthly)](https://packagist.org/packages/chubbyphp/chubbyphp-framework) [![bugs](https://sonarcloud.io/api/project_badges/measure?project=chubbyphp_chubbyphp-framework&metric=bugs)](https://sonarcloud.io/dashboard?id=chubbyphp_chubbyphp-framework) From b72dac6396dedc9811a2166e07f3ae3c464ddfb2 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 8 Dec 2024 08:27:40 +0100 Subject: [PATCH 25/55] php 8.4 and phpunit 11 --- .github/workflows/ci.yml | 26 +++++++++++++------------- README.md | 21 ++++++++++----------- composer.json | 32 ++++++++++++++++---------------- phpstan.neon | 5 ++++- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d96f1c1..7b9bf9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,41 +6,41 @@ on: - cron: '0 0 * * *' jobs: - php81: - name: PHP 8.1 - runs-on: ubuntu-22.04 + php82: + name: PHP 8.2 + runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://ghcr.io/chubbyphp/ci-php81:latest + uses: docker://ghcr.io/chubbyphp/ci-php82:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - php82: - name: PHP 8.2 - runs-on: ubuntu-22.04 + php83: + name: PHP 8.3 + runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://ghcr.io/chubbyphp/ci-php82:latest + uses: docker://ghcr.io/chubbyphp/ci-php83:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - php83: - name: PHP 8.3 - runs-on: ubuntu-22.04 + php84: + name: PHP 8.4 + runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://ghcr.io/chubbyphp/ci-php83:latest + uses: docker://ghcr.io/chubbyphp/ci-php84:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - name: sonarcloud.io - uses: sonarsource/sonarcloud-github-action@master + uses: sonarsource/sonarqube-scan-action@v4.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/README.md b/README.md index 0f331b3..31b6ad7 100644 --- a/README.md +++ b/README.md @@ -36,16 +36,16 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements - * php: ^8.1 + * php: ^8.2 * [chubbyphp/chubbyphp-http-exception][20]: ^1.1 * [psr/container][21]: ^1.1.2|^2.0.2 * [psr/http-factory-implementation][22]: ^1.0 - * [psr/http-factory][23]: ^1.0.2 + * [psr/http-factory][23]: ^1.1 * [psr/http-message-implementation][24]: ^1.0|^2.0 * [psr/http-message][25]: ^1.1|^2.0 * [psr/http-server-handler][26]: ^1.0.2 * [psr/http-server-middleware][27]: ^1.0.2 - * [psr/log][28]: ^2.0|^3.0 + * [psr/log][28]: ^2.0|^3.0.2 ## Suggest @@ -54,23 +54,22 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi Any Router which implements `Chubbyphp\Framework\Router\RouteMatcherInterface` can be used. * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.1 - ### PSR 7 / PSR 17 - * [guzzlehttp/psr7][40]: ^2.6.1 (with [http-interop/http-factory-guzzle][41]: ^1.2) - * [laminas/laminas-diactoros][42]: ^3.3 - * [nyholm/psr7][43]: ^1.8.1 - * [slim/psr7][44]: ^1.6.1 - * [sunrise/http-message][45]: ^3.0 + * [guzzlehttp/psr7][40]: ^2.7 (with [http-interop/http-factory-guzzle][41]: ^1.2) + * [laminas/laminas-diactoros][42]: ^3.5 + * [nyholm/psr7][43]: ^1.8.2 + * [slim/psr7][44]: ^1.7 + * [sunrise/http-message][45]: ^3.2 ## Installation Through [Composer](http://getcomposer.org) as [chubbyphp/chubbyphp-framework][60]. ```bash -composer require chubbyphp/chubbyphp-framework "^5.1" \ +composer require chubbyphp/chubbyphp-framework "^5.2" \ chubbyphp/chubbyphp-framework-router-fastroute "^2.1" \ - slim/psr7 "^1.5" + slim/psr7 "^1.7" ``` ## Usage diff --git a/composer.json b/composer.json index 766edec..e36f7af 100644 --- a/composer.json +++ b/composer.json @@ -22,31 +22,31 @@ } ], "require": { - "php": "^8.1", + "php": "^8.2", "chubbyphp/chubbyphp-http-exception": "^1.1", "psr/container": "^1.1.2|^2.0.2", "psr/http-factory-implementation": "^1.0", - "psr/http-factory": "^1.0.2", + "psr/http-factory": "^1.1", "psr/http-message-implementation": "^1.0|^2.0", "psr/http-message": "^1.1|^2.0", "psr/http-server-handler": "^1.0.2", "psr/http-server-middleware": "^1.0.2", - "psr/log": "^2.0|^3.0" + "psr/log": "^2.0|^3.0.2" }, "require-dev": { "chubbyphp/chubbyphp-dev-helper": "dev-master", - "chubbyphp/chubbyphp-mock": "^1.7", - "guzzlehttp/psr7": "^2.6.1", + "chubbyphp/chubbyphp-mock": "^1.8", + "guzzlehttp/psr7": "^2.7", "http-interop/http-factory-guzzle": "^1.2", - "infection/infection": "^0.27.8", - "laminas/laminas-diactoros": "^3.3", - "nyholm/psr7": "^1.8.1", + "infection/infection": "^0.29.8", + "laminas/laminas-diactoros": "^3.5", + "nyholm/psr7": "^1.8.2", "php-coveralls/php-coveralls": "^2.7.0", - "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.10.45", - "phpunit/phpunit": "^10.4.2", - "slim/psr7": "^1.6.1", - "sunrise/http-message": "^3.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.0.3", + "phpunit/phpunit": "^11.5.0", + "slim/psr7": "^1.7", + "sunrise/http-message": "^3.2" }, "autoload": { "psr-4": { "Chubbyphp\\Framework\\": "src/" } @@ -63,7 +63,7 @@ }, "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-master": "5.2-dev" } }, "scripts": { @@ -78,9 +78,9 @@ ], "test:cs": "mkdir -p build && PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --cache-file=build/phpcs.cache", "test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=100 --verbose --coverage=build/phpunit", - "test:integration": "vendor/bin/phpunit --testsuite=Integration --cache-result-file=build/phpunit/result.cache", + "test:integration": "vendor/bin/phpunit --testsuite=Integration --cache-directory=build/phpunit", "test:lint": "mkdir -p build && find src tests -name '*.php' -print0 | xargs -0 -n1 -P$(nproc) php -l | tee build/phplint.log", "test:static-analysis": "mkdir -p build && bash -c 'vendor/bin/phpstan analyse src --no-progress --level=8 --error-format=junit | tee build/phpstan.junit.xml; if [ ${PIPESTATUS[0]} -ne \"0\" ]; then exit 1; fi'", - "test:unit": "vendor/bin/phpunit --testsuite=Unit --coverage-text --coverage-clover=build/phpunit/clover.xml --coverage-html=build/phpunit/coverage-html --coverage-xml=build/phpunit/coverage-xml --log-junit=build/phpunit/junit.xml --cache-result-file=build/phpunit/result.cache" + "test:unit": "vendor/bin/phpunit --testsuite=Unit --coverage-text --coverage-clover=build/phpunit/clover.xml --coverage-html=build/phpunit/coverage-html --coverage-xml=build/phpunit/coverage-xml --log-junit=build/phpunit/junit.xml --cache-directory=build/phpunit" } } diff --git a/phpstan.neon b/phpstan.neon index f51e71c..b68e5b4 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,2 +1,5 @@ parameters: - ignoreErrors: [] + ignoreErrors: + - + message: '/Instanceof between Chubbyphp\\Framework\\Router\\RouteInterface and Chubbyphp\\Framework\\Router\\RouteInterface will always evaluate to true./' + path: %currentWorkingDirectory%/src/Router/RoutesByName.php From 625cd98c00211d0a35d5e62027c5223ae9d27087 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Mon, 30 Dec 2024 11:14:38 +0100 Subject: [PATCH 26/55] cs-fix --- src/Middleware/SlimCallbackMiddleware.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Middleware/SlimCallbackMiddleware.php b/src/Middleware/SlimCallbackMiddleware.php index 099c5e2..f0afd23 100644 --- a/src/Middleware/SlimCallbackMiddleware.php +++ b/src/Middleware/SlimCallbackMiddleware.php @@ -29,11 +29,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return ($this->slimCallable)( $request, $this->getResponse($request), - static function (ServerRequestInterface $request, ResponseInterface $response) use ($handler) { - return $handler->handle( - $request->withAttribute(self::ATTRIBUTE_RESPONSE, $response) - ); - } + static fn (ServerRequestInterface $request, ResponseInterface $response) => $handler->handle( + $request->withAttribute(self::ATTRIBUTE_RESPONSE, $response) + ) ); } From 5a0c22d6da2579ba0bfac77c6cfd7e8675036bff Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Tue, 7 Jan 2025 22:14:41 +0100 Subject: [PATCH 27/55] check for the type instead of against null --- src/Router/Exceptions/RouteGenerationException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Router/Exceptions/RouteGenerationException.php b/src/Router/Exceptions/RouteGenerationException.php index 0bb09cc..e825664 100644 --- a/src/Router/Exceptions/RouteGenerationException.php +++ b/src/Router/Exceptions/RouteGenerationException.php @@ -22,7 +22,7 @@ public static function create(string $name, string $path, array $attributes, ?\T $name, $path, json_encode([] !== $attributes ? $attributes : new \stdClass(), JSON_THROW_ON_ERROR), - null !== $previous ? ' '.$previous->getMessage() : '', + $previous instanceof \Throwable ? ' '.$previous->getMessage() : '', ), 3, $previous From 0975022ef00ebc76b74834737a124e1dff4d29f1 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Tue, 21 Jan 2025 06:45:04 +0100 Subject: [PATCH 28/55] change year from 2024 to 2025 --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index f71a527..7ff164f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2024 Dominik Zogg +Copyright (c) 2025 Dominik Zogg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 31b6ad7..8e40c68 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); ## Copyright -2024 Dominik Zogg +2025 Dominik Zogg [1]: https://web-frameworks-benchmark.netlify.app/result From 0cb46b0bb944d8ad8c2465712392980d5a852414 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 23 Feb 2025 17:11:29 +0100 Subject: [PATCH 29/55] cs-fix --- tests/Integration/RouteMatcherLessTest.php | 52 +++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/Integration/RouteMatcherLessTest.php b/tests/Integration/RouteMatcherLessTest.php index 76d973f..aca5ab7 100644 --- a/tests/Integration/RouteMatcherLessTest.php +++ b/tests/Integration/RouteMatcherLessTest.php @@ -28,32 +28,6 @@ */ final class RouteMatcherLessTest extends TestCase { - public static function providePsr7Implementations(): iterable - { - return [ - 'guzzle' => [ - 'responseFactory' => new GuzzleResponseFactory(), - 'serverRequestFactory' => new GuzzleServerRequestFactory(), - ], - 'nyholm' => [ - 'responseFactory' => new NyholmResponseFactory(), - 'serverRequestFactory' => new NyholmServerRequestFactory(), - ], - 'slim' => [ - 'responseFactory' => new SlimResponseFactory(), - 'serverRequestFactory' => new SlimServerRequestFactory(), - ], - 'sunrise' => [ - 'responseFactory' => new SunriseResponseFactory(), - 'serverRequestFactory' => new SunriseServerRequestFactory(), - ], - 'zend' => [ - 'responseFactory' => new LaminasResponseFactory(), - 'serverRequestFactory' => new LaminasServerRequestFactory(), - ], - ]; - } - /** * @dataProvider providePsr7Implementations */ @@ -99,4 +73,30 @@ public function testMissingRouteMatcherMiddlewareWithoutExceptionMiddleware( $app->handle($request); } + + public static function providePsr7Implementations(): iterable + { + return [ + 'guzzle' => [ + 'responseFactory' => new GuzzleResponseFactory(), + 'serverRequestFactory' => new GuzzleServerRequestFactory(), + ], + 'nyholm' => [ + 'responseFactory' => new NyholmResponseFactory(), + 'serverRequestFactory' => new NyholmServerRequestFactory(), + ], + 'slim' => [ + 'responseFactory' => new SlimResponseFactory(), + 'serverRequestFactory' => new SlimServerRequestFactory(), + ], + 'sunrise' => [ + 'responseFactory' => new SunriseResponseFactory(), + 'serverRequestFactory' => new SunriseServerRequestFactory(), + ], + 'zend' => [ + 'responseFactory' => new LaminasResponseFactory(), + 'serverRequestFactory' => new LaminasServerRequestFactory(), + ], + ]; + } } From 63bdf62252057d45eb7cdcf44cf7d74958aead67 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Fri, 28 Feb 2025 23:03:26 +0100 Subject: [PATCH 30/55] chubbyphp-mock-2.x --- .github/workflows/ci.yml | 2 +- README.md | 2 +- composer.json | 12 +- .../Integration/MiddlewareDispatcherTest.php | 3 - tests/Unit/ApplicationTest.php | 115 +++--- tests/Unit/Emitter/EmitterTest.php | 38 +- .../Middleware/CallbackMiddlewareTest.php | 23 +- .../Middleware/ExceptionMiddlewareTest.php | 376 +++++++++--------- tests/Unit/Middleware/LazyMiddlewareTest.php | 69 ++-- .../Middleware/MiddlewareDispatcherTest.php | 95 ++--- .../MiddlewareRequestHandlerTest.php | 37 +- .../Middleware/RouteMatcherMiddlewareTest.php | 57 +-- .../Middleware/SlimCallbackMiddlewareTest.php | 58 +-- .../Middleware/SlimLazyMiddlewareTest.php | 90 +++-- .../CallbackRequestHandlerTest.php | 15 +- .../RequestHandler/LazyRequestHandlerTest.php | 57 +-- .../RouteRequestHandlerTest.php | 57 +-- .../SlimCallbackRequestHandlerTest.php | 53 +-- .../SlimLazyRequestHandlerTest.php | 71 ++-- tests/Unit/Router/GroupTest.php | 26 +- tests/Unit/Router/RouteTest.php | 171 ++++---- tests/Unit/Router/RoutesByNameTest.php | 16 +- 22 files changed, 751 insertions(+), 692 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b9bf9e..142c808 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - name: sonarcloud.io - uses: sonarsource/sonarqube-scan-action@v4.1.0 + uses: sonarsource/sonarqube-scan-action@v5.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/README.md b/README.md index 8e40c68..d73a031 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements * php: ^8.2 - * [chubbyphp/chubbyphp-http-exception][20]: ^1.1 + * [chubbyphp/chubbyphp-http-exception][20]: ^1.2 * [psr/container][21]: ^1.1.2|^2.0.2 * [psr/http-factory-implementation][22]: ^1.0 * [psr/http-factory][23]: ^1.1 diff --git a/composer.json b/composer.json index e36f7af..f5c919b 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ ], "require": { "php": "^8.2", - "chubbyphp/chubbyphp-http-exception": "^1.1", + "chubbyphp/chubbyphp-http-exception": "^1.2", "psr/container": "^1.1.2|^2.0.2", "psr/http-factory-implementation": "^1.0", "psr/http-factory": "^1.1", @@ -35,18 +35,18 @@ }, "require-dev": { "chubbyphp/chubbyphp-dev-helper": "dev-master", - "chubbyphp/chubbyphp-mock": "^1.8", + "chubbyphp/chubbyphp-mock": "^2.0@dev", "guzzlehttp/psr7": "^2.7", "http-interop/http-factory-guzzle": "^1.2", - "infection/infection": "^0.29.8", + "infection/infection": "^0.29.13", "laminas/laminas-diactoros": "^3.5", "nyholm/psr7": "^1.8.2", "php-coveralls/php-coveralls": "^2.7.0", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.0.3", - "phpunit/phpunit": "^11.5.0", + "phpstan/phpstan": "^2.1.6", + "phpunit/phpunit": "^11.5.10", "slim/psr7": "^1.7", - "sunrise/http-message": "^3.2" + "sunrise/http-message": "^3.4.2" }, "autoload": { "psr-4": { "Chubbyphp\\Framework\\": "src/" } diff --git a/tests/Integration/MiddlewareDispatcherTest.php b/tests/Integration/MiddlewareDispatcherTest.php index e735158..94c05a1 100644 --- a/tests/Integration/MiddlewareDispatcherTest.php +++ b/tests/Integration/MiddlewareDispatcherTest.php @@ -9,7 +9,6 @@ use Chubbyphp\Framework\Middleware\SlimCallbackMiddleware; use Chubbyphp\Framework\RequestHandler\CallbackRequestHandler; use Chubbyphp\Framework\RequestHandler\SlimCallbackRequestHandler; -use Chubbyphp\Mock\MockByCallsTrait; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -24,8 +23,6 @@ */ final class MiddlewareDispatcherTest extends TestCase { - use MockByCallsTrait; - public function testCallback(): void { $responseFactory = new SlimResponseFactory(); diff --git a/tests/Unit/ApplicationTest.php b/tests/Unit/ApplicationTest.php index f09f0db..1db0b27 100644 --- a/tests/Unit/ApplicationTest.php +++ b/tests/Unit/ApplicationTest.php @@ -8,8 +8,10 @@ use Chubbyphp\Framework\Emitter\EmitterInterface; use Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface; use Chubbyphp\Framework\Router\RouteInterface; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; +use Chubbyphp\Mock\MockMethod\WithCallback; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; @@ -24,52 +26,48 @@ */ final class ApplicationTest extends TestCase { - use MockByCallsTrait; - public function testInvoke(): void { - /** @var MiddlewareInterface|MockObject $routeIndependMiddleware */ - $routeIndependMiddleware = $this->getMockByCalls(MiddlewareInterface::class); + $builder = new MockObjectBuilder(); + + /** @var MiddlewareInterface $routeIndependentMiddleware */ + $routeIndependentMiddleware = $builder->create(MiddlewareInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware */ - $middleware = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware */ + $middleware = $builder->create(MiddlewareInterface::class, []); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $handler = $builder->create(RequestHandlerInterface::class, []); /** @var MockObject|RouteInterface $route */ - $route = $this->getMockByCalls(RouteInterface::class, [ - Call::create('getMiddlewares')->with()->willReturn([$middleware]), - Call::create('getRequestHandler')->with()->willReturn($handler), + $route = $builder->create(RouteInterface::class, [ + new WithReturn('getMiddlewares', [], [$middleware]), + new WithReturn('getRequestHandler', [], $handler), ]); /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('route', null)->willReturn($route), + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['route', null], $route), ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - - /** @var MiddlewareDispatcherInterface|MockObject $middlewareDispatcher */ - $middlewareDispatcher = $this->getMockByCalls(MiddlewareDispatcherInterface::class, [ - Call::create('dispatch') - ->willReturnCallback( - static function ( - array $middlewares, - RequestHandlerInterface $requestHandler, - ServerRequestInterface $request - ) use ($routeIndependMiddleware) { - self::assertSame([$routeIndependMiddleware], $middlewares); - - return $requestHandler->handle($request); - } - ), - Call::create('dispatch')->with([$middleware], $handler, $request)->willReturn($response), + $response = $builder->create(ResponseInterface::class, []); + + /** @var MiddlewareDispatcherInterface $middlewareDispatcher */ + $middlewareDispatcher = $builder->create(MiddlewareDispatcherInterface::class, [ + new WithCallback( + 'dispatch', + static function (array $middlewares, RequestHandlerInterface $requestHandler, ServerRequestInterface $req) use ($routeIndependentMiddleware): ResponseInterface { + TestCase::assertSame([$routeIndependentMiddleware], $middlewares); + + return $requestHandler->handle($req); + } + ), + new WithReturn('dispatch', [[$middleware], $handler, $request], $response), ]); $application = new Application([ - $routeIndependMiddleware, + $routeIndependentMiddleware, ], $middlewareDispatcher); self::assertSame($response, $application($request)); @@ -77,51 +75,52 @@ static function ( public function testHandle(): void { - /** @var MiddlewareInterface|MockObject $routeIndependMiddleware */ - $routeIndependMiddleware = $this->getMockByCalls(MiddlewareInterface::class); + $builder = new MockObjectBuilder(); + + /** @var MiddlewareInterface $routeIndependentMiddleware */ + $routeIndependentMiddleware = $builder->create(MiddlewareInterface::class, []); /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(ServerRequestInterface::class, []); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - - /** @var MiddlewareDispatcherInterface|MockObject $middlewareDispatcher */ - $middlewareDispatcher = $this->getMockByCalls(MiddlewareDispatcherInterface::class, [ - Call::create('dispatch') - ->willReturnCallback( - static function ( - array $middlewares, - RequestHandlerInterface $requestHandler, - ServerRequestInterface $request - ) use ($routeIndependMiddleware) { - self::assertSame([$routeIndependMiddleware], $middlewares); - - return $requestHandler->handle($request); - } - ), + $response = $builder->create(ResponseInterface::class, []); + + /** @var MiddlewareDispatcherInterface $middlewareDispatcher */ + $middlewareDispatcher = $builder->create(MiddlewareDispatcherInterface::class, [ + new WithCallback( + 'dispatch', + static function (array $middlewares, RequestHandlerInterface $requestHandler, ServerRequestInterface $req) use ($routeIndependentMiddleware): ResponseInterface { + TestCase::assertSame([$routeIndependentMiddleware], $middlewares); + + return $requestHandler->handle($req); + } + ), ]); /** @var MockObject|RequestHandlerInterface $requestHandler */ - $requestHandler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + $requestHandler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); $application = new Application([ - $routeIndependMiddleware, + $routeIndependentMiddleware, ], $middlewareDispatcher, $requestHandler); self::assertSame($response, $application->handle($request)); } + #[DoesNotPerformAssertions] public function testEmit(): void { + $builder = new MockObjectBuilder(); + /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $response = $builder->create(ResponseInterface::class, []); - /** @var EmitterInterface|MockObject $emitter */ - $emitter = $this->getMockByCalls(EmitterInterface::class, [ - Call::create('emit')->with($response), + /** @var EmitterInterface $emitter */ + $emitter = $builder->create(EmitterInterface::class, [ + new WithReturn('emit', [$response], null), ]); $application = new Application([], null, null, $emitter); diff --git a/tests/Unit/Emitter/EmitterTest.php b/tests/Unit/Emitter/EmitterTest.php index d0de91f..f40abf7 100644 --- a/tests/Unit/Emitter/EmitterTest.php +++ b/tests/Unit/Emitter/EmitterTest.php @@ -38,9 +38,9 @@ function header(string $header, bool $replace = true, ?int $http_response_code = { use Chubbyphp\Framework\Emitter\Emitter; use Chubbyphp\Framework\Emitter\TestHeader; - use Chubbyphp\Mock\Call; - use Chubbyphp\Mock\MockByCallsTrait; - use PHPUnit\Framework\MockObject\MockObject; + use Chubbyphp\Mock\MockMethod\WithCallback; + use Chubbyphp\Mock\MockMethod\WithReturn; + use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -52,26 +52,26 @@ function header(string $header, bool $replace = true, ?int $http_response_code = */ final class EmitterTest extends TestCase { - use MockByCallsTrait; - public function testEmit(): void { - /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('isSeekable')->with()->willReturn(true), - Call::create('rewind')->with(), - Call::create('eof')->with()->willReturn(false), - Call::create('read')->with(256)->willReturn('sample body'), - Call::create('eof')->with()->willReturn(true), + $builder = new MockObjectBuilder(); + + /** @var StreamInterface $responseBody */ + $responseBody = $builder->create(StreamInterface::class, [ + new WithReturn('isSeekable', [], true), + new WithCallback('rewind', static fn () => null), + new WithReturn('eof', [], false), + new WithReturn('read', [256], 'sample body'), + new WithReturn('eof', [], true), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class, [ - Call::create('getStatusCode')->with()->willReturn(200), - Call::create('getProtocolVersion')->with()->willReturn('1.1'), - Call::create('getReasonPhrase')->with()->willReturn('OK'), - Call::create('getHeaders')->with()->willReturn(['X-Name' => ['value1', 'value2']]), - Call::create('getBody')->with()->willReturn($responseBody), + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, [ + new WithReturn('getStatusCode', [], 200), + new WithReturn('getProtocolVersion', [], '1.1'), + new WithReturn('getReasonPhrase', [], 'OK'), + new WithReturn('getHeaders', [], ['X-Name' => ['value1', 'value2']]), + new WithReturn('getBody', [], $responseBody), ]); $emitter = new Emitter(); diff --git a/tests/Unit/Middleware/CallbackMiddlewareTest.php b/tests/Unit/Middleware/CallbackMiddlewareTest.php index c7de728..cd9c20f 100644 --- a/tests/Unit/Middleware/CallbackMiddlewareTest.php +++ b/tests/Unit/Middleware/CallbackMiddlewareTest.php @@ -5,9 +5,8 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\CallbackMiddleware; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -20,19 +19,19 @@ */ final class CallbackMiddlewareTest extends TestCase { - use MockByCallsTrait; - public function testHandle(): void { - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); $middleware = new CallbackMiddleware( diff --git a/tests/Unit/Middleware/ExceptionMiddlewareTest.php b/tests/Unit/Middleware/ExceptionMiddlewareTest.php index 73af9dc..7e2bd27 100644 --- a/tests/Unit/Middleware/ExceptionMiddlewareTest.php +++ b/tests/Unit/Middleware/ExceptionMiddlewareTest.php @@ -6,9 +6,12 @@ use Chubbyphp\Framework\Middleware\ExceptionMiddleware; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Mock\Argument\ArgumentCallback; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; +use Chubbyphp\Mock\MockMethod\WithCallback; +use Chubbyphp\Mock\MockMethod\WithException; +use Chubbyphp\Mock\MockMethod\WithoutReturn; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockMethod\WithReturnSelf; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseFactoryInterface; @@ -25,23 +28,23 @@ */ final class ExceptionMiddlewareTest extends TestCase { - use MockByCallsTrait; - public function testProcess(): void { + $builder = new MockObjectBuilder(); + /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(ServerRequestInterface::class, []); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $response = $builder->create(ResponseInterface::class, []); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + $responseFactory = $builder->create(ResponseFactoryInterface::class, []); $middleware = new ExceptionMiddleware($responseFactory); @@ -55,8 +58,10 @@ public function testProcessWithClientHttpExceptionWithoutLogger(): void 'key2' => 'value2', ], new \LogicException('logic exception', 42)); + $builder = new MockObjectBuilder(); + /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(ServerRequestInterface::class, []); $expectedBody = <<<'EOT' @@ -206,24 +211,24 @@ public function testProcessWithClientHttpExceptionWithoutLogger(): void EOT; /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write')->with($expectedBody), + $responseBody = $builder->create(StreamInterface::class, [ + new WithoutReturn('write', [$expectedBody]), ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class, [ - Call::create('withHeader')->with('Content-Type', 'text/html')->willReturnSelf(), - Call::create('getBody')->with()->willReturn($responseBody), + $response = $builder->create(ResponseInterface::class, [ + new WithReturnSelf('withHeader', ['Content-Type', 'text/html']), + new WithReturn('getBody', [], $responseBody), ]); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willThrowException($httpException), + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithException('handle', [$request], $httpException), ]); /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(418, '')->willReturn($response), + $responseFactory = $builder->create(ResponseFactoryInterface::class, [ + new WithReturn('createResponse', [418, ''], $response), ]); $middleware = new ExceptionMiddleware($responseFactory); @@ -238,8 +243,10 @@ public function testProcessWithClientHttpExceptionWithLogger(): void 'instance' => 'instance-1234', ], new \LogicException('logic exception', 42)); + $builder = new MockObjectBuilder(); + /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(ServerRequestInterface::class, []); $expectedBody = <<<'EOT' @@ -389,63 +396,61 @@ public function testProcessWithClientHttpExceptionWithLogger(): void EOT; /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write')->with($expectedBody), + $responseBody = $builder->create(StreamInterface::class, [ + new WithoutReturn('write', [$expectedBody]), ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class, [ - Call::create('withHeader')->with('Content-Type', 'text/html')->willReturnSelf(), - Call::create('getBody')->with()->willReturn($responseBody), + $response = $builder->create(ResponseInterface::class, [ + new WithReturnSelf('withHeader', ['Content-Type', 'text/html']), + new WithReturn('getBody', [], $responseBody), ]); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willThrowException($httpException), + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithException('handle', [$request], $httpException), ]); /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(404, '')->willReturn($response), + $responseFactory = $builder->create(ResponseFactoryInterface::class, [ + new WithReturn('createResponse', [404, ''], $response), ]); /** @var LoggerInterface|MockObject $logger */ - $logger = $this->getMockByCalls(LoggerInterface::class, [ - Call::create('info')->with( - 'Http Exception', - new ArgumentCallback(static function (array $context): void { - self::assertArrayHasKey('data', $context); - $data = $context['data']; - self::assertSame([ - 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.5', - 'status' => 404, - 'title' => 'Not Found', - 'detail' => 'Could not found route "/unknown"', - 'instance' => 'instance-1234', - ], $data); - self::assertArrayHasKey('exceptions', $context); - $exceptions = $context['exceptions']; - self::assertCount(2, $exceptions); - $exception1 = $exceptions[0]; - self::assertArrayHasKey('message', $exception1); - self::assertArrayHasKey('code', $exception1); - self::assertArrayHasKey('file', $exception1); - self::assertArrayHasKey('line', $exception1); - self::assertArrayHasKey('trace', $exception1); - self::assertSame(HttpException::class, $exception1['class']); - self::assertSame('Not Found', $exception1['message']); - self::assertSame(404, $exception1['code']); - $exception2 = $exceptions[1]; - self::assertArrayHasKey('message', $exception2); - self::assertArrayHasKey('code', $exception2); - self::assertArrayHasKey('file', $exception2); - self::assertArrayHasKey('line', $exception2); - self::assertArrayHasKey('trace', $exception2); - self::assertSame('LogicException', $exception2['class']); - self::assertSame('logic exception', $exception2['message']); - self::assertSame(42, $exception2['code']); - }) - ), + $logger = $builder->create(LoggerInterface::class, [ + new WithCallback('info', static function (string $message, array $context): void { + self::assertSame('Http Exception', $message); + self::assertArrayHasKey('data', $context); + $data = $context['data']; + self::assertSame([ + 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.5', + 'status' => 404, + 'title' => 'Not Found', + 'detail' => 'Could not found route "/unknown"', + 'instance' => 'instance-1234', + ], $data); + self::assertArrayHasKey('exceptions', $context); + $exceptions = $context['exceptions']; + self::assertCount(2, $exceptions); + $exception1 = $exceptions[0]; + self::assertArrayHasKey('message', $exception1); + self::assertArrayHasKey('code', $exception1); + self::assertArrayHasKey('file', $exception1); + self::assertArrayHasKey('line', $exception1); + self::assertArrayHasKey('trace', $exception1); + self::assertSame(HttpException::class, $exception1['class']); + self::assertSame('Not Found', $exception1['message']); + self::assertSame(404, $exception1['code']); + $exception2 = $exceptions[1]; + self::assertArrayHasKey('message', $exception2); + self::assertArrayHasKey('code', $exception2); + self::assertArrayHasKey('file', $exception2); + self::assertArrayHasKey('line', $exception2); + self::assertArrayHasKey('trace', $exception2); + self::assertSame('LogicException', $exception2['class']); + self::assertSame('logic exception', $exception2['message']); + self::assertSame(42, $exception2['code']); + }), ]); $middleware = new ExceptionMiddleware($responseFactory, false, $logger); @@ -457,8 +462,10 @@ public function testProcessWithServerHttpExceptionWithDebugWithLogger(): void { $httpException = HttpException::createInternalServerError([], new \LogicException('logic exception', 42)); + $builder = new MockObjectBuilder(); + /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(ServerRequestInterface::class, []); $expectedBody = <<<'EOT' @@ -608,63 +615,61 @@ public function testProcessWithServerHttpExceptionWithDebugWithLogger(): void EOT; /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write')->with($expectedBody), + $responseBody = $builder->create(StreamInterface::class, [ + new WithoutReturn('write', [$expectedBody]), ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class, [ - Call::create('withHeader')->with('Content-Type', 'text/html')->willReturnSelf(), - Call::create('getBody')->with()->willReturn($responseBody), + $response = $builder->create(ResponseInterface::class, [ + new WithReturnSelf('withHeader', ['Content-Type', 'text/html']), + new WithReturn('getBody', [], $responseBody), ]); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willThrowException($httpException), + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithException('handle', [$request], $httpException), ]); /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(500, '')->willReturn($response), + $responseFactory = $builder->create(ResponseFactoryInterface::class, [ + new WithReturn('createResponse', [500, ''], $response), ]); /** @var LoggerInterface|MockObject $logger */ - $logger = $this->getMockByCalls(LoggerInterface::class, [ - Call::create('error')->with( - 'Http Exception', - new ArgumentCallback(static function (array $context): void { - self::assertArrayHasKey('data', $context); - $data = $context['data']; - self::assertSame([ - 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', - 'status' => 500, - 'title' => 'Internal Server Error', - 'detail' => null, - 'instance' => null, - ], $data); - self::assertArrayHasKey('exceptions', $context); - $exceptions = $context['exceptions']; - self::assertCount(2, $exceptions); - $runtimeException = $exceptions[0]; - self::assertArrayHasKey('message', $runtimeException); - self::assertArrayHasKey('code', $runtimeException); - self::assertArrayHasKey('file', $runtimeException); - self::assertArrayHasKey('line', $runtimeException); - self::assertArrayHasKey('trace', $runtimeException); - self::assertSame(HttpException::class, $runtimeException['class']); - self::assertSame('Internal Server Error', $runtimeException['message']); - self::assertSame(500, $runtimeException['code']); - $logicException = $exceptions[1]; - self::assertArrayHasKey('message', $logicException); - self::assertArrayHasKey('code', $logicException); - self::assertArrayHasKey('file', $logicException); - self::assertArrayHasKey('line', $logicException); - self::assertArrayHasKey('trace', $logicException); - self::assertSame('LogicException', $logicException['class']); - self::assertSame('logic exception', $logicException['message']); - self::assertSame(42, $logicException['code']); - }) - ), + $logger = $builder->create(LoggerInterface::class, [ + new WithCallback('error', static function (string $message, array $context): void { + self::assertSame('Http Exception', $message); + self::assertArrayHasKey('data', $context); + $data = $context['data']; + self::assertSame([ + 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', + 'status' => 500, + 'title' => 'Internal Server Error', + 'detail' => null, + 'instance' => null, + ], $data); + self::assertArrayHasKey('exceptions', $context); + $exceptions = $context['exceptions']; + self::assertCount(2, $exceptions); + $runtimeException = $exceptions[0]; + self::assertArrayHasKey('message', $runtimeException); + self::assertArrayHasKey('code', $runtimeException); + self::assertArrayHasKey('file', $runtimeException); + self::assertArrayHasKey('line', $runtimeException); + self::assertArrayHasKey('trace', $runtimeException); + self::assertSame(HttpException::class, $runtimeException['class']); + self::assertSame('Internal Server Error', $runtimeException['message']); + self::assertSame(500, $runtimeException['code']); + $logicException = $exceptions[1]; + self::assertArrayHasKey('message', $logicException); + self::assertArrayHasKey('code', $logicException); + self::assertArrayHasKey('file', $logicException); + self::assertArrayHasKey('line', $logicException); + self::assertArrayHasKey('trace', $logicException); + self::assertSame('LogicException', $logicException['class']); + self::assertSame('logic exception', $logicException['message']); + self::assertSame(42, $logicException['code']); + }), ]); $middleware = new ExceptionMiddleware($responseFactory, false, $logger); @@ -676,95 +681,94 @@ public function testProcessWithExceptionWithDebugWithLogger(): void { $exception = new \RuntimeException('runtime exception', 418, new \LogicException('logic exception', 42)); + $builder = new MockObjectBuilder(); + /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(ServerRequestInterface::class, []); /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write') - ->with(new ArgumentCallback(static function (string $html): void { - self::assertStringContainsString( - '

A website error has occurred. Sorry for the temporary inconvenience.

', - $html - ); - self::assertStringContainsString('
Class
', $html); - self::assertStringContainsString('
RuntimeException
', $html); - self::assertStringContainsString('
Message
', $html); - self::assertStringContainsString('
runtime exception
', $html); - self::assertStringContainsString('
Code
', $html); - self::assertStringContainsString('
418
', $html); - - self::assertStringContainsString('
Class
', $html); - self::assertStringContainsString('
LogicException
', $html); - self::assertStringContainsString('
Message
', $html); - self::assertStringContainsString('
logic exception
', $html); - self::assertStringContainsString('
Code
', $html); - self::assertStringContainsString('
42
', $html); - })), + $responseBody = $builder->create(StreamInterface::class, [ + new WithCallback('write', static function (string $html): void { + self::assertStringContainsString( + '

A website error has occurred. Sorry for the temporary inconvenience.

', + $html + ); + self::assertStringContainsString('
Class
', $html); + self::assertStringContainsString('
RuntimeException
', $html); + self::assertStringContainsString('
Message
', $html); + self::assertStringContainsString('
runtime exception
', $html); + self::assertStringContainsString('
Code
', $html); + self::assertStringContainsString('
418
', $html); + + self::assertStringContainsString('
Class
', $html); + self::assertStringContainsString('
LogicException
', $html); + self::assertStringContainsString('
Message
', $html); + self::assertStringContainsString('
logic exception
', $html); + self::assertStringContainsString('
Code
', $html); + self::assertStringContainsString('
42
', $html); + }), ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class, [ - Call::create('withHeader')->with('Content-Type', 'text/html')->willReturnSelf(), - Call::create('getBody')->with()->willReturn($responseBody), + $response = $builder->create(ResponseInterface::class, [ + new WithReturnSelf('withHeader', ['Content-Type', 'text/html']), + new WithReturn('getBody', [], $responseBody), ]); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willThrowException($exception), + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithException('handle', [$request], $exception), ]); /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(500, '')->willReturn($response), + $responseFactory = $builder->create(ResponseFactoryInterface::class, [ + new WithReturn('createResponse', [500, ''], $response), ]); /** @var LoggerInterface|MockObject $logger */ - $logger = $this->getMockByCalls(LoggerInterface::class, [ - Call::create('error')->with( - 'Http Exception', - new ArgumentCallback(static function (array $context): void { - self::assertArrayHasKey('data', $context); - $data = $context['data']; - self::assertSame([ - 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', - 'status' => 500, - 'title' => 'Internal Server Error', - 'detail' => 'A website error has occurred. Sorry for the temporary inconvenience.', - 'instance' => null, - ], $data); - self::assertArrayHasKey('exceptions', $context); - $exceptions = $context['exceptions']; - self::assertCount(3, $exceptions); - $exception1 = $exceptions[0]; - self::assertArrayHasKey('message', $exception1); - self::assertArrayHasKey('code', $exception1); - self::assertArrayHasKey('file', $exception1); - self::assertArrayHasKey('line', $exception1); - self::assertArrayHasKey('trace', $exception1); - self::assertSame(HttpException::class, $exception1['class']); - self::assertSame('Internal Server Error', $exception1['message']); - self::assertSame(500, $exception1['code']); - $exception2 = $exceptions[1]; - self::assertArrayHasKey('message', $exception2); - self::assertArrayHasKey('code', $exception2); - self::assertArrayHasKey('file', $exception2); - self::assertArrayHasKey('line', $exception2); - self::assertArrayHasKey('trace', $exception2); - self::assertSame('RuntimeException', $exception2['class']); - self::assertSame('runtime exception', $exception2['message']); - self::assertSame(418, $exception2['code']); - $exception3 = $exceptions[2]; - self::assertArrayHasKey('message', $exception3); - self::assertArrayHasKey('code', $exception3); - self::assertArrayHasKey('file', $exception3); - self::assertArrayHasKey('line', $exception3); - self::assertArrayHasKey('trace', $exception3); - self::assertSame('LogicException', $exception3['class']); - self::assertSame('logic exception', $exception3['message']); - self::assertSame(42, $exception3['code']); - }) - ), + $logger = $builder->create(LoggerInterface::class, [ + new WithCallback('error', static function (string $message, array $context): void { + self::assertSame('Http Exception', $message); + self::assertArrayHasKey('data', $context); + $data = $context['data']; + self::assertSame([ + 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', + 'status' => 500, + 'title' => 'Internal Server Error', + 'detail' => 'A website error has occurred. Sorry for the temporary inconvenience.', + 'instance' => null, + ], $data); + self::assertArrayHasKey('exceptions', $context); + $exceptions = $context['exceptions']; + self::assertCount(3, $exceptions); + $exception1 = $exceptions[0]; + self::assertArrayHasKey('message', $exception1); + self::assertArrayHasKey('code', $exception1); + self::assertArrayHasKey('file', $exception1); + self::assertArrayHasKey('line', $exception1); + self::assertArrayHasKey('trace', $exception1); + self::assertSame(HttpException::class, $exception1['class']); + self::assertSame('Internal Server Error', $exception1['message']); + self::assertSame(500, $exception1['code']); + $exception2 = $exceptions[1]; + self::assertArrayHasKey('message', $exception2); + self::assertArrayHasKey('code', $exception2); + self::assertArrayHasKey('file', $exception2); + self::assertArrayHasKey('line', $exception2); + self::assertArrayHasKey('trace', $exception2); + self::assertSame('RuntimeException', $exception2['class']); + self::assertSame('runtime exception', $exception2['message']); + self::assertSame(418, $exception2['code']); + $exception3 = $exceptions[2]; + self::assertArrayHasKey('message', $exception3); + self::assertArrayHasKey('code', $exception3); + self::assertArrayHasKey('file', $exception3); + self::assertArrayHasKey('line', $exception3); + self::assertArrayHasKey('trace', $exception3); + self::assertSame('LogicException', $exception3['class']); + self::assertSame('logic exception', $exception3['message']); + self::assertSame(42, $exception3['code']); + }), ]); $middleware = new ExceptionMiddleware($responseFactory, true, $logger); diff --git a/tests/Unit/Middleware/LazyMiddlewareTest.php b/tests/Unit/Middleware/LazyMiddlewareTest.php index bdda00b..e06ae6a 100644 --- a/tests/Unit/Middleware/LazyMiddlewareTest.php +++ b/tests/Unit/Middleware/LazyMiddlewareTest.php @@ -5,9 +5,8 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\LazyMiddleware; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; @@ -22,27 +21,27 @@ */ final class LazyMiddlewareTest extends TestCase { - use MockByCallsTrait; - public function testProcess(): void { - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware */ - $middleware = $this->getMockByCalls(MiddlewareInterface::class, [ - Call::create('process')->with($request, $handler)->willReturn($response), + /** @var MiddlewareInterface $originalMiddleware */ + $originalMiddleware = $builder->create(MiddlewareInterface::class, [ + new WithReturn('process', [$request, $handler], $response), ]); - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($middleware), + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalMiddleware), ]); $middleware = new LazyMiddleware($container, 'serviceName'); @@ -58,17 +57,19 @@ public function testProcessWithWrongObject(): void .' to be Psr\Http\Server\MiddlewareInterface, stdClass given' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - $middleware = new \stdClass(); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($middleware), + $originalMiddleware = new \stdClass(); + + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalMiddleware), ]); $middleware = new LazyMiddleware($container, 'serviceName'); @@ -83,17 +84,19 @@ public function testProcessWithString(): void .' to be Psr\Http\Server\MiddlewareInterface, string given' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - $middleware = ''; + $originalMiddleware = ''; - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($middleware), + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalMiddleware), ]); $middleware = new LazyMiddleware($container, 'serviceName'); diff --git a/tests/Unit/Middleware/MiddlewareDispatcherTest.php b/tests/Unit/Middleware/MiddlewareDispatcherTest.php index 8f989cd..2dda21b 100644 --- a/tests/Unit/Middleware/MiddlewareDispatcherTest.php +++ b/tests/Unit/Middleware/MiddlewareDispatcherTest.php @@ -5,11 +5,10 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\MiddlewareDispatcher; -use Chubbyphp\Framework\Middleware\MiddlewareRequestHandler; -use Chubbyphp\Mock\Argument\ArgumentInstanceOf; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithCallback; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockMethod\WithReturnSelf; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -23,19 +22,19 @@ */ final class MiddlewareDispatcherTest extends TestCase { - use MockByCallsTrait; - public function testWithoutMiddlewares(): void { - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); $middlewareDispatcher = new MiddlewareDispatcher(); @@ -45,44 +44,50 @@ public function testWithoutMiddlewares(): void public function testWithMiddlewares(): void { - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('withAttribute')->with('middleware', 1)->willReturnSelf(), - Call::create('withAttribute')->with('middleware', 2)->willReturnSelf(), + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturnSelf('withAttribute', ['middleware', 1]), + new WithReturnSelf('withAttribute', ['middleware', 2]), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class, [ - Call::create('process') - ->with($request, new ArgumentInstanceOf(MiddlewareRequestHandler::class)) - ->willReturnCallback( - static function (ServerRequestInterface $request, RequestHandlerInterface $handler) { - $request->withAttribute('middleware', 1); - - return $handler->handle($request); - } - ), + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static function ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) { + $request = $request->withAttribute('middleware', 1); + + return $requestHandler->handle($request); + }, + ), ]); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class, [ - Call::create('process') - ->with($request, $handler) - ->willReturnCallback( - static function (ServerRequestInterface $request, RequestHandlerInterface $handler) { - $request->withAttribute('middleware', 2); - - return $handler->handle($request); - } - ), + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static function ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) { + $request = $request->withAttribute('middleware', 2); + + return $requestHandler->handle($request); + }, + ), ]); $middlewareDispatcher = new MiddlewareDispatcher(); diff --git a/tests/Unit/Middleware/MiddlewareRequestHandlerTest.php b/tests/Unit/Middleware/MiddlewareRequestHandlerTest.php index 887a00e..89040d2 100644 --- a/tests/Unit/Middleware/MiddlewareRequestHandlerTest.php +++ b/tests/Unit/Middleware/MiddlewareRequestHandlerTest.php @@ -5,9 +5,9 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\MiddlewareRequestHandler; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithCallback; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -21,28 +21,27 @@ */ final class MiddlewareRequestHandlerTest extends TestCase { - use MockByCallsTrait; - public function testHandle(): void { - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); - /** @var MiddlewareInterface|MockObject $middleware */ - $middleware = $this->getMockByCalls(MiddlewareInterface::class, [ - Call::create('process') - ->with($request, $handler) - ->willReturnCallback( - static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request) - ), + /** @var MiddlewareInterface $middleware */ + $middleware = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static fn (ServerRequestInterface $request, RequestHandlerInterface $requestHandler) => $requestHandler->handle($request) + ), ]); $middlewareRequestHandler = new MiddlewareRequestHandler($middleware, $handler); diff --git a/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php b/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php index b9f64d5..3c0f81a 100644 --- a/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php +++ b/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php @@ -8,9 +8,10 @@ use Chubbyphp\Framework\Router\RouteInterface; use Chubbyphp\Framework\Router\RouteMatcherInterface; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithException; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockMethod\WithReturnSelf; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -23,32 +24,32 @@ */ final class RouteMatcherMiddlewareTest extends TestCase { - use MockByCallsTrait; - public function testProcess(): void { - /** @var MockObject|RouteInterface $route */ - $route = $this->getMockByCalls(RouteInterface::class, [ - Call::create('getAttributes')->with()->willReturn(['key' => 'value']), + $builder = new MockObjectBuilder(); + + /** @var RouteInterface $route */ + $route = $builder->create(RouteInterface::class, [ + new WithReturn('getAttributes', [], ['key' => 'value']), ]); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('withAttribute')->with('route', $route)->willReturnSelf(), - Call::create('withAttribute')->with('key', 'value')->willReturnSelf(), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturnSelf('withAttribute', ['route', $route]), + new WithReturnSelf('withAttribute', ['key', 'value']), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); - /** @var MockObject|RouteMatcherInterface $router */ - $router = $this->getMockByCalls(RouteMatcherInterface::class, [ - Call::create('match')->with($request)->willReturn($route), + /** @var RouteMatcherInterface $router */ + $router = $builder->create(RouteMatcherInterface::class, [ + new WithReturn('match', [$request], $route), ]); $middleware = new RouteMatcherMiddleware($router); @@ -64,15 +65,17 @@ public function testProcessWithException(): void $this->expectExceptionObject($httpException); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MockObject|RouteMatcherInterface $router */ - $router = $this->getMockByCalls(RouteMatcherInterface::class, [ - Call::create('match')->with($request)->willThrowException($httpException), + /** @var RouteMatcherInterface $router */ + $router = $builder->create(RouteMatcherInterface::class, [ + new WithException('match', [$request], $httpException), ]); $middleware = new RouteMatcherMiddleware($router); diff --git a/tests/Unit/Middleware/SlimCallbackMiddlewareTest.php b/tests/Unit/Middleware/SlimCallbackMiddlewareTest.php index d253dd4..0befb58 100644 --- a/tests/Unit/Middleware/SlimCallbackMiddlewareTest.php +++ b/tests/Unit/Middleware/SlimCallbackMiddlewareTest.php @@ -5,9 +5,9 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\SlimCallbackMiddleware; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockMethod\WithReturnSelf; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; @@ -21,27 +21,27 @@ */ final class SlimCallbackMiddlewareTest extends TestCase { - use MockByCallsTrait; - public function testProcessWithoutExistingResponse(): void { - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('response', null)->willReturn(null), - Call::create('withAttribute')->with('response', $response)->willReturnSelf(), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['response', null], null), + new WithReturnSelf('withAttribute', ['response', $response]), ]); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(200, '')->willReturn($response), + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, [ + new WithReturn('createResponse', [200, ''], $response), ]); $middleware = new SlimCallbackMiddleware( @@ -63,22 +63,24 @@ static function ( public function testProcessWithExistingResponse(): void { - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('response', null)->willReturn($response), - Call::create('withAttribute')->with('response', $response)->willReturnSelf(), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['response', null], $response), + new WithReturnSelf('withAttribute', ['response', $response]), ]); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, []); $middleware = new SlimCallbackMiddleware( static function ( diff --git a/tests/Unit/Middleware/SlimLazyMiddlewareTest.php b/tests/Unit/Middleware/SlimLazyMiddlewareTest.php index d4570d0..2160eb8 100644 --- a/tests/Unit/Middleware/SlimLazyMiddlewareTest.php +++ b/tests/Unit/Middleware/SlimLazyMiddlewareTest.php @@ -5,9 +5,9 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\SlimLazyMiddleware; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockMethod\WithReturnSelf; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseFactoryInterface; @@ -22,34 +22,38 @@ */ final class SlimLazyMiddlewareTest extends TestCase { - use MockByCallsTrait; - public function testProcess(): void { - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('response', null)->willReturn(null), - Call::create('withAttribute')->with('response', $response)->willReturnSelf(), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['response', null], null), + new WithReturnSelf('withAttribute', ['response', $response]), ]); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); - $middleware = static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req, $res); + $originalMiddleware = static fn ( + ServerRequestInterface $req, + ResponseInterface $res, + callable $next + ): ResponseInterface => $next($req, $res); - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($middleware), + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalMiddleware), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(200, '')->willReturn($response), + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, [ + new WithReturn('createResponse', [200, ''], $response), ]); $middleware = new SlimLazyMiddleware($container, 'serviceName', $responseFactory); @@ -65,21 +69,23 @@ public function testProcessWithWrongObject(): void .' to be callable, stdClass given' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - $middleware = new \stdClass(); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($middleware), + $originalMiddleware = new \stdClass(); + + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalMiddleware), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, []); $middleware = new SlimLazyMiddleware($container, 'serviceName', $responseFactory); $middleware->process($request, $handler); @@ -93,21 +99,23 @@ public function testProcessWithString(): void .' to be callable, string given' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - $middleware = ''; + $originalMiddleware = ''; - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($middleware), + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalMiddleware), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, []); $middleware = new SlimLazyMiddleware($container, 'serviceName', $responseFactory); $middleware->process($request, $handler); diff --git a/tests/Unit/RequestHandler/CallbackRequestHandlerTest.php b/tests/Unit/RequestHandler/CallbackRequestHandlerTest.php index d927b3f..5973807 100644 --- a/tests/Unit/RequestHandler/CallbackRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/CallbackRequestHandlerTest.php @@ -5,8 +5,7 @@ namespace Chubbyphp\Tests\Framework\Unit\RequestHandler; use Chubbyphp\Framework\RequestHandler\CallbackRequestHandler; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -18,15 +17,15 @@ */ final class CallbackRequestHandlerTest extends TestCase { - use MockByCallsTrait; - public function testHandle(): void { - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); $requestHandler = new CallbackRequestHandler(static fn (ServerRequestInterface $request) => $response); diff --git a/tests/Unit/RequestHandler/LazyRequestHandlerTest.php b/tests/Unit/RequestHandler/LazyRequestHandlerTest.php index 2fbaeca..7ef01f0 100644 --- a/tests/Unit/RequestHandler/LazyRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/LazyRequestHandlerTest.php @@ -5,9 +5,8 @@ namespace Chubbyphp\Tests\Framework\Unit\RequestHandler; use Chubbyphp\Framework\RequestHandler\LazyRequestHandler; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; @@ -21,24 +20,24 @@ */ final class LazyRequestHandlerTest extends TestCase { - use MockByCallsTrait; - public function testHandle(): void { - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|RequestHandlerInterface $requestHander */ - $requestHander = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RequestHandlerInterface $originalRequestHandler */ + $originalRequestHandler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($requestHander), + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalRequestHandler), ]); $requestHandler = new LazyRequestHandler($container, 'serviceName'); @@ -54,14 +53,16 @@ public function testHandleWithWrongObject(): void .' to be Psr\Http\Server\RequestHandlerInterface, stdClass given' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); - $requestHander = new \stdClass(); + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($requestHander), + $originalRequestHandler = new \stdClass(); + + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalRequestHandler), ]); $requestHandler = new LazyRequestHandler($container, 'serviceName'); @@ -76,14 +77,16 @@ public function testHandleWithString(): void .' to be Psr\Http\Server\RequestHandlerInterface, string given' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - $requestHander = ''; + $originalRequestHandler = ''; - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($requestHander), + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalRequestHandler), ]); $requestHandler = new LazyRequestHandler($container, 'serviceName'); diff --git a/tests/Unit/RequestHandler/RouteRequestHandlerTest.php b/tests/Unit/RequestHandler/RouteRequestHandlerTest.php index 6c45340..0647b71 100644 --- a/tests/Unit/RequestHandler/RouteRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/RouteRequestHandlerTest.php @@ -8,9 +8,8 @@ use Chubbyphp\Framework\RequestHandler\RouteRequestHandler; use Chubbyphp\Framework\Router\Exceptions\MissingRouteAttributeOnRequestException; use Chubbyphp\Framework\Router\RouteInterface; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -24,8 +23,6 @@ */ final class RouteRequestHandlerTest extends TestCase { - use MockByCallsTrait; - public function testHandleWithoutRoute(): void { $this->expectException(MissingRouteAttributeOnRequestException::class); @@ -34,16 +31,18 @@ public function testHandleWithoutRoute(): void .' "Chubbyphp\Framework\Middleware\RouteMatcherMiddleware" middleware' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('route', null)->willReturn(null), + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['route', null], null), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MiddlewareDispatcherInterface|MockObject $middlewareDispatcher */ - $middlewareDispatcher = $this->getMockByCalls(MiddlewareDispatcherInterface::class); + /** @var MiddlewareDispatcherInterface $middlewareDispatcher */ + $middlewareDispatcher = $builder->create(MiddlewareDispatcherInterface::class, []); $requestHandler = new RouteRequestHandler($middlewareDispatcher); @@ -52,29 +51,31 @@ public function testHandleWithoutRoute(): void public function testHandleWithRoute(): void { - /** @var MiddlewareInterface|MockObject $middleware */ - $middleware = $this->getMockByCalls(MiddlewareInterface::class); + $builder = new MockObjectBuilder(); + + /** @var MiddlewareInterface $middleware */ + $middleware = $builder->create(MiddlewareInterface::class, []); - /** @var MockObject|RequestHandlerInterface $requestHandler */ - $requestHandler = $this->getMockByCalls(RequestHandlerInterface::class); + /** @var RequestHandlerInterface $innerRequestHandler */ + $innerRequestHandler = $builder->create(RequestHandlerInterface::class, []); - /** @var MockObject|RouteInterface $route */ - $route = $this->getMockByCalls(RouteInterface::class, [ - Call::create('getMiddlewares')->with()->willReturn([$middleware]), - Call::create('getRequestHandler')->with()->willReturn($requestHandler), + /** @var RouteInterface $route */ + $route = $builder->create(RouteInterface::class, [ + new WithReturn('getMiddlewares', [], [$middleware]), + new WithReturn('getRequestHandler', [], $innerRequestHandler), ]); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('route', null)->willReturn($route), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['route', null], $route), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MiddlewareDispatcherInterface|MockObject $middlewareDispatcher */ - $middlewareDispatcher = $this->getMockByCalls(MiddlewareDispatcherInterface::class, [ - Call::create('dispatch')->with([$middleware], $requestHandler, $request)->willReturn($response), + /** @var MiddlewareDispatcherInterface $middlewareDispatcher */ + $middlewareDispatcher = $builder->create(MiddlewareDispatcherInterface::class, [ + new WithReturn('dispatch', [[$middleware], $innerRequestHandler, $request], $response), ]); $requestHandler = new RouteRequestHandler($middlewareDispatcher); diff --git a/tests/Unit/RequestHandler/SlimCallbackRequestHandlerTest.php b/tests/Unit/RequestHandler/SlimCallbackRequestHandlerTest.php index 0df13b2..45d600f 100644 --- a/tests/Unit/RequestHandler/SlimCallbackRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/SlimCallbackRequestHandlerTest.php @@ -5,9 +5,8 @@ namespace Chubbyphp\Tests\Framework\Unit\RequestHandler; use Chubbyphp\Framework\RequestHandler\SlimCallbackRequestHandler; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; @@ -20,22 +19,22 @@ */ final class SlimCallbackRequestHandlerTest extends TestCase { - use MockByCallsTrait; - public function testHandleWithoutExistingResponse(): void { - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('response', null)->willReturn(null), - Call::create('getAttributes')->with()->willReturn(['key1' => 'value1', 'key2' => 'value2']), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['response', null], null), + new WithReturn('getAttributes', [], ['key1' => 'value1', 'key2' => 'value2']), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(200, '')->willReturn($response), + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, [ + new WithReturn('createResponse', [200, ''], $response), ]); $requestHandler = new SlimCallbackRequestHandler( @@ -58,19 +57,23 @@ static function ( public function testHandleWithExistingResponse(): void { - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('response', null)->willReturn($response), - Call::create('getAttributes') - ->with() - ->willReturn(['key1' => 'value1', 'key2' => 'value2', 'response' => $response]), + $builder = new MockObjectBuilder(); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['response', null], $response), + new WithReturn( + 'getAttributes', + [], + ['key1' => 'value1', 'key2' => 'value2', 'response' => $response] + ), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, []); $requestHandler = new SlimCallbackRequestHandler( static function ( diff --git a/tests/Unit/RequestHandler/SlimLazyRequestHandlerTest.php b/tests/Unit/RequestHandler/SlimLazyRequestHandlerTest.php index 13119c4..0c089c1 100644 --- a/tests/Unit/RequestHandler/SlimLazyRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/SlimLazyRequestHandlerTest.php @@ -5,9 +5,8 @@ namespace Chubbyphp\Tests\Framework\Unit\RequestHandler; use Chubbyphp\Framework\RequestHandler\SlimLazyRequestHandler; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseFactoryInterface; @@ -21,29 +20,29 @@ */ final class SlimLazyRequestHandlerTest extends TestCase { - use MockByCallsTrait; - public function testHandle(): void { - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('response', null)->willReturn(null), - Call::create('getAttributes')->with()->willReturn(['key1' => 'value1', 'key2' => 'value2']), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['response', null], null), + new WithReturn('getAttributes', [], ['key1' => 'value1', 'key2' => 'value2']), ]); - $requestHander = static fn (ServerRequestInterface $req, ResponseInterface $res, array $args) => $res; + $originalRequestHandler = static fn (ServerRequestInterface $req, ResponseInterface $res, array $args) => $res; - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($requestHander), + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalRequestHandler), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(200, '')->willReturn($response), + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, [ + new WithReturn('createResponse', [200, ''], $response), ]); $requestHandler = new SlimLazyRequestHandler($container, 'serviceName', $responseFactory); @@ -59,18 +58,20 @@ public function testHandleWithWrongObject(): void .' to be callable, stdClass given' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); - $requestHander = new \stdClass(); + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($requestHander), + $originalRequestHandler = new \stdClass(); + + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalRequestHandler), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, []); $requestHandler = new SlimLazyRequestHandler($container, 'serviceName', $responseFactory); $requestHandler->handle($request); @@ -84,18 +85,20 @@ public function testHandleWithString(): void .' to be callable, string given' ); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - $requestHander = ''; + $originalRequestHandler = ''; - /** @var ContainerInterface|MockObject $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with('serviceName')->willReturn($requestHander), + /** @var ContainerInterface $container */ + $container = $builder->create(ContainerInterface::class, [ + new WithReturn('get', ['serviceName'], $originalRequestHandler), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $builder->create(ResponseFactoryInterface::class, []); $requestHandler = new SlimLazyRequestHandler($container, 'serviceName', $responseFactory); $requestHandler->handle($request); diff --git a/tests/Unit/Router/GroupTest.php b/tests/Unit/Router/GroupTest.php index a924382..382efab 100644 --- a/tests/Unit/Router/GroupTest.php +++ b/tests/Unit/Router/GroupTest.php @@ -7,7 +7,7 @@ use Chubbyphp\Framework\Router\Group; use Chubbyphp\Framework\Router\Route; use Chubbyphp\Framework\Router\RouteInterface; -use Chubbyphp\Mock\MockByCallsTrait; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Server\MiddlewareInterface; @@ -20,8 +20,6 @@ */ final class GroupTest extends TestCase { - use MockByCallsTrait; - public function testMinimal(): void { $group = Group::create(''); @@ -42,17 +40,19 @@ public function testWithInvalidChildren(): void public function testMaximal(): void { - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + $builder = new MockObjectBuilder(); + + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware3 */ - $middleware3 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware3 */ + $middleware3 = $builder->create(MiddlewareInterface::class, []); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $handler = $builder->create(RequestHandlerInterface::class, []); $group = Group::create('/{id}', [ Route::get('/{slug}', 'element_read', $handler, [$middleware2], ['tokens' => ['slug' => '[a-z]+']]), @@ -72,7 +72,7 @@ public function testMaximal(): void self::assertCount(3, $routes); - /** @var RouteInterface */ + /** @var RouteInterface $route1 */ $route1 = $routes[0]; self::assertSame('element_read', $route1->getName()); @@ -82,7 +82,7 @@ public function testMaximal(): void self::assertSame([$middleware1, $middleware2], $route1->getMiddlewares()); self::assertSame($handler, $route1->getRequestHandler()); - /** @var RouteInterface */ + /** @var RouteInterface $route2 */ $route2 = $routes[1]; self::assertSame('another_route', $route2->getName()); @@ -95,7 +95,7 @@ public function testMaximal(): void self::assertSame([$middleware1, $middleware2, $middleware3], $route2->getMiddlewares()); self::assertSame($handler, $route2->getRequestHandler()); - /** @var RouteInterface */ + /** @var RouteInterface $route3 */ $route3 = $routes[2]; self::assertSame('yet_another_route', $route3->getName()); diff --git a/tests/Unit/Router/RouteTest.php b/tests/Unit/Router/RouteTest.php index d138318..58751b2 100644 --- a/tests/Unit/Router/RouteTest.php +++ b/tests/Unit/Router/RouteTest.php @@ -5,8 +5,7 @@ namespace Chubbyphp\Tests\Framework\Unit\Router; use Chubbyphp\Framework\Router\Route; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -18,12 +17,12 @@ */ final class RouteTest extends TestCase { - use MockByCallsTrait; - public function testMinimal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::create('GET', '/{id}', 'read', $handler); @@ -38,14 +37,16 @@ public function testMinimal(): void public function testMaximal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); $route = Route::create( 'GET', @@ -67,8 +68,10 @@ public function testMaximal(): void public function testDeleteMinimal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::delete('/{id}', 'delete', $handler); @@ -83,14 +86,16 @@ public function testDeleteMinimal(): void public function testDeleteMaximal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); $route = Route::delete('/{id}', 'delete', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); @@ -105,8 +110,10 @@ public function testDeleteMaximal(): void public function testGetMinimal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::get('/{id}', 'read', $handler); @@ -121,14 +128,16 @@ public function testGetMinimal(): void public function testGetMaximal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); $route = Route::get('/{id}', 'get', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); @@ -143,8 +152,10 @@ public function testGetMaximal(): void public function testHeadMinimal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::head('/{id}', 'read_header', $handler); @@ -159,14 +170,16 @@ public function testHeadMinimal(): void public function testHeadMaximal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); $route = Route::head('/{id}', 'head', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); @@ -181,8 +194,10 @@ public function testHeadMaximal(): void public function testOptionsMinimal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::options('/{id}', 'options', $handler); @@ -197,14 +212,16 @@ public function testOptionsMinimal(): void public function testOptionsMaximal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); $route = Route::options('/{id}', 'options', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); @@ -219,8 +236,10 @@ public function testOptionsMaximal(): void public function testPatchMinimal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::patch('/{id}', 'update', $handler); @@ -235,14 +254,16 @@ public function testPatchMinimal(): void public function testPatchMaximal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); + + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); $route = Route::patch('/{id}', 'patch', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); @@ -257,8 +278,10 @@ public function testPatchMaximal(): void public function testPostMinimal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::post('/{id}', 'create', $handler); @@ -273,14 +296,16 @@ public function testPostMinimal(): void public function testPostMaximal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); + + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); $route = Route::post('/{id}', 'post', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); @@ -295,8 +320,10 @@ public function testPostMaximal(): void public function testPutMinimal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::put('/{id}', 'replace', $handler); @@ -311,14 +338,16 @@ public function testPutMinimal(): void public function testPutMaximal(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); - /** @var MiddlewareInterface|MockObject $middleware1 */ - $middleware1 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var MiddlewareInterface|MockObject $middleware2 */ - $middleware2 = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MiddlewareInterface $middleware1 */ + $middleware1 = $builder->create(MiddlewareInterface::class, []); + + /** @var MiddlewareInterface $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, []); $route = Route::put('/{id}', 'put', $handler, [$middleware1, $middleware2], ['tokens' => ['id' => '\d+']]); @@ -333,8 +362,10 @@ public function testPutMaximal(): void public function testWithAttributes(): void { - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); $route = Route::create('GET', '/{id}', 'read', $handler); diff --git a/tests/Unit/Router/RoutesByNameTest.php b/tests/Unit/Router/RoutesByNameTest.php index 82fed5a..6a52339 100644 --- a/tests/Unit/Router/RoutesByNameTest.php +++ b/tests/Unit/Router/RoutesByNameTest.php @@ -6,8 +6,8 @@ use Chubbyphp\Framework\Router\RouteInterface; use Chubbyphp\Framework\Router\RoutesByName; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; +use Chubbyphp\Mock\MockMethod\WithReturn; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; /** @@ -17,8 +17,6 @@ */ final class RoutesByNameTest extends TestCase { - use MockByCallsTrait; - public function testWithWrongType(): void { $this->expectException(\TypeError::class); @@ -34,12 +32,14 @@ public function testWithWrongType(): void public function testGetRoutes(): void { - $route1 = $this->getMockByCalls(RouteInterface::class, [ - Call::create('getName')->with()->willReturn('name1'), + $builder = new MockObjectBuilder(); + + $route1 = $builder->create(RouteInterface::class, [ + new WithReturn('getName', [], 'name1'), ]); - $route2 = $this->getMockByCalls(RouteInterface::class, [ - Call::create('getName')->with()->willReturn('name2'), + $route2 = $builder->create(RouteInterface::class, [ + new WithReturn('getName', [], 'name2'), ]); $routes = new RoutesByName([$route1, $route2]); From 294d876aa39673f30230fc1a6f4df4486cda4666 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Thu, 6 Mar 2025 07:15:21 +0100 Subject: [PATCH 31/55] update /sonarqube-scan-action --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b9bf9e..142c808 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - name: sonarcloud.io - uses: sonarsource/sonarqube-scan-action@v4.1.0 + uses: sonarsource/sonarqube-scan-action@v5.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From dcf5555218258e062eaefbbb4b784619005b99e4 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Wed, 12 Mar 2025 14:52:16 +0100 Subject: [PATCH 32/55] Keep unnecessary artifacts out of releases --- .gitattributes | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.gitattributes b/.gitattributes index 9374867..4080f38 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,12 +2,14 @@ * text=auto eol=lf -.gitattributes export-ignore -.github/ export-ignore -.gitignore export-ignore -.php-cs-fixer.php export-ignore -infection.json export-ignore -phpstan.neon export-ignore -phpunit.xml export-ignore -tests/ export-ignore +.gitattributes export-ignore +.github/ export-ignore +.gitignore export-ignore +.php-cs-fixer.php export-ignore +.php_cs export-ignore +infection.json export-ignore +LICENSE export-ignore +phpstan.neon export-ignore +phpunit.xml export-ignore sonar-project.properties export-ignore +tests/ export-ignore From 6b0b73f5453e1ee49abe5b52534166dcdf978048 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Wed, 12 Mar 2025 15:09:32 +0100 Subject: [PATCH 33/55] use chubbyphp-mock 2.x --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f5c919b..d22d0cb 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ }, "require-dev": { "chubbyphp/chubbyphp-dev-helper": "dev-master", - "chubbyphp/chubbyphp-mock": "^2.0@dev", + "chubbyphp/chubbyphp-mock": "^2.0", "guzzlehttp/psr7": "^2.7", "http-interop/http-factory-guzzle": "^1.2", "infection/infection": "^0.29.13", From ffea44b2a8f3c52f74c2ea83c3e6d1a61f49a08d Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Thu, 20 Mar 2025 06:47:15 +0100 Subject: [PATCH 34/55] fix test, write should return the amount of signes it wrote on steam --- tests/Unit/Middleware/ExceptionMiddlewareTest.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/Unit/Middleware/ExceptionMiddlewareTest.php b/tests/Unit/Middleware/ExceptionMiddlewareTest.php index 7e2bd27..7b7248c 100644 --- a/tests/Unit/Middleware/ExceptionMiddlewareTest.php +++ b/tests/Unit/Middleware/ExceptionMiddlewareTest.php @@ -8,7 +8,6 @@ use Chubbyphp\HttpException\HttpException; use Chubbyphp\Mock\MockMethod\WithCallback; use Chubbyphp\Mock\MockMethod\WithException; -use Chubbyphp\Mock\MockMethod\WithoutReturn; use Chubbyphp\Mock\MockMethod\WithReturn; use Chubbyphp\Mock\MockMethod\WithReturnSelf; use Chubbyphp\Mock\MockObjectBuilder; @@ -212,7 +211,7 @@ public function testProcessWithClientHttpExceptionWithoutLogger(): void /** @var MockObject|StreamInterface $responseBody */ $responseBody = $builder->create(StreamInterface::class, [ - new WithoutReturn('write', [$expectedBody]), + new WithReturn('write', [$expectedBody], \strlen($expectedBody)), ]); /** @var MockObject|ResponseInterface $response */ @@ -397,7 +396,7 @@ public function testProcessWithClientHttpExceptionWithLogger(): void /** @var MockObject|StreamInterface $responseBody */ $responseBody = $builder->create(StreamInterface::class, [ - new WithoutReturn('write', [$expectedBody]), + new WithReturn('write', [$expectedBody], \strlen($expectedBody)), ]); /** @var MockObject|ResponseInterface $response */ @@ -616,7 +615,7 @@ public function testProcessWithServerHttpExceptionWithDebugWithLogger(): void /** @var MockObject|StreamInterface $responseBody */ $responseBody = $builder->create(StreamInterface::class, [ - new WithoutReturn('write', [$expectedBody]), + new WithReturn('write', [$expectedBody], \strlen($expectedBody)), ]); /** @var MockObject|ResponseInterface $response */ @@ -688,7 +687,7 @@ public function testProcessWithExceptionWithDebugWithLogger(): void /** @var MockObject|StreamInterface $responseBody */ $responseBody = $builder->create(StreamInterface::class, [ - new WithCallback('write', static function (string $html): void { + new WithCallback('write', static function (string $html): int { self::assertStringContainsString( '

A website error has occurred. Sorry for the temporary inconvenience.

', $html @@ -706,6 +705,8 @@ public function testProcessWithExceptionWithDebugWithLogger(): void self::assertStringContainsString('
logic exception
', $html); self::assertStringContainsString('
Code
', $html); self::assertStringContainsString('
42
', $html); + + return \strlen($html); }), ]); From 562b8e2aad61b42fd27373f9e9e0dd6ffc8ea9b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:34:36 +0000 Subject: [PATCH 35/55] Bump sonarsource/sonarqube-scan-action in /.github/workflows Bumps [sonarsource/sonarqube-scan-action](https://github.com/sonarsource/sonarqube-scan-action) from 5.0.0 to 5.3.1. - [Release notes](https://github.com/sonarsource/sonarqube-scan-action/releases) - [Commits](https://github.com/sonarsource/sonarqube-scan-action/compare/v5.0.0...v5.3.1) --- updated-dependencies: - dependency-name: sonarsource/sonarqube-scan-action dependency-version: 5.3.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 142c808..0ab8b53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - name: sonarcloud.io - uses: sonarsource/sonarqube-scan-action@v5.0.0 + uses: sonarsource/sonarqube-scan-action@v5.3.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From db409b1d2a493e1eb5b5795c4e3fddf2802d39fa Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Tue, 30 Sep 2025 07:56:39 +0200 Subject: [PATCH 36/55] update sonarsource/sonarqube-scan-action --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ab8b53..9c646d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: schedule: - - cron: '0 0 * * *' + - cron: "0 0 * * *" jobs: php82: @@ -40,7 +40,7 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - name: sonarcloud.io - uses: sonarsource/sonarqube-scan-action@v5.3.1 + uses: sonarsource/sonarqube-scan-action@v6.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 8edc0708953d304c63fb565f86848308b785d307 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Thu, 9 Oct 2025 22:28:34 +0200 Subject: [PATCH 37/55] pipe-middleware --- README.md | 6 +- composer.json | 2 +- ...dlewareDispatcher.md => PipeMiddleware.md} | 8 +- doc/Migration/5.x-6.x.md | 18 ++ doc/RequestHandler/RouteRequestHandler.md | 7 +- phpstan.neon | 5 +- src/Application.php | 25 +-- src/Collection.php | 51 ------ src/Middleware/MiddlewareDispatcher.php | 28 ---- .../MiddlewareDispatcherInterface.php | 22 --- src/Middleware/PipeMiddleware.php | 36 ++++ src/RequestHandler/RouteRequestHandler.php | 10 +- src/Router/Group.php | 5 +- src/Router/Route.php | 3 +- src/Router/RoutesByName.php | 11 -- .../Integration/MiddlewareDispatcherTest.php | 157 ------------------ tests/Integration/PipeMiddlewareTest.php | 154 +++++++++++++++++ tests/Unit/ApplicationTest.php | 101 +++++++---- tests/Unit/CollectionTest.php | 65 -------- ...patcherTest.php => PipeMiddlewareTest.php} | 14 +- .../RouteRequestHandlerTest.php | 39 +++-- tests/Unit/Router/GroupTest.php | 11 -- tests/Unit/Router/RoutesByNameTest.php | 13 -- 23 files changed, 326 insertions(+), 465 deletions(-) rename doc/Middleware/{MiddlewareDispatcher.md => PipeMiddleware.md} (80%) create mode 100644 doc/Migration/5.x-6.x.md delete mode 100644 src/Collection.php delete mode 100644 src/Middleware/MiddlewareDispatcher.php delete mode 100644 src/Middleware/MiddlewareDispatcherInterface.php create mode 100644 src/Middleware/PipeMiddleware.php delete mode 100644 tests/Integration/MiddlewareDispatcherTest.php create mode 100644 tests/Integration/PipeMiddlewareTest.php delete mode 100644 tests/Unit/CollectionTest.php rename tests/Unit/Middleware/{MiddlewareDispatcherTest.php => PipeMiddlewareTest.php} (85%) diff --git a/README.md b/README.md index d73a031..0f99e88 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); * [CallbackMiddleware][70] * [ExceptionMiddleware][71] * [LazyMiddleware][72] - * [MiddlewareDispatcher][73] + * [PipeMiddleware][73] * [RouteMatcherMiddleware][74] * [SlimCallbackMiddleware][75] * [SlimLazyMiddleware][76] @@ -156,6 +156,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); ## Migration + * [5.x to 6.x][214] * [4.x to 5.x][213] * [3.x to 4.x][212] * [2.x to 3.x][211] @@ -205,7 +206,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); [70]: doc/Middleware/CallbackMiddleware.md [71]: doc/Middleware/ExceptionMiddleware.md [72]: doc/Middleware/LazyMiddleware.md -[73]: doc/Middleware/MiddlewareDispatcher.md +[73]: doc/Middleware/PipeMiddleware.md [74]: doc/Middleware/RouteMatcherMiddleware.md [75]: doc/Middleware/SlimCallbackMiddleware.md [76]: doc/Middleware/SlimLazyMiddleware.md @@ -233,5 +234,6 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); [211]: doc/Migration/2.x-3.x.md [212]: doc/Migration/3.x-4.x.md [213]: doc/Migration/4.x-5.x.md +[214]: doc/Migration/5.x-6.x.md [219]: doc/Migration/Slim-Chubbyphp.md diff --git a/composer.json b/composer.json index d22d0cb..3faa687 100644 --- a/composer.json +++ b/composer.json @@ -63,7 +63,7 @@ }, "extra": { "branch-alias": { - "dev-master": "5.2-dev" + "dev-master": "6.0-dev" } }, "scripts": { diff --git a/doc/Middleware/MiddlewareDispatcher.md b/doc/Middleware/PipeMiddleware.md similarity index 80% rename from doc/Middleware/MiddlewareDispatcher.md rename to doc/Middleware/PipeMiddleware.md index a59512f..adcf2d1 100644 --- a/doc/Middleware/MiddlewareDispatcher.md +++ b/doc/Middleware/PipeMiddleware.md @@ -1,4 +1,4 @@ -# MiddlewareDispatcher +# PipeMiddleware ## Methods @@ -7,7 +7,7 @@ ```php dispatch([$middleware1, $middleware2], $handler, $request); +$response = $pipeMiddleware->dispatch($request, $handler); ``` diff --git a/doc/Migration/5.x-6.x.md b/doc/Migration/5.x-6.x.md new file mode 100644 index 0000000..5f0eeda --- /dev/null +++ b/doc/Migration/5.x-6.x.md @@ -0,0 +1,18 @@ +# 5.x to 6.x + +`Chubbyphp\Framework\Middleware\MiddlewareDispatcher` gets dropped and replaced by `Chubbyphp\Framework\Middleware\PipeMiddleware`. +`Chubbyphp\Framework\Middleware\MiddlewareDispatcher` as an implements of `Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface` which is gone as well. + +This makes the framework as bit less flexible for special use cases, but easier in use for the majority. + +Resulting changes: +- `Chubbyphp\Framework\Handler\RouteRequestHandler::__construct` does not take a `Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface` as argument anymore. +- `Chubbyphp\Framework\Application::__construct` does not take a `Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface` and `Chubbyphp\Framework\Handler\RouteRequestHandler` as argument anymore. + +**If you need a custom `Chubbyphp\Framework\Middleware\PipeMiddleware` for your use case:** + +I suggest you: +- Create an own `Chubbyphp\Framework\Application` and `Chubbyphp\Framework\Handler\RouteRequestHandler` onces you read the code you'll see it's little to replace and to maintain yourself. +- Create an issue convincing me to find a better solution with you in collaboration + +`Chubbyphp\Framework\Collection` gets dropped, cause i do not believe the additional runtime security for typing arrays was worth the performance hit. Use phpstan or something compatible to prevent mistakes in usage. diff --git a/doc/RequestHandler/RouteRequestHandler.md b/doc/RequestHandler/RouteRequestHandler.md index d1b45d0..d54c70c 100644 --- a/doc/RequestHandler/RouteRequestHandler.md +++ b/doc/RequestHandler/RouteRequestHandler.md @@ -7,7 +7,6 @@ ```php handle($request); +$response = $handler->handle($request); ``` diff --git a/phpstan.neon b/phpstan.neon index b68e5b4..f51e71c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,2 @@ parameters: - ignoreErrors: - - - message: '/Instanceof between Chubbyphp\\Framework\\Router\\RouteInterface and Chubbyphp\\Framework\\Router\\RouteInterface will always evaluate to true./' - path: %currentWorkingDirectory%/src/Router/RoutesByName.php + ignoreErrors: [] diff --git a/src/Application.php b/src/Application.php index dde6b2a..5a39c28 100644 --- a/src/Application.php +++ b/src/Application.php @@ -6,8 +6,7 @@ use Chubbyphp\Framework\Emitter\Emitter; use Chubbyphp\Framework\Emitter\EmitterInterface; -use Chubbyphp\Framework\Middleware\MiddlewareDispatcher; -use Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface; +use Chubbyphp\Framework\Middleware\PipeMiddleware; use Chubbyphp\Framework\RequestHandler\RouteRequestHandler; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -16,14 +15,9 @@ final class Application implements RequestHandlerInterface { - /** - * @var array - */ - private array $middlewares; - - private MiddlewareDispatcherInterface $middlewareDispatcher; + private PipeMiddleware $pipeMiddleware; - private RequestHandlerInterface $requestHandler; + private RequestHandlerInterface $routeRequestHandler; private EmitterInterface $emitter; @@ -32,13 +26,10 @@ final class Application implements RequestHandlerInterface */ public function __construct( array $middlewares, - ?MiddlewareDispatcherInterface $middlewareDispatcher = null, - ?RequestHandlerInterface $requestHandler = null, ?EmitterInterface $emitter = null ) { - $this->middlewares = (new Collection($middlewares, [MiddlewareInterface::class]))->toArray(); - $this->middlewareDispatcher = $middlewareDispatcher ?? new MiddlewareDispatcher(); - $this->requestHandler = $requestHandler ?? new RouteRequestHandler($this->middlewareDispatcher); + $this->pipeMiddleware = new PipeMiddleware($middlewares); + $this->routeRequestHandler = new RouteRequestHandler(); $this->emitter = $emitter ?? new Emitter(); } @@ -49,11 +40,7 @@ public function __invoke(ServerRequestInterface $request): ResponseInterface public function handle(ServerRequestInterface $request): ResponseInterface { - return $this->middlewareDispatcher->dispatch( - $this->middlewares, - $this->requestHandler, - $request - ); + return $this->pipeMiddleware->process($request, $this->routeRequestHandler); } public function emit(ResponseInterface $response): void diff --git a/src/Collection.php b/src/Collection.php deleted file mode 100644 index 76a8274..0000000 --- a/src/Collection.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ - private array $items = []; - - /** - * @param array $items - * @param array $types - */ - public function __construct(array $items, array $types) - { - foreach ($items as $i => $item) { - foreach ($types as $type) { - if ($item instanceof $type) { - $this->items[$i] = $item; - - continue 2; - } - } - - throw new \TypeError( - \sprintf( - '%s::__construct() expects parameter 1 at index %s to be %s, %s given', - self::class, - $i, - implode('|', $types), - $item::class - ) - ); - } - } - - /** - * @return array - */ - public function toArray(): array - { - return $this->items; - } -} diff --git a/src/Middleware/MiddlewareDispatcher.php b/src/Middleware/MiddlewareDispatcher.php deleted file mode 100644 index 4965709..0000000 --- a/src/Middleware/MiddlewareDispatcher.php +++ /dev/null @@ -1,28 +0,0 @@ - $middlewares - */ - public function dispatch( - array $middlewares, - RequestHandlerInterface $handler, - ServerRequestInterface $request - ): ResponseInterface { - return array_reduce( - array_reverse($middlewares), - static fn ($handler, $middleware) => new MiddlewareRequestHandler($middleware, $handler), - $handler - )->handle($request); - } -} diff --git a/src/Middleware/MiddlewareDispatcherInterface.php b/src/Middleware/MiddlewareDispatcherInterface.php deleted file mode 100644 index be5d9bd..0000000 --- a/src/Middleware/MiddlewareDispatcherInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - $middlewares - */ - public function dispatch( - array $middlewares, - RequestHandlerInterface $handler, - ServerRequestInterface $request - ): ResponseInterface; -} diff --git a/src/Middleware/PipeMiddleware.php b/src/Middleware/PipeMiddleware.php new file mode 100644 index 0000000..c839a41 --- /dev/null +++ b/src/Middleware/PipeMiddleware.php @@ -0,0 +1,36 @@ + + */ + private array $middlewares; + + /** + * @param array $middlewares + */ + public function __construct(array $middlewares) + { + $this->middlewares = array_reverse($middlewares); + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $reducedHandler = $handler; + foreach ($this->middlewares as $middleware) { + $reducedHandler = new MiddlewareRequestHandler($middleware, $reducedHandler); + } + + return $reducedHandler->handle($request); + } +} diff --git a/src/RequestHandler/RouteRequestHandler.php b/src/RequestHandler/RouteRequestHandler.php index 7eb0464..c8d008c 100644 --- a/src/RequestHandler/RouteRequestHandler.php +++ b/src/RequestHandler/RouteRequestHandler.php @@ -4,7 +4,7 @@ namespace Chubbyphp\Framework\RequestHandler; -use Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface; +use Chubbyphp\Framework\Middleware\PipeMiddleware; use Chubbyphp\Framework\Router\Exceptions\MissingRouteAttributeOnRequestException; use Chubbyphp\Framework\Router\RouteInterface; use Psr\Http\Message\ResponseInterface; @@ -13,8 +13,6 @@ final class RouteRequestHandler implements RequestHandlerInterface { - public function __construct(private MiddlewareDispatcherInterface $middlewareDispatcher) {} - public function handle(ServerRequestInterface $request): ResponseInterface { $route = $request->getAttribute('route'); @@ -23,10 +21,6 @@ public function handle(ServerRequestInterface $request): ResponseInterface throw MissingRouteAttributeOnRequestException::create($route); } - return $this->middlewareDispatcher->dispatch( - $route->getMiddlewares(), - $route->getRequestHandler(), - $request - ); + return (new PipeMiddleware($route->getMiddlewares()))->process($request, $route->getRequestHandler()); } } diff --git a/src/Router/Group.php b/src/Router/Group.php index 84ff25e..d61b716 100644 --- a/src/Router/Group.php +++ b/src/Router/Group.php @@ -4,7 +4,6 @@ namespace Chubbyphp\Framework\Router; -use Chubbyphp\Framework\Collection; use Psr\Http\Server\MiddlewareInterface; final class Group implements GroupInterface @@ -30,8 +29,8 @@ private function __construct( array $middlewares = [], private array $pathOptions = [] ) { - $this->children = (new Collection($children, [GroupInterface::class, RouteInterface::class]))->toArray(); - $this->middlewares = (new Collection($middlewares, [MiddlewareInterface::class]))->toArray(); + $this->children = $children; + $this->middlewares = $middlewares; } /** diff --git a/src/Router/Route.php b/src/Router/Route.php index 7e9e046..f12efa8 100644 --- a/src/Router/Route.php +++ b/src/Router/Route.php @@ -4,7 +4,6 @@ namespace Chubbyphp\Framework\Router; -use Chubbyphp\Framework\Collection; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -32,7 +31,7 @@ private function __construct( array $middlewares = [], private array $pathOptions = [] ) { - $this->middlewares = (new Collection($middlewares, [MiddlewareInterface::class]))->toArray(); + $this->middlewares = $middlewares; } /** diff --git a/src/Router/RoutesByName.php b/src/Router/RoutesByName.php index aaede6c..065fb1b 100644 --- a/src/Router/RoutesByName.php +++ b/src/Router/RoutesByName.php @@ -17,17 +17,6 @@ final class RoutesByName implements RoutesByNameInterface public function __construct(array $routes) { foreach ($routes as $i => $route) { - if (!$route instanceof RouteInterface) { - throw new \TypeError( - \sprintf( - '%s::__construct() expects parameter 1 at index %s to be %s, %s given', - self::class, - $i, - RouteInterface::class, - $route::class - ) - ); - } $this->routes[$route->getName()] = $route; } } diff --git a/tests/Integration/MiddlewareDispatcherTest.php b/tests/Integration/MiddlewareDispatcherTest.php deleted file mode 100644 index 94c05a1..0000000 --- a/tests/Integration/MiddlewareDispatcherTest.php +++ /dev/null @@ -1,157 +0,0 @@ -dispatch( - [ - new CallbackMiddleware( - static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req1', 'value1')) - ), - new CallbackMiddleware( - static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req2', 'value2')) - ), - new CallbackMiddleware( - static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req3', 'value3')) - ), - new CallbackMiddleware( - static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withAttribute('req', 'value')) - ), - ], - new CallbackRequestHandler( - static function (ServerRequestInterface $request) use ($responseFactory) { - self::assertSame([ - 'req1' => ['value1'], - 'req2' => ['value2'], - 'req3' => ['value3'], - ], $request->getHeaders()); - - return $responseFactory->createResponse(); - } - ), - $serverRequestFactory->createServerRequest('GET', '/hello/test') - ); - } - - public function testSlim(): void - { - $responseFactory = new SlimResponseFactory(); - $serverRequestFactory = new SunriseServerRequestFactory(); - - $middlewareDispatcher = new MiddlewareDispatcher(); - $middlewareDispatcher->dispatch( - [ - new SlimCallbackMiddleware( - static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withHeader('req1', 'value1'), $res->withHeader('res1', 'value1')), - $responseFactory - ), - new SlimCallbackMiddleware( - static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withHeader('req2', 'value2'), $res->withHeader('res2', 'value2')), - $responseFactory - ), - new SlimCallbackMiddleware( - static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withHeader('req3', 'value3'), $res->withHeader('res3', 'value3')), - $responseFactory - ), - new SlimCallbackMiddleware( - static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withAttribute('req', 'value'), $res), - $responseFactory - ), - ], - new SlimCallbackRequestHandler( - static function (ServerRequestInterface $req, ResponseInterface $res, array $args) { - self::assertSame([ - 'req1' => ['value1'], - 'req2' => ['value2'], - 'req3' => ['value3'], - ], $req->getHeaders()); - - self::assertSame([ - 'res1' => ['value1'], - 'res2' => ['value2'], - 'res3' => ['value3'], - ], $res->getHeaders()); - - self::assertSame(['response', 'req'], array_keys($args)); - self::assertSame('value', $args['req']); - - return $res; - }, - $responseFactory - ), - $serverRequestFactory->createServerRequest('GET', '/hello/test') - ); - } - - public function testCallbackSlimMixed(): void - { - $responseFactory = new SlimResponseFactory(); - $serverRequestFactory = new SunriseServerRequestFactory(); - - $middlewareDispatcher = new MiddlewareDispatcher(); - $middlewareDispatcher->dispatch( - [ - new CallbackMiddleware( - static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req1', 'value1')) - ), - new SlimCallbackMiddleware( - static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withHeader('req2', 'value2'), $res->withHeader('res2', 'value2')), - $responseFactory - ), - new CallbackMiddleware( - static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req3', 'value3')) - ), - new SlimCallbackMiddleware( - static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withAttribute('req', 'value'), $res), - $responseFactory - ), - ], - new SlimCallbackRequestHandler( - static function (ServerRequestInterface $req, ResponseInterface $res, array $args) { - self::assertSame([ - 'req1' => ['value1'], - 'req2' => ['value2'], - 'req3' => ['value3'], - ], $req->getHeaders()); - - self::assertSame([ - 'res2' => ['value2'], - ], $res->getHeaders()); - - self::assertSame(['response', 'req'], array_keys($args)); - self::assertSame('value', $args['req']); - - return $res; - }, - $responseFactory - ), - $serverRequestFactory->createServerRequest('GET', '/hello/test') - ); - } -} diff --git a/tests/Integration/PipeMiddlewareTest.php b/tests/Integration/PipeMiddlewareTest.php new file mode 100644 index 0000000..c3a8f4f --- /dev/null +++ b/tests/Integration/PipeMiddlewareTest.php @@ -0,0 +1,154 @@ + $handler->handle($request->withHeader('req1', 'value1')) + ), + new CallbackMiddleware( + static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req2', 'value2')) + ), + new CallbackMiddleware( + static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req3', 'value3')) + ), + new CallbackMiddleware( + static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withAttribute('req', 'value')) + ), + ]); + $pipeMiddleware->process( + $serverRequestFactory->createServerRequest('GET', '/hello/test'), + new CallbackRequestHandler( + static function (ServerRequestInterface $request) use ($responseFactory) { + self::assertSame([ + 'req1' => ['value1'], + 'req2' => ['value2'], + 'req3' => ['value3'], + ], $request->getHeaders()); + + return $responseFactory->createResponse(); + } + ), + ); + } + + public function testSlim(): void + { + $responseFactory = new SlimResponseFactory(); + $serverRequestFactory = new SunriseServerRequestFactory(); + + $pipeMiddleware = new PipeMiddleware([ + new SlimCallbackMiddleware( + static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withHeader('req1', 'value1'), $res->withHeader('res1', 'value1')), + $responseFactory + ), + new SlimCallbackMiddleware( + static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withHeader('req2', 'value2'), $res->withHeader('res2', 'value2')), + $responseFactory + ), + new SlimCallbackMiddleware( + static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withHeader('req3', 'value3'), $res->withHeader('res3', 'value3')), + $responseFactory + ), + new SlimCallbackMiddleware( + static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withAttribute('req', 'value'), $res), + $responseFactory + ), + ]); + $pipeMiddleware->process( + $serverRequestFactory->createServerRequest('GET', '/hello/test'), + new SlimCallbackRequestHandler( + static function (ServerRequestInterface $req, ResponseInterface $res, array $args) { + self::assertSame([ + 'req1' => ['value1'], + 'req2' => ['value2'], + 'req3' => ['value3'], + ], $req->getHeaders()); + + self::assertSame([ + 'res1' => ['value1'], + 'res2' => ['value2'], + 'res3' => ['value3'], + ], $res->getHeaders()); + + self::assertSame(['response', 'req'], array_keys($args)); + self::assertSame('value', $args['req']); + + return $res; + }, + $responseFactory + ), + ); + } + + public function testCallbackSlimMixed(): void + { + $responseFactory = new SlimResponseFactory(); + $serverRequestFactory = new SunriseServerRequestFactory(); + + $pipeMiddleware = new PipeMiddleware([ + new CallbackMiddleware( + static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req1', 'value1')) + ), + new SlimCallbackMiddleware( + static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withHeader('req2', 'value2'), $res->withHeader('res2', 'value2')), + $responseFactory + ), + new CallbackMiddleware( + static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle($request->withHeader('req3', 'value3')) + ), + new SlimCallbackMiddleware( + static fn (ServerRequestInterface $req, ResponseInterface $res, callable $next) => $next($req->withAttribute('req', 'value'), $res), + $responseFactory + ), + ]); + $pipeMiddleware->process( + $serverRequestFactory->createServerRequest('GET', '/hello/test'), + new SlimCallbackRequestHandler( + static function (ServerRequestInterface $req, ResponseInterface $res, array $args) { + self::assertSame([ + 'req1' => ['value1'], + 'req2' => ['value2'], + 'req3' => ['value3'], + ], $req->getHeaders()); + + self::assertSame([ + 'res2' => ['value2'], + ], $res->getHeaders()); + + self::assertSame(['response', 'req'], array_keys($args)); + self::assertSame('value', $args['req']); + + return $res; + }, + $responseFactory + ), + ); + } +} diff --git a/tests/Unit/ApplicationTest.php b/tests/Unit/ApplicationTest.php index 1db0b27..7dc09e2 100644 --- a/tests/Unit/ApplicationTest.php +++ b/tests/Unit/ApplicationTest.php @@ -6,7 +6,6 @@ use Chubbyphp\Framework\Application; use Chubbyphp\Framework\Emitter\EmitterInterface; -use Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface; use Chubbyphp\Framework\Router\RouteInterface; use Chubbyphp\Mock\MockMethod\WithCallback; use Chubbyphp\Mock\MockMethod\WithReturn; @@ -30,14 +29,29 @@ public function testInvoke(): void { $builder = new MockObjectBuilder(); - /** @var MiddlewareInterface $routeIndependentMiddleware */ - $routeIndependentMiddleware = $builder->create(MiddlewareInterface::class, []); + /** @var MockObject|ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); /** @var MiddlewareInterface $middleware */ - $middleware = $builder->create(MiddlewareInterface::class, []); + $middleware = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) + ), + ]); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $builder->create(RequestHandlerInterface::class, []); + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithCallback( + 'handle', + static fn ( + ServerRequestInterface $request, + ) => $response, + ), + ]); /** @var MockObject|RouteInterface $route */ $route = $builder->create(RouteInterface::class, [ @@ -50,25 +64,20 @@ public function testInvoke(): void new WithReturn('getAttribute', ['route', null], $route), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $builder->create(ResponseInterface::class, []); - - /** @var MiddlewareDispatcherInterface $middlewareDispatcher */ - $middlewareDispatcher = $builder->create(MiddlewareDispatcherInterface::class, [ + /** @var MiddlewareInterface $routeIndependentMiddleware */ + $routeIndependentMiddleware = $builder->create(MiddlewareInterface::class, [ new WithCallback( - 'dispatch', - static function (array $middlewares, RequestHandlerInterface $requestHandler, ServerRequestInterface $req) use ($routeIndependentMiddleware): ResponseInterface { - TestCase::assertSame([$routeIndependentMiddleware], $middlewares); - - return $requestHandler->handle($req); - } + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) ), - new WithReturn('dispatch', [[$middleware], $handler, $request], $response), ]); $application = new Application([ $routeIndependentMiddleware, - ], $middlewareDispatcher); + ]); self::assertSame($response, $application($request)); } @@ -77,35 +86,55 @@ public function testHandle(): void { $builder = new MockObjectBuilder(); - /** @var MiddlewareInterface $routeIndependentMiddleware */ - $routeIndependentMiddleware = $builder->create(MiddlewareInterface::class, []); - - /** @var MockObject|ServerRequestInterface $request */ - $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|ResponseInterface $response */ $response = $builder->create(ResponseInterface::class, []); - /** @var MiddlewareDispatcherInterface $middlewareDispatcher */ - $middlewareDispatcher = $builder->create(MiddlewareDispatcherInterface::class, [ + /** @var MiddlewareInterface $middleware */ + $middleware = $builder->create(MiddlewareInterface::class, [ new WithCallback( - 'dispatch', - static function (array $middlewares, RequestHandlerInterface $requestHandler, ServerRequestInterface $req) use ($routeIndependentMiddleware): ResponseInterface { - TestCase::assertSame([$routeIndependentMiddleware], $middlewares); + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) + ), + ]); - return $requestHandler->handle($req); - } + /** @var MockObject|RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithCallback( + 'handle', + static fn ( + ServerRequestInterface $request, + ) => $response, ), ]); - /** @var MockObject|RequestHandlerInterface $requestHandler */ - $requestHandler = $builder->create(RequestHandlerInterface::class, [ - new WithReturn('handle', [$request], $response), + /** @var MockObject|RouteInterface $route */ + $route = $builder->create(RouteInterface::class, [ + new WithReturn('getMiddlewares', [], [$middleware]), + new WithReturn('getRequestHandler', [], $handler), + ]); + + /** @var MockObject|ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['route', null], $route), + ]); + + /** @var MiddlewareInterface $routeIndependentMiddleware */ + $routeIndependentMiddleware = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) + ), ]); $application = new Application([ $routeIndependentMiddleware, - ], $middlewareDispatcher, $requestHandler); + ]); self::assertSame($response, $application->handle($request)); } @@ -123,7 +152,7 @@ public function testEmit(): void new WithReturn('emit', [$response], null), ]); - $application = new Application([], null, null, $emitter); + $application = new Application([], $emitter); $application->emit($response); } } diff --git a/tests/Unit/CollectionTest.php b/tests/Unit/CollectionTest.php deleted file mode 100644 index d5c6380..0000000 --- a/tests/Unit/CollectionTest.php +++ /dev/null @@ -1,65 +0,0 @@ -toArray()); - } - - public function testValidWithOneType(): void - { - $items = [new \stdClass()]; - - self::assertSame($items, (new Collection($items, [\stdClass::class]))->toArray()); - } - - public function testInValidWithOneType(): void - { - $this->expectException(\TypeError::class); - $this->expectExceptionMessage( - 'Chubbyphp\Framework\Collection::__construct() expects parameter 1 at index 1 to be' - .' stdClass, DateTimeImmutable given' - ); - - new Collection([new \stdClass(), new \DateTimeImmutable()], [\stdClass::class]); - } - - public function testValidWithMultipleTypes(): void - { - $items = [new \stdClass(), new \DateTimeImmutable(), new \Exception()]; - - self::assertSame( - $items, - (new Collection($items, [\stdClass::class, \DateTimeImmutable::class, \Exception::class]))->toArray() - ); - } - - public function testInValidWithMultipleTypes(): void - { - $this->expectException(\TypeError::class); - $this->expectExceptionMessage( - 'Chubbyphp\Framework\Collection::__construct() expects parameter 1 at index 1 to be' - .' stdClass|DateTimeImmutable|Exception, Error given' - ); - - new Collection( - [new \stdClass(), new \Error(), new \DateTimeImmutable()], - [\stdClass::class, \DateTimeImmutable::class, \Exception::class] - ); - } -} diff --git a/tests/Unit/Middleware/MiddlewareDispatcherTest.php b/tests/Unit/Middleware/PipeMiddlewareTest.php similarity index 85% rename from tests/Unit/Middleware/MiddlewareDispatcherTest.php rename to tests/Unit/Middleware/PipeMiddlewareTest.php index 2dda21b..b758aa3 100644 --- a/tests/Unit/Middleware/MiddlewareDispatcherTest.php +++ b/tests/Unit/Middleware/PipeMiddlewareTest.php @@ -4,7 +4,7 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; -use Chubbyphp\Framework\Middleware\MiddlewareDispatcher; +use Chubbyphp\Framework\Middleware\PipeMiddleware; use Chubbyphp\Mock\MockMethod\WithCallback; use Chubbyphp\Mock\MockMethod\WithReturn; use Chubbyphp\Mock\MockMethod\WithReturnSelf; @@ -16,11 +16,11 @@ use Psr\Http\Server\RequestHandlerInterface; /** - * @covers \Chubbyphp\Framework\Middleware\MiddlewareDispatcher + * @covers \Chubbyphp\Framework\Middleware\PipeMiddleware * * @internal */ -final class MiddlewareDispatcherTest extends TestCase +final class PipeMiddlewareTest extends TestCase { public function testWithoutMiddlewares(): void { @@ -37,9 +37,9 @@ public function testWithoutMiddlewares(): void new WithReturn('handle', [$request], $response), ]); - $middlewareDispatcher = new MiddlewareDispatcher(); + $pipeMiddleware = new PipeMiddleware([]); - self::assertSame($response, $middlewareDispatcher->dispatch([], $handler, $request)); + self::assertSame($response, $pipeMiddleware->process($request, $handler)); } public function testWithMiddlewares(): void @@ -90,11 +90,11 @@ static function ( ), ]); - $middlewareDispatcher = new MiddlewareDispatcher(); + $pipeMiddleware = new PipeMiddleware([$middleware1, $middleware2]); self::assertSame( $response, - $middlewareDispatcher->dispatch([$middleware1, $middleware2], $handler, $request) + $pipeMiddleware->process($request, $handler) ); } } diff --git a/tests/Unit/RequestHandler/RouteRequestHandlerTest.php b/tests/Unit/RequestHandler/RouteRequestHandlerTest.php index 0647b71..c6d78d5 100644 --- a/tests/Unit/RequestHandler/RouteRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/RouteRequestHandlerTest.php @@ -4,10 +4,10 @@ namespace Chubbyphp\Tests\Framework\Unit\RequestHandler; -use Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface; use Chubbyphp\Framework\RequestHandler\RouteRequestHandler; use Chubbyphp\Framework\Router\Exceptions\MissingRouteAttributeOnRequestException; use Chubbyphp\Framework\Router\RouteInterface; +use Chubbyphp\Mock\MockMethod\WithCallback; use Chubbyphp\Mock\MockMethod\WithReturn; use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; @@ -41,10 +41,7 @@ public function testHandleWithoutRoute(): void /** @var ResponseInterface $response */ $response = $builder->create(ResponseInterface::class, []); - /** @var MiddlewareDispatcherInterface $middlewareDispatcher */ - $middlewareDispatcher = $builder->create(MiddlewareDispatcherInterface::class, []); - - $requestHandler = new RouteRequestHandler($middlewareDispatcher); + $requestHandler = new RouteRequestHandler(); self::assertSame($response, $requestHandler->handle($request)); } @@ -53,11 +50,29 @@ public function testHandleWithRoute(): void { $builder = new MockObjectBuilder(); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); + /** @var MiddlewareInterface $middleware */ - $middleware = $builder->create(MiddlewareInterface::class, []); + $middleware = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) + ), + ]); /** @var RequestHandlerInterface $innerRequestHandler */ - $innerRequestHandler = $builder->create(RequestHandlerInterface::class, []); + $innerRequestHandler = $builder->create(RequestHandlerInterface::class, [ + new WithCallback( + 'handle', + static fn ( + ServerRequestInterface $request, + ) => $response, + ), + ]); /** @var RouteInterface $route */ $route = $builder->create(RouteInterface::class, [ @@ -70,15 +85,7 @@ public function testHandleWithRoute(): void new WithReturn('getAttribute', ['route', null], $route), ]); - /** @var ResponseInterface $response */ - $response = $builder->create(ResponseInterface::class, []); - - /** @var MiddlewareDispatcherInterface $middlewareDispatcher */ - $middlewareDispatcher = $builder->create(MiddlewareDispatcherInterface::class, [ - new WithReturn('dispatch', [[$middleware], $innerRequestHandler, $request], $response), - ]); - - $requestHandler = new RouteRequestHandler($middlewareDispatcher); + $requestHandler = new RouteRequestHandler(); self::assertSame($response, $requestHandler->handle($request)); } diff --git a/tests/Unit/Router/GroupTest.php b/tests/Unit/Router/GroupTest.php index 382efab..c95bd98 100644 --- a/tests/Unit/Router/GroupTest.php +++ b/tests/Unit/Router/GroupTest.php @@ -27,17 +27,6 @@ public function testMinimal(): void self::assertSame([], $group->getRoutes()); } - public function testWithInvalidChildren(): void - { - $this->expectException(\TypeError::class); - $this->expectExceptionMessage( - 'Chubbyphp\Framework\Collection::__construct() expects parameter 1 at index 0 to be ' - .'Chubbyphp\Framework\Router\GroupInterface|Chubbyphp\Framework\Router\RouteInterface, stdClass given' - ); - - Group::create('', [new \stdClass()]); - } - public function testMaximal(): void { $builder = new MockObjectBuilder(); diff --git a/tests/Unit/Router/RoutesByNameTest.php b/tests/Unit/Router/RoutesByNameTest.php index 6a52339..e64f52e 100644 --- a/tests/Unit/Router/RoutesByNameTest.php +++ b/tests/Unit/Router/RoutesByNameTest.php @@ -17,19 +17,6 @@ */ final class RoutesByNameTest extends TestCase { - public function testWithWrongType(): void - { - $this->expectException(\TypeError::class); - $this->expectExceptionMessage( - 'Chubbyphp\Framework\Router\RoutesByName::__construct() expects parameter 1' - .' at index 0 to be Chubbyphp\Framework\Router\RouteInterface, stdClass given' - ); - - $route = new \stdClass(); - - new RoutesByName([$route]); - } - public function testGetRoutes(): void { $builder = new MockObjectBuilder(); From 412d91178b7c0c5cc1ca7587b45b9ea70736d66d Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Thu, 4 Dec 2025 15:41:33 +0100 Subject: [PATCH 38/55] php85 --- .github/workflows/ci.yml | 18 ++++++------- .php-cs-fixer.php | 1 + README.md | 11 +++++--- composer.json | 26 +++++++++---------- src/Middleware/ExceptionMiddleware.php | 2 +- src/Middleware/SlimCallbackMiddleware.php | 2 +- .../SlimCallbackRequestHandler.php | 2 +- tests/Integration/RouteMatcherLessTest.php | 9 +++---- 8 files changed, 36 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c646d9..640398e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,36 +6,36 @@ on: - cron: "0 0 * * *" jobs: - php82: - name: PHP 8.2 + php83: + name: PHP 8.3 runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://ghcr.io/chubbyphp/ci-php82:latest + uses: docker://ghcr.io/chubbyphp/ci-php83:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - php83: - name: PHP 8.3 + php84: + name: PHP 8.4 runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://ghcr.io/chubbyphp/ci-php83:latest + uses: docker://ghcr.io/chubbyphp/ci-php84:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - php84: - name: PHP 8.4 + php85: + name: PHP 8.5 runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v4 - name: composer test - uses: docker://ghcr.io/chubbyphp/ci-php84:latest + uses: docker://ghcr.io/chubbyphp/ci-php85:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index d81b4d7..85172b8 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -13,6 +13,7 @@ $config = require __DIR__ . '/vendor/chubbyphp/chubbyphp-dev-helper/phpcs.php'; return (new PhpCsFixer\Config) + ->setUnsupportedPhpVersionAllowed(true) ->setIndent($config['indent']) ->setLineEnding($config['lineEnding']) ->setRules($config['rules']) diff --git a/README.md b/README.md index d73a031..9455570 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements - * php: ^8.2 + * php: ^8.3 * [chubbyphp/chubbyphp-http-exception][20]: ^1.2 * [psr/container][21]: ^1.1.2|^2.0.2 * [psr/http-factory-implementation][22]: ^1.0 @@ -53,7 +53,9 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi Any Router which implements `Chubbyphp\Framework\Router\RouteMatcherInterface` can be used. - * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.1 + * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.3 + * [chubbyphp/chubbyphp-framework-router-symfony][31]: ^2.3 + ### PSR 7 / PSR 17 * [guzzlehttp/psr7][40]: ^2.7 (with [http-interop/http-factory-guzzle][41]: ^1.2) @@ -67,8 +69,8 @@ Any Router which implements `Chubbyphp\Framework\Router\RouteMatcherInterface` c Through [Composer](http://getcomposer.org) as [chubbyphp/chubbyphp-framework][60]. ```bash -composer require chubbyphp/chubbyphp-framework "^5.2" \ - chubbyphp/chubbyphp-framework-router-fastroute "^2.1" \ +composer require chubbyphp/chubbyphp-framework "^5.3" \ + chubbyphp/chubbyphp-framework-router-fastroute "^2.3" \ slim/psr7 "^1.7" ``` @@ -190,6 +192,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); [28]: https://packagist.org/packages/psr/log [30]: https://github.com/chubbyphp/chubbyphp-framework-router-fastroute#usage +[31]: https://github.com/chubbyphp/chubbyphp-framework-router-symfony#usage [40]: https://packagist.org/packages/guzzlehttp/psr7 [41]: https://packagist.org/packages/http-interop/http-factory-guzzle diff --git a/composer.json b/composer.json index d22d0cb..cb5c7e7 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } ], "require": { - "php": "^8.2", + "php": "^8.3", "chubbyphp/chubbyphp-http-exception": "^1.2", "psr/container": "^1.1.2|^2.0.2", "psr/http-factory-implementation": "^1.0", @@ -35,18 +35,18 @@ }, "require-dev": { "chubbyphp/chubbyphp-dev-helper": "dev-master", - "chubbyphp/chubbyphp-mock": "^2.0", - "guzzlehttp/psr7": "^2.7", + "chubbyphp/chubbyphp-mock": "^2.0.1", + "guzzlehttp/psr7": "^2.8", "http-interop/http-factory-guzzle": "^1.2", - "infection/infection": "^0.29.13", - "laminas/laminas-diactoros": "^3.5", + "infection/infection": "^0.31.9", + "laminas/laminas-diactoros": "^3.8", "nyholm/psr7": "^1.8.2", - "php-coveralls/php-coveralls": "^2.7.0", + "php-coveralls/php-coveralls": "^2.9", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.6", - "phpunit/phpunit": "^11.5.10", - "slim/psr7": "^1.7", - "sunrise/http-message": "^3.4.2" + "phpstan/phpstan": "^2.1.32", + "phpunit/phpunit": "^12.4.5", + "slim/psr7": "^1.8", + "sunrise/http-message": "^3.7" }, "autoload": { "psr-4": { "Chubbyphp\\Framework\\": "src/" } @@ -63,11 +63,11 @@ }, "extra": { "branch-alias": { - "dev-master": "5.2-dev" + "dev-master": "5.3-dev" } }, "scripts": { - "fix:cs": "mkdir -p build && PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --cache-file=build/phpcs.cache", + "fix:cs": "mkdir -p build && vendor/bin/php-cs-fixer fix --cache-file=build/phpcs.cache", "test": [ "@test:lint", "@test:unit", @@ -76,7 +76,7 @@ "@test:static-analysis", "@test:cs" ], - "test:cs": "mkdir -p build && PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --cache-file=build/phpcs.cache", + "test:cs": "mkdir -p build && vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --cache-file=build/phpcs.cache", "test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=100 --verbose --coverage=build/phpunit", "test:integration": "vendor/bin/phpunit --testsuite=Integration --cache-directory=build/phpunit", "test:lint": "mkdir -p build && find src tests -name '*.php' -print0 | xargs -0 -n1 -P$(nproc) php -l | tee build/phplint.log", diff --git a/src/Middleware/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index 7e6aba2..dc88433 100644 --- a/src/Middleware/ExceptionMiddleware.php +++ b/src/Middleware/ExceptionMiddleware.php @@ -16,7 +16,7 @@ final class ExceptionMiddleware implements MiddlewareInterface { - private const HTML = <<<'EOT' + private const string HTML = <<<'EOT' diff --git a/src/Middleware/SlimCallbackMiddleware.php b/src/Middleware/SlimCallbackMiddleware.php index f0afd23..0ec7851 100644 --- a/src/Middleware/SlimCallbackMiddleware.php +++ b/src/Middleware/SlimCallbackMiddleware.php @@ -12,7 +12,7 @@ final class SlimCallbackMiddleware implements MiddlewareInterface { - private const ATTRIBUTE_RESPONSE = 'response'; + private const string ATTRIBUTE_RESPONSE = 'response'; /** * @var callable diff --git a/src/RequestHandler/SlimCallbackRequestHandler.php b/src/RequestHandler/SlimCallbackRequestHandler.php index 621b224..69a4c54 100644 --- a/src/RequestHandler/SlimCallbackRequestHandler.php +++ b/src/RequestHandler/SlimCallbackRequestHandler.php @@ -11,7 +11,7 @@ final class SlimCallbackRequestHandler implements RequestHandlerInterface { - private const ATTRIBUTE_RESPONSE = 'response'; + private const string ATTRIBUTE_RESPONSE = 'response'; /** * @var callable diff --git a/tests/Integration/RouteMatcherLessTest.php b/tests/Integration/RouteMatcherLessTest.php index aca5ab7..d895056 100644 --- a/tests/Integration/RouteMatcherLessTest.php +++ b/tests/Integration/RouteMatcherLessTest.php @@ -13,6 +13,7 @@ use Laminas\Diactoros\ServerRequestFactory as LaminasServerRequestFactory; use Nyholm\Psr7\Factory\Psr17Factory as NyholmResponseFactory; use Nyholm\Psr7\Factory\Psr17Factory as NyholmServerRequestFactory; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ServerRequestFactoryInterface; @@ -28,9 +29,7 @@ */ final class RouteMatcherLessTest extends TestCase { - /** - * @dataProvider providePsr7Implementations - */ + #[DataProvider('providePsr7Implementations')] public function testMissingRouteMatcherMiddleware( ResponseFactoryInterface $responseFactory, ServerRequestFactoryInterface $serverRequestFactory @@ -54,9 +53,7 @@ public function testMissingRouteMatcherMiddleware( ); } - /** - * @dataProvider providePsr7Implementations - */ + #[DataProvider('providePsr7Implementations')] public function testMissingRouteMatcherMiddlewareWithoutExceptionMiddleware( ResponseFactoryInterface $responseFactory, ServerRequestFactoryInterface $serverRequestFactory From 425b316fa62d2460a194631b8cb2a4bf778ebb13 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 14 Dec 2025 22:29:49 +0100 Subject: [PATCH 39/55] thos property can be defined in __construct --- src/Router/Group.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/Router/Group.php b/src/Router/Group.php index d61b716..42f8fcf 100644 --- a/src/Router/Group.php +++ b/src/Router/Group.php @@ -8,16 +8,6 @@ final class Group implements GroupInterface { - /** - * @var array - */ - private array $middlewares; - - /** - * @var array - */ - private array $children; - /** * @param array $children * @param array $middlewares @@ -25,13 +15,10 @@ final class Group implements GroupInterface */ private function __construct( private string $path, - array $children = [], - array $middlewares = [], + private array $children = [], + private array $middlewares = [], private array $pathOptions = [] - ) { - $this->children = $children; - $this->middlewares = $middlewares; - } + ) {} /** * @param array $children From 3d3024b1311e9ec0e51c93ac215cac0b2ee71a14 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 14 Dec 2025 22:35:28 +0100 Subject: [PATCH 40/55] keep the possiblity to override routeRequestHandler --- src/Application.php | 9 ++------- tests/Unit/ApplicationTest.php | 5 ++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Application.php b/src/Application.php index 5a39c28..fc80df5 100644 --- a/src/Application.php +++ b/src/Application.php @@ -17,20 +17,15 @@ final class Application implements RequestHandlerInterface { private PipeMiddleware $pipeMiddleware; - private RequestHandlerInterface $routeRequestHandler; - - private EmitterInterface $emitter; - /** * @param array $middlewares */ public function __construct( array $middlewares, - ?EmitterInterface $emitter = null + private RequestHandlerInterface $routeRequestHandler = new RouteRequestHandler(), + private EmitterInterface $emitter = new Emitter() ) { $this->pipeMiddleware = new PipeMiddleware($middlewares); - $this->routeRequestHandler = new RouteRequestHandler(); - $this->emitter = $emitter ?? new Emitter(); } public function __invoke(ServerRequestInterface $request): ResponseInterface diff --git a/tests/Unit/ApplicationTest.php b/tests/Unit/ApplicationTest.php index 7dc09e2..3247a78 100644 --- a/tests/Unit/ApplicationTest.php +++ b/tests/Unit/ApplicationTest.php @@ -147,12 +147,15 @@ public function testEmit(): void /** @var MockObject|ResponseInterface $response */ $response = $builder->create(ResponseInterface::class, []); + /** @var RequestHandlerInterface $routeRequestHandler */ + $routeRequestHandler = $builder->create(RequestHandlerInterface::class, []); + /** @var EmitterInterface $emitter */ $emitter = $builder->create(EmitterInterface::class, [ new WithReturn('emit', [$response], null), ]); - $application = new Application([], $emitter); + $application = new Application([], $routeRequestHandler, $emitter); $application->emit($response); } } From 99b60e07b593ae1663f40d961637067763b4151d Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 14 Dec 2025 22:38:11 +0100 Subject: [PATCH 41/55] move another property to be defined in construct --- src/Router/Route.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Router/Route.php b/src/Router/Route.php index f12efa8..97973dc 100644 --- a/src/Router/Route.php +++ b/src/Router/Route.php @@ -9,11 +9,6 @@ final class Route implements RouteInterface { - /** - * @var array - */ - private array $middlewares; - /** * @var array */ @@ -28,11 +23,9 @@ private function __construct( private string $path, private string $name, private RequestHandlerInterface $requestHandler, - array $middlewares = [], + private array $middlewares = [], private array $pathOptions = [] - ) { - $this->middlewares = $middlewares; - } + ) {} /** * @param array $middlewares From bc5c9ae69802860b748c7c70c0c5294ed8c7d018 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 14 Dec 2025 22:40:01 +0100 Subject: [PATCH 42/55] fix changeset --- doc/Migration/5.x-6.x.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/doc/Migration/5.x-6.x.md b/doc/Migration/5.x-6.x.md index 5f0eeda..1ecb790 100644 --- a/doc/Migration/5.x-6.x.md +++ b/doc/Migration/5.x-6.x.md @@ -7,12 +7,4 @@ This makes the framework as bit less flexible for special use cases, but easier Resulting changes: - `Chubbyphp\Framework\Handler\RouteRequestHandler::__construct` does not take a `Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface` as argument anymore. -- `Chubbyphp\Framework\Application::__construct` does not take a `Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface` and `Chubbyphp\Framework\Handler\RouteRequestHandler` as argument anymore. - -**If you need a custom `Chubbyphp\Framework\Middleware\PipeMiddleware` for your use case:** - -I suggest you: -- Create an own `Chubbyphp\Framework\Application` and `Chubbyphp\Framework\Handler\RouteRequestHandler` onces you read the code you'll see it's little to replace and to maintain yourself. -- Create an issue convincing me to find a better solution with you in collaboration - -`Chubbyphp\Framework\Collection` gets dropped, cause i do not believe the additional runtime security for typing arrays was worth the performance hit. Use phpstan or something compatible to prevent mistakes in usage. +- `Chubbyphp\Framework\Application::__construct` does not take a `Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface` as argument anymore. From c0de4d66d1ce1414565ca98e5c59dbfb40e4f38b Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 14 Dec 2025 22:45:09 +0100 Subject: [PATCH 43/55] comment about collection --- doc/Migration/5.x-6.x.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/Migration/5.x-6.x.md b/doc/Migration/5.x-6.x.md index 1ecb790..1c029e3 100644 --- a/doc/Migration/5.x-6.x.md +++ b/doc/Migration/5.x-6.x.md @@ -8,3 +8,5 @@ This makes the framework as bit less flexible for special use cases, but easier Resulting changes: - `Chubbyphp\Framework\Handler\RouteRequestHandler::__construct` does not take a `Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface` as argument anymore. - `Chubbyphp\Framework\Application::__construct` does not take a `Chubbyphp\Framework\Middleware\MiddlewareDispatcherInterface` as argument anymore. + +`Chubbyphp\Framework\Collection` is gone, use phpstan if you want to make sure you providing the correct types on array of. From dc8a1cf13b55d3af3af2d81ab2357205d36e4d05 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Mon, 15 Dec 2025 12:04:51 +0100 Subject: [PATCH 44/55] $i is not needed anymore --- src/Router/RoutesByName.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Router/RoutesByName.php b/src/Router/RoutesByName.php index 065fb1b..4968537 100644 --- a/src/Router/RoutesByName.php +++ b/src/Router/RoutesByName.php @@ -16,7 +16,7 @@ final class RoutesByName implements RoutesByNameInterface */ public function __construct(array $routes) { - foreach ($routes as $i => $route) { + foreach ($routes as $route) { $this->routes[$route->getName()] = $route; } } From 2e78791f07568a89f177cfe020d52288a18f1b39 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Tue, 16 Dec 2025 13:24:58 +0100 Subject: [PATCH 45/55] test/syntax/quality first, then run tests --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index d8a29d1..6c9d066 100644 --- a/composer.json +++ b/composer.json @@ -70,11 +70,11 @@ "fix:cs": "mkdir -p build && vendor/bin/php-cs-fixer fix --cache-file=build/phpcs.cache", "test": [ "@test:lint", + "@test:static-analysis", + "@test:cs", "@test:unit", "@test:integration", - "@test:infection", - "@test:static-analysis", - "@test:cs" + "@test:infection" ], "test:cs": "mkdir -p build && vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --cache-file=build/phpcs.cache", "test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=100 --verbose --coverage=build/phpunit", From f127cbf2c7a6eca1267e02394b3d08a3b16c6192 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Wed, 24 Dec 2025 11:16:50 +0100 Subject: [PATCH 46/55] readonly properties --- src/Application.php | 6 +++--- src/Middleware/ExceptionMiddleware.php | 6 +++--- src/Middleware/LazyMiddleware.php | 2 +- src/Middleware/MiddlewareRequestHandler.php | 2 +- src/Middleware/PipeMiddleware.php | 2 +- src/Middleware/RouteMatcherMiddleware.php | 2 +- src/Middleware/SlimCallbackMiddleware.php | 2 +- src/Middleware/SlimLazyMiddleware.php | 6 +++--- src/RequestHandler/LazyRequestHandler.php | 2 +- src/RequestHandler/SlimCallbackRequestHandler.php | 2 +- src/RequestHandler/SlimLazyRequestHandler.php | 6 +++--- src/Router/Group.php | 8 ++++---- src/Router/Route.php | 12 ++++++------ src/Router/RoutesByName.php | 9 ++++++--- 14 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/Application.php b/src/Application.php index fc80df5..97e84b5 100644 --- a/src/Application.php +++ b/src/Application.php @@ -15,15 +15,15 @@ final class Application implements RequestHandlerInterface { - private PipeMiddleware $pipeMiddleware; + private readonly PipeMiddleware $pipeMiddleware; /** * @param array $middlewares */ public function __construct( array $middlewares, - private RequestHandlerInterface $routeRequestHandler = new RouteRequestHandler(), - private EmitterInterface $emitter = new Emitter() + private readonly RequestHandlerInterface $routeRequestHandler = new RouteRequestHandler(), + private readonly EmitterInterface $emitter = new Emitter() ) { $this->pipeMiddleware = new PipeMiddleware($middlewares); } diff --git a/src/Middleware/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index dc88433..a3eb698 100644 --- a/src/Middleware/ExceptionMiddleware.php +++ b/src/Middleware/ExceptionMiddleware.php @@ -163,11 +163,11 @@ final class ExceptionMiddleware implements MiddlewareInterface EOT; - private LoggerInterface $logger; + private readonly LoggerInterface $logger; public function __construct( - private ResponseFactoryInterface $responseFactory, - private bool $debug = false, + private readonly ResponseFactoryInterface $responseFactory, + private readonly bool $debug = false, ?LoggerInterface $logger = null ) { $this->logger = $logger ?? new NullLogger(); diff --git a/src/Middleware/LazyMiddleware.php b/src/Middleware/LazyMiddleware.php index 1acae5f..d999623 100644 --- a/src/Middleware/LazyMiddleware.php +++ b/src/Middleware/LazyMiddleware.php @@ -12,7 +12,7 @@ final class LazyMiddleware implements MiddlewareInterface { - public function __construct(private ContainerInterface $container, private string $id) {} + public function __construct(private readonly ContainerInterface $container, private readonly string $id) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/src/Middleware/MiddlewareRequestHandler.php b/src/Middleware/MiddlewareRequestHandler.php index 5c6b91d..dca2c68 100644 --- a/src/Middleware/MiddlewareRequestHandler.php +++ b/src/Middleware/MiddlewareRequestHandler.php @@ -11,7 +11,7 @@ final class MiddlewareRequestHandler implements RequestHandlerInterface { - public function __construct(private MiddlewareInterface $middleware, private RequestHandlerInterface $handler) {} + public function __construct(private readonly MiddlewareInterface $middleware, private readonly RequestHandlerInterface $handler) {} public function handle(ServerRequestInterface $request): ResponseInterface { diff --git a/src/Middleware/PipeMiddleware.php b/src/Middleware/PipeMiddleware.php index c839a41..2a704f2 100644 --- a/src/Middleware/PipeMiddleware.php +++ b/src/Middleware/PipeMiddleware.php @@ -14,7 +14,7 @@ final class PipeMiddleware implements MiddlewareInterface /** * @var array */ - private array $middlewares; + private readonly array $middlewares; /** * @param array $middlewares diff --git a/src/Middleware/RouteMatcherMiddleware.php b/src/Middleware/RouteMatcherMiddleware.php index 4289b66..d1dd6bc 100644 --- a/src/Middleware/RouteMatcherMiddleware.php +++ b/src/Middleware/RouteMatcherMiddleware.php @@ -12,7 +12,7 @@ final class RouteMatcherMiddleware implements MiddlewareInterface { - public function __construct(private RouteMatcherInterface $routeMatcher) {} + public function __construct(private readonly RouteMatcherInterface $routeMatcher) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/src/Middleware/SlimCallbackMiddleware.php b/src/Middleware/SlimCallbackMiddleware.php index 0ec7851..3194d23 100644 --- a/src/Middleware/SlimCallbackMiddleware.php +++ b/src/Middleware/SlimCallbackMiddleware.php @@ -19,7 +19,7 @@ final class SlimCallbackMiddleware implements MiddlewareInterface */ private $slimCallable; - public function __construct(callable $slimCallable, private ResponseFactoryInterface $responseFactory) + public function __construct(callable $slimCallable, private readonly ResponseFactoryInterface $responseFactory) { $this->slimCallable = $slimCallable; } diff --git a/src/Middleware/SlimLazyMiddleware.php b/src/Middleware/SlimLazyMiddleware.php index 3f81565..cdaaa93 100644 --- a/src/Middleware/SlimLazyMiddleware.php +++ b/src/Middleware/SlimLazyMiddleware.php @@ -14,9 +14,9 @@ final class SlimLazyMiddleware implements MiddlewareInterface { public function __construct( - private ContainerInterface $container, - private string $id, - private ResponseFactoryInterface $responseFactory + private readonly ContainerInterface $container, + private readonly string $id, + private readonly ResponseFactoryInterface $responseFactory ) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/src/RequestHandler/LazyRequestHandler.php b/src/RequestHandler/LazyRequestHandler.php index 9fa3bf0..efe1940 100644 --- a/src/RequestHandler/LazyRequestHandler.php +++ b/src/RequestHandler/LazyRequestHandler.php @@ -11,7 +11,7 @@ final class LazyRequestHandler implements RequestHandlerInterface { - public function __construct(private ContainerInterface $container, private string $id) {} + public function __construct(private readonly ContainerInterface $container, private readonly string $id) {} public function handle(ServerRequestInterface $request): ResponseInterface { diff --git a/src/RequestHandler/SlimCallbackRequestHandler.php b/src/RequestHandler/SlimCallbackRequestHandler.php index 69a4c54..34b4309 100644 --- a/src/RequestHandler/SlimCallbackRequestHandler.php +++ b/src/RequestHandler/SlimCallbackRequestHandler.php @@ -18,7 +18,7 @@ final class SlimCallbackRequestHandler implements RequestHandlerInterface */ private $slimCallable; - public function __construct(callable $slimCallable, private ResponseFactoryInterface $responseFactory) + public function __construct(callable $slimCallable, private readonly ResponseFactoryInterface $responseFactory) { $this->slimCallable = $slimCallable; } diff --git a/src/RequestHandler/SlimLazyRequestHandler.php b/src/RequestHandler/SlimLazyRequestHandler.php index bd0c601..2760fe5 100644 --- a/src/RequestHandler/SlimLazyRequestHandler.php +++ b/src/RequestHandler/SlimLazyRequestHandler.php @@ -13,9 +13,9 @@ final class SlimLazyRequestHandler implements RequestHandlerInterface { public function __construct( - private ContainerInterface $container, - private string $id, - private ResponseFactoryInterface $responseFactory + private readonly ContainerInterface $container, + private readonly string $id, + private readonly ResponseFactoryInterface $responseFactory ) {} public function handle(ServerRequestInterface $request): ResponseInterface diff --git a/src/Router/Group.php b/src/Router/Group.php index 42f8fcf..01ffd03 100644 --- a/src/Router/Group.php +++ b/src/Router/Group.php @@ -14,10 +14,10 @@ final class Group implements GroupInterface * @param array $pathOptions */ private function __construct( - private string $path, - private array $children = [], - private array $middlewares = [], - private array $pathOptions = [] + private readonly string $path, + private readonly array $children = [], + private readonly array $middlewares = [], + private readonly array $pathOptions = [] ) {} /** diff --git a/src/Router/Route.php b/src/Router/Route.php index 97973dc..3bfdb1e 100644 --- a/src/Router/Route.php +++ b/src/Router/Route.php @@ -19,12 +19,12 @@ final class Route implements RouteInterface * @param array $pathOptions */ private function __construct( - private string $method, - private string $path, - private string $name, - private RequestHandlerInterface $requestHandler, - private array $middlewares = [], - private array $pathOptions = [] + private readonly string $method, + private readonly string $path, + private readonly string $name, + private readonly RequestHandlerInterface $requestHandler, + private readonly array $middlewares = [], + private readonly array $pathOptions = [] ) {} /** diff --git a/src/Router/RoutesByName.php b/src/Router/RoutesByName.php index 4968537..3ab6f9c 100644 --- a/src/Router/RoutesByName.php +++ b/src/Router/RoutesByName.php @@ -9,16 +9,19 @@ final class RoutesByName implements RoutesByNameInterface /** * @var array */ - private array $routes = []; + private readonly array $routesByName; /** * @param array $routes */ public function __construct(array $routes) { + $routesByName = []; foreach ($routes as $route) { - $this->routes[$route->getName()] = $route; + $routesByName[$route->getName()] = $route; } + + $this->routesByName = $routesByName; } /** @@ -26,6 +29,6 @@ public function __construct(array $routes) */ public function getRoutesByName(): array { - return $this->routes; + return $this->routesByName; } } From bb4ec3af318ad9afbc1a2ffd73bb9f545ce5e8e5 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Wed, 24 Dec 2025 11:17:25 +0100 Subject: [PATCH 47/55] Add missing div to provide valid html in a error message --- src/Middleware/ExceptionMiddleware.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Middleware/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index a3eb698..335d983 100644 --- a/src/Middleware/ExceptionMiddleware.php +++ b/src/Middleware/ExceptionMiddleware.php @@ -273,6 +273,7 @@ private function addDebugToBody(array $exceptionsData): string $body .= '
'; } + $body .= '
'; return $body; } From 46a3537f5c7cdecb3adedb50cabe2ed277b54954 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sat, 27 Dec 2025 23:31:37 +0100 Subject: [PATCH 48/55] add readonly to properties add mixed type to callable to make it happen --- src/Middleware/CallbackMiddleware.php | 2 +- src/Middleware/SlimCallbackMiddleware.php | 2 +- src/RequestHandler/CallbackRequestHandler.php | 2 +- src/RequestHandler/SlimCallbackRequestHandler.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Middleware/CallbackMiddleware.php b/src/Middleware/CallbackMiddleware.php index f6c689d..08af05e 100644 --- a/src/Middleware/CallbackMiddleware.php +++ b/src/Middleware/CallbackMiddleware.php @@ -14,7 +14,7 @@ final class CallbackMiddleware implements MiddlewareInterface /** * @var callable */ - private $callback; + private readonly mixed $callback; public function __construct(callable $callback) { diff --git a/src/Middleware/SlimCallbackMiddleware.php b/src/Middleware/SlimCallbackMiddleware.php index 3194d23..a5540dd 100644 --- a/src/Middleware/SlimCallbackMiddleware.php +++ b/src/Middleware/SlimCallbackMiddleware.php @@ -17,7 +17,7 @@ final class SlimCallbackMiddleware implements MiddlewareInterface /** * @var callable */ - private $slimCallable; + private readonly mixed $slimCallable; public function __construct(callable $slimCallable, private readonly ResponseFactoryInterface $responseFactory) { diff --git a/src/RequestHandler/CallbackRequestHandler.php b/src/RequestHandler/CallbackRequestHandler.php index fe9505d..9d5884f 100644 --- a/src/RequestHandler/CallbackRequestHandler.php +++ b/src/RequestHandler/CallbackRequestHandler.php @@ -13,7 +13,7 @@ final class CallbackRequestHandler implements RequestHandlerInterface /** * @var callable */ - private $callback; + private readonly mixed $callback; public function __construct(callable $callback) { diff --git a/src/RequestHandler/SlimCallbackRequestHandler.php b/src/RequestHandler/SlimCallbackRequestHandler.php index 34b4309..2147787 100644 --- a/src/RequestHandler/SlimCallbackRequestHandler.php +++ b/src/RequestHandler/SlimCallbackRequestHandler.php @@ -16,7 +16,7 @@ final class SlimCallbackRequestHandler implements RequestHandlerInterface /** * @var callable */ - private $slimCallable; + private readonly mixed $slimCallable; public function __construct(callable $slimCallable, private readonly ResponseFactoryInterface $responseFactory) { From 3d5566b11d9f846c4a03e452b86972e24f3ac26c Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Mon, 12 Jan 2026 20:47:29 +0100 Subject: [PATCH 49/55] update year --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 7ff164f..f841fcc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2025 Dominik Zogg +Copyright (c) 2026 Dominik Zogg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a9f47f6..664e472 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); ## Copyright -2025 Dominik Zogg +2026 Dominik Zogg [1]: https://web-frameworks-benchmark.netlify.app/result From 8c2826c39d0c60fc6507e5b1cea4b97decc53a57 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Tue, 13 Jan 2026 22:18:36 +0100 Subject: [PATCH 50/55] phpstan level 9 opencode (opus 4.5) --- README.md | 2 +- composer.json | 4 ++-- phpstan.neon | 1 - src/Middleware/ExceptionMiddleware.php | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 664e472..188fa98 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements * php: ^8.3 - * [chubbyphp/chubbyphp-http-exception][20]: ^1.2 + * [chubbyphp/chubbyphp-http-exception][20]: ^1.3.1 * [psr/container][21]: ^1.1.2|^2.0.2 * [psr/http-factory-implementation][22]: ^1.0 * [psr/http-factory][23]: ^1.1 diff --git a/composer.json b/composer.json index 6c9d066..a4891f6 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ ], "require": { "php": "^8.3", - "chubbyphp/chubbyphp-http-exception": "^1.2", + "chubbyphp/chubbyphp-http-exception": "^1.3.1", "psr/container": "^1.1.2|^2.0.2", "psr/http-factory-implementation": "^1.0", "psr/http-factory": "^1.1", @@ -80,7 +80,7 @@ "test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=100 --verbose --coverage=build/phpunit", "test:integration": "vendor/bin/phpunit --testsuite=Integration --cache-directory=build/phpunit", "test:lint": "mkdir -p build && find src tests -name '*.php' -print0 | xargs -0 -n1 -P$(nproc) php -l | tee build/phplint.log", - "test:static-analysis": "mkdir -p build && bash -c 'vendor/bin/phpstan analyse src --no-progress --level=8 --error-format=junit | tee build/phpstan.junit.xml; if [ ${PIPESTATUS[0]} -ne \"0\" ]; then exit 1; fi'", + "test:static-analysis": "mkdir -p build && bash -c 'vendor/bin/phpstan analyse src --no-progress --level=9 --error-format=junit | tee build/phpstan.junit.xml; if [ ${PIPESTATUS[0]} -ne \"0\" ]; then exit 1; fi'", "test:unit": "vendor/bin/phpunit --testsuite=Unit --coverage-text --coverage-clover=build/phpunit/clover.xml --coverage-html=build/phpunit/coverage-html --coverage-xml=build/phpunit/coverage-xml --log-junit=build/phpunit/junit.xml --cache-directory=build/phpunit" } } diff --git a/phpstan.neon b/phpstan.neon index f51e71c..9631d44 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,2 +1 @@ parameters: - ignoreErrors: [] diff --git a/src/Middleware/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index 335d983..321b42f 100644 --- a/src/Middleware/ExceptionMiddleware.php +++ b/src/Middleware/ExceptionMiddleware.php @@ -236,7 +236,7 @@ private function exceptionToHttpException(\Throwable $exception): HttpExceptionI } /** - * @return array> + * @return array */ private function toExceptionsArray(\Throwable $exception): array { @@ -256,7 +256,7 @@ private function toExceptionsArray(\Throwable $exception): array } /** - * @param array> $exceptionsData + * @param array $exceptionsData */ private function addDebugToBody(array $exceptionsData): string { From ab8d6198ebff448f3a37c51ba421ec2ec2506547 Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Fri, 16 Jan 2026 21:36:30 +0100 Subject: [PATCH 51/55] vendor upgrade --- README.md | 20 ++++++++++---------- composer.json | 14 +++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 188fa98..a2db4d3 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements * php: ^8.3 - * [chubbyphp/chubbyphp-http-exception][20]: ^1.3.1 + * [chubbyphp/chubbyphp-http-exception][20]: ^1.3.2 * [psr/container][21]: ^1.1.2|^2.0.2 * [psr/http-factory-implementation][22]: ^1.0 * [psr/http-factory][23]: ^1.1 @@ -53,25 +53,25 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi Any Router which implements `Chubbyphp\Framework\Router\RouteMatcherInterface` can be used. - * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.3 - * [chubbyphp/chubbyphp-framework-router-symfony][31]: ^2.3 + * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.3.3 + * [chubbyphp/chubbyphp-framework-router-symfony][31]: ^2.3.3 ### PSR 7 / PSR 17 - * [guzzlehttp/psr7][40]: ^2.7 (with [http-interop/http-factory-guzzle][41]: ^1.2) - * [laminas/laminas-diactoros][42]: ^3.5 + * [guzzlehttp/psr7][40]: ^2.8 (with [http-interop/http-factory-guzzle][41]: ^1.2.1) + * [laminas/laminas-diactoros][42]: ^3.8 * [nyholm/psr7][43]: ^1.8.2 - * [slim/psr7][44]: ^1.7 - * [sunrise/http-message][45]: ^3.2 + * [slim/psr7][44]: ^1.8 + * [sunrise/http-message][45]: ^3.7 ## Installation Through [Composer](http://getcomposer.org) as [chubbyphp/chubbyphp-framework][60]. ```bash -composer require chubbyphp/chubbyphp-framework "^6.0" \ - chubbyphp/chubbyphp-framework-router-fastroute "^2.3" \ - slim/psr7 "^1.7" +composer require chubbyphp/chubbyphp-framework "^6.0.2" \ + chubbyphp/chubbyphp-framework-router-fastroute "^2.3.3" \ + slim/psr7 "^1.8" ``` ## Usage diff --git a/composer.json b/composer.json index a4891f6..e47bfd1 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ ], "require": { "php": "^8.3", - "chubbyphp/chubbyphp-http-exception": "^1.3.1", + "chubbyphp/chubbyphp-http-exception": "^1.3.2", "psr/container": "^1.1.2|^2.0.2", "psr/http-factory-implementation": "^1.0", "psr/http-factory": "^1.1", @@ -35,16 +35,16 @@ }, "require-dev": { "chubbyphp/chubbyphp-dev-helper": "dev-master", - "chubbyphp/chubbyphp-mock": "^2.0.1", + "chubbyphp/chubbyphp-mock": "^2.1.2", "guzzlehttp/psr7": "^2.8", - "http-interop/http-factory-guzzle": "^1.2", - "infection/infection": "^0.31.9", + "http-interop/http-factory-guzzle": "^1.2.1", + "infection/infection": "^0.32.3", "laminas/laminas-diactoros": "^3.8", "nyholm/psr7": "^1.8.2", - "php-coveralls/php-coveralls": "^2.9", + "php-coveralls/php-coveralls": "^2.9.1", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.32", - "phpunit/phpunit": "^12.4.5", + "phpstan/phpstan": "^2.1.33", + "phpunit/phpunit": "^12.5.6", "slim/psr7": "^1.8", "sunrise/http-message": "^3.7" }, From dc440e184f79d6ca12cb578ee5db6e857512cc5a Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Wed, 11 Feb 2026 21:12:22 +0100 Subject: [PATCH 52/55] array list< --- src/Middleware/ExceptionMiddleware.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Middleware/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index 321b42f..2eb3e6d 100644 --- a/src/Middleware/ExceptionMiddleware.php +++ b/src/Middleware/ExceptionMiddleware.php @@ -236,7 +236,7 @@ private function exceptionToHttpException(\Throwable $exception): HttpExceptionI } /** - * @return array + * @return list */ private function toExceptionsArray(\Throwable $exception): array { @@ -256,7 +256,7 @@ private function toExceptionsArray(\Throwable $exception): array } /** - * @param array $exceptionsData + * @param list $exceptionsData */ private function addDebugToBody(array $exceptionsData): string { From f8df3cce62bf9a19a6fc8eba7c6af28f2a4510bd Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 19 Apr 2026 13:21:15 +0200 Subject: [PATCH 53/55] upgrade actions --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 640398e..afca47b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: composer test uses: docker://ghcr.io/chubbyphp/ci-php83:latest env: @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: composer test uses: docker://ghcr.io/chubbyphp/ci-php84:latest env: @@ -33,14 +33,14 @@ jobs: runs-on: ubuntu-24.04 steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: composer test uses: docker://ghcr.io/chubbyphp/ci-php85:latest env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - name: sonarcloud.io - uses: sonarsource/sonarqube-scan-action@v6.0.0 + uses: SonarSource/sonarqube-scan-action@v7.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From b1c3468e4e66576124142d5855990bb8b9c7592e Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 19 Apr 2026 13:30:26 +0200 Subject: [PATCH 54/55] optimize phpunit calls --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e47bfd1..555510a 100644 --- a/composer.json +++ b/composer.json @@ -78,7 +78,7 @@ ], "test:cs": "mkdir -p build && vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --cache-file=build/phpcs.cache", "test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=100 --verbose --coverage=build/phpunit", - "test:integration": "vendor/bin/phpunit --testsuite=Integration --cache-directory=build/phpunit", + "test:integration": "vendor/bin/phpunit --testsuite=Integration --cache-directory=build/phpunit/integration --do-not-fail-on-empty-test-suite", "test:lint": "mkdir -p build && find src tests -name '*.php' -print0 | xargs -0 -n1 -P$(nproc) php -l | tee build/phplint.log", "test:static-analysis": "mkdir -p build && bash -c 'vendor/bin/phpstan analyse src --no-progress --level=9 --error-format=junit | tee build/phpstan.junit.xml; if [ ${PIPESTATUS[0]} -ne \"0\" ]; then exit 1; fi'", "test:unit": "vendor/bin/phpunit --testsuite=Unit --coverage-text --coverage-clover=build/phpunit/clover.xml --coverage-html=build/phpunit/coverage-html --coverage-xml=build/phpunit/coverage-xml --log-junit=build/phpunit/junit.xml --cache-directory=build/phpunit" From e433344504249b1f0478770886119da9fd1fe44c Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 5 Jul 2026 21:01:06 +0200 Subject: [PATCH 55/55] update dependencies --- README.md | 2 +- composer.json | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a2db4d3..2814eee 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ A minimal, highly [performant][1] middleware [PSR-15][8] microframework built wi ## Requirements * php: ^8.3 - * [chubbyphp/chubbyphp-http-exception][20]: ^1.3.2 + * [chubbyphp/chubbyphp-http-exception][20]: ^1.3.3 * [psr/container][21]: ^1.1.2|^2.0.2 * [psr/http-factory-implementation][22]: ^1.0 * [psr/http-factory][23]: ^1.1 diff --git a/composer.json b/composer.json index 555510a..e3475ae 100644 --- a/composer.json +++ b/composer.json @@ -23,28 +23,28 @@ ], "require": { "php": "^8.3", - "chubbyphp/chubbyphp-http-exception": "^1.3.2", + "chubbyphp/chubbyphp-http-exception": "^1.3.3", "psr/container": "^1.1.2|^2.0.2", - "psr/http-factory-implementation": "^1.0", "psr/http-factory": "^1.1", - "psr/http-message-implementation": "^1.0|^2.0", + "psr/http-factory-implementation": "^1.0", "psr/http-message": "^1.1|^2.0", + "psr/http-message-implementation": "^1.0|^2.0", "psr/http-server-handler": "^1.0.2", "psr/http-server-middleware": "^1.0.2", "psr/log": "^2.0|^3.0.2" }, "require-dev": { "chubbyphp/chubbyphp-dev-helper": "dev-master", - "chubbyphp/chubbyphp-mock": "^2.1.2", + "chubbyphp/chubbyphp-mock": "^2.2.1", "guzzlehttp/psr7": "^2.8", "http-interop/http-factory-guzzle": "^1.2.1", - "infection/infection": "^0.32.3", + "infection/infection": "^0.34.0", "laminas/laminas-diactoros": "^3.8", "nyholm/psr7": "^1.8.2", "php-coveralls/php-coveralls": "^2.9.1", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.33", - "phpunit/phpunit": "^12.5.6", + "phpstan/phpstan": "^2.2.5", + "phpunit/phpunit": "^12.5.30", "slim/psr7": "^1.8", "sunrise/http-message": "^3.7" },