diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4080f38 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +# 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 +.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 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5b415f..afca47b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,33 +3,44 @@ name: CI on: push: schedule: - - cron: '0 0 * * *' + - cron: "0 0 * * *" jobs: - php80: - name: PHP 8.0 - runs-on: ubuntu-20.04 + php83: + name: PHP 8.3 + runs-on: ubuntu-24.04 steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v6 - name: composer test - uses: docker://chubbyphp/ci-php80: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 }} - php81: - name: PHP 8.1 - runs-on: ubuntu-20.04 + php84: + name: PHP 8.4 + runs-on: ubuntu-24.04 steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v6 - name: composer test - uses: docker://chubbyphp/ci-php81: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 }} + php85: + name: PHP 8.5 + runs-on: ubuntu-24.04 + steps: + - name: checkout + 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/sonarcloud-github-action@master + uses: SonarSource/sonarqube-scan-action@v7.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 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/.php-cs-fixer.php b/.php-cs-fixer.php index 865b4bc..85172b8 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -12,13 +12,8 @@ /** @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) + ->setUnsupportedPhpVersionAllowed(true) ->setIndent($config['indent']) ->setLineEnding($config['lineEnding']) ->setRules($config['rules']) diff --git a/LICENSE b/LICENSE index 7b2b327..f841fcc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 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 a37975c..2814eee 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) -[![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) -[![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) +[![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)](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) @@ -36,14 +36,16 @@ 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 + * php: ^8.3 + * [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 + * [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.2 ## Suggest @@ -51,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]: ^1.3.1 + * [chubbyphp/chubbyphp-framework-router-fastroute][30]: ^2.3.3 + * [chubbyphp/chubbyphp-framework-router-symfony][31]: ^2.3.3 ### 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 - * [sunrise/http-message][45]: ^1.0 (with [sunrise/http-factory][46]: ^1.0) - * [laminas/laminas-diactoros][47]: ^2.0 + * [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.8 + * [sunrise/http-message][45]: ^3.7 ## Installation 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" \ - slim/psr7 "^1.5" +composer require chubbyphp/chubbyphp-framework "^6.0.2" \ + chubbyphp/chubbyphp-framework-router-fastroute "^2.3.3" \ + slim/psr7 "^1.8" ``` ## Usage @@ -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(); @@ -107,7 +109,7 @@ $app = new Application([ return $response; } )) - ])), $responseFactory), + ]))), ]); $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); @@ -122,7 +124,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 +158,8 @@ $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] * [1.x to 2.x][210] @@ -163,7 +167,7 @@ $app->emit($app->handle((new ServerRequestFactory())->createFromGlobals())); ## Copyright -Dominik Zogg 2022 +2026 Dominik Zogg [1]: https://web-frameworks-benchmark.netlify.app/result @@ -178,24 +182,25 @@ 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/psr/container +[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-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 +[31]: https://github.com/chubbyphp/chubbyphp-framework-router-symfony#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/sunrise/http-factory -[47]: https://packagist.org/packages/laminas/laminas-diactoros [60]: https://packagist.org/packages/chubbyphp/chubbyphp-framework @@ -204,7 +209,7 @@ Dominik Zogg 2022 [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 @@ -231,4 +236,7 @@ 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 +[214]: doc/Migration/5.x-6.x.md + [219]: doc/Migration/Slim-Chubbyphp.md diff --git a/composer.json b/composer.json index 513b544..e3475ae 100644 --- a/composer.json +++ b/composer.json @@ -22,32 +22,31 @@ } ], "require": { - "php": "^8.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-server-handler": "^1.0.1", - "psr/http-server-middleware": "^1.0.1", - "psr/log": "^1.1.4|^2.0|^3.0" + "php": "^8.3", + "chubbyphp/chubbyphp-http-exception": "^1.3.3", + "psr/container": "^1.1.2|^2.0.2", + "psr/http-factory": "^1.1", + "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": { - "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", - "sunrise/http-factory": "^1.0.4" + "chubbyphp/chubbyphp-mock": "^2.2.1", + "guzzlehttp/psr7": "^2.8", + "http-interop/http-factory-guzzle": "^1.2.1", + "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.2.5", + "phpunit/phpunit": "^12.5.30", + "slim/psr7": "^1.8", + "sunrise/http-message": "^3.7" }, "autoload": { "psr-4": { "Chubbyphp\\Framework\\": "src/" } @@ -64,26 +63,24 @@ }, "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "6.0-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:static-analysis", + "@test:cs", "@test:unit", "@test:integration", - "@test:infection", - "@test:static-analysis", - "@test:loc", - "@test:cs" + "@test:infection" ], - "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:integration": "vendor/bin/phpunit --testsuite=Integration --cache-result-file=build/phpunit/result.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/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=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" + "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/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/4.x-5.x.md b/doc/Migration/4.x-5.x.md new file mode 100644 index 0000000..8554afc --- /dev/null +++ b/doc/Migration/4.x-5.x.md @@ -0,0 +1,41 @@ +# 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] + +### 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 diff --git a/doc/Migration/5.x-6.x.md b/doc/Migration/5.x-6.x.md new file mode 100644 index 0000000..1c029e3 --- /dev/null +++ b/doc/Migration/5.x-6.x.md @@ -0,0 +1,12 @@ +# 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` as argument anymore. + +`Chubbyphp\Framework\Collection` is gone, use phpstan if you want to make sure you providing the correct types on array of. 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 f51e71c..9631d44 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,2 +1 @@ parameters: - ignoreErrors: [] diff --git a/phpunit.xml b/phpunit.xml index 4ea36f1..5adb3df 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,26 +1,20 @@ - - - - ./src - - - - - ./tests/Integration - - - ./tests/Unit - - + + + + + ./tests/Integration + + + ./tests/Unit + + + + + ./src + + + ./src + + diff --git a/src/Application.php b/src/Application.php index dde6b2a..97e84b5 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,30 +15,17 @@ final class Application implements RequestHandlerInterface { - /** - * @var array - */ - private array $middlewares; - - private MiddlewareDispatcherInterface $middlewareDispatcher; - - private RequestHandlerInterface $requestHandler; - - private EmitterInterface $emitter; + private readonly PipeMiddleware $pipeMiddleware; /** * @param array $middlewares */ public function __construct( array $middlewares, - ?MiddlewareDispatcherInterface $middlewareDispatcher = null, - ?RequestHandlerInterface $requestHandler = null, - ?EmitterInterface $emitter = null + private readonly RequestHandlerInterface $routeRequestHandler = new RouteRequestHandler(), + private readonly EmitterInterface $emitter = new Emitter() ) { - $this->middlewares = (new Collection($middlewares, [MiddlewareInterface::class]))->toArray(); - $this->middlewareDispatcher = $middlewareDispatcher ?? new MiddlewareDispatcher(); - $this->requestHandler = $requestHandler ?? new RouteRequestHandler($this->middlewareDispatcher); - $this->emitter = $emitter ?? new Emitter(); + $this->pipeMiddleware = new PipeMiddleware($middlewares); } public function __invoke(ServerRequestInterface $request): ResponseInterface @@ -49,11 +35,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 fa2691b..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/Emitter/Emitter.php b/src/Emitter/Emitter.php index 8b32992..21e35af 100644 --- a/src/Emitter/Emitter.php +++ b/src/Emitter/Emitter.php @@ -12,16 +12,20 @@ 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) { - header(sprintf('%s: %s', $name, $value), false); + header(\sprintf('%s: %s', $name, $value), false); } } 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/ExceptionMiddleware.php b/src/Middleware/ExceptionMiddleware.php index 9bf54bd..2eb3e6d 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; @@ -14,50 +16,158 @@ final class ExceptionMiddleware implements MiddlewareInterface { - private const HTML = <<<'EOT' + private const string HTML = <<<'EOT' + - %s + __TITLE__ - %s +
+
+
__STATUS__
+
+ __TITLE____BODY__ +
+
+
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(); @@ -74,32 +184,61 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface private function handleException(\Throwable $exception): ResponseInterface { - $exceptionsData = $this->toExceptionArray($exception); + $httpException = $this->exceptionToHttpException($exception); - $this->logger->error('Exception', ['exceptions' => $exceptionsData]); + $data = $httpException->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($httpException); + + $this->logger->{$logMethod}('Http Exception', [ + 'data' => $data, + 'exceptions' => $exceptions, + ]); - $response = $this->responseFactory->createResponse(500); + $lines = [ + ...(isset($data['detail']) ? ['

'.$data['detail'].'

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

'.$data['instance'].'

'] : []), + ...($this->debug ? [$this->addDebugToBody($exceptions)] : []), + ]; + + $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; } + 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> + * @return list */ - private function toExceptionArray(\Throwable $exception): array + private function toExceptionsArray(\Throwable $exception): array { $exceptions = []; do { @@ -117,16 +256,16 @@ private function toExceptionArray(\Throwable $exception): array } /** - * @param array> $exceptionsData + * @param list $exceptionsData */ private function addDebugToBody(array $exceptionsData): string { - $body = '

Details

'; + $body = '
'; foreach ($exceptionsData as $exceptionData) { - $body .= '
'; + $body .= '
'; foreach ($exceptionData as $key => $value) { - $body .= sprintf( - '
%s
%s
', + $body .= \sprintf( + '
%s
%s
', ucfirst($key), nl2br((string) $value) ); @@ -134,6 +273,7 @@ private function addDebugToBody(array $exceptionsData): string $body .= '
'; } + $body .= '
'; return $body; } diff --git a/src/Middleware/LazyMiddleware.php b/src/Middleware/LazyMiddleware.php index 4df3b7e..d999623 100644 --- a/src/Middleware/LazyMiddleware.php +++ b/src/Middleware/LazyMiddleware.php @@ -12,16 +12,14 @@ 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 { $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/MiddlewareDispatcher.php b/src/Middleware/MiddlewareDispatcher.php deleted file mode 100644 index 03d40b9..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 ($middlewareHandler, $middleware) => new MiddlewareRequestHandler($middleware, $middlewareHandler), - $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/MiddlewareRequestHandler.php b/src/Middleware/MiddlewareRequestHandler.php index cf4813a..dca2c68 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 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 new file mode 100644 index 0000000..2a704f2 --- /dev/null +++ b/src/Middleware/PipeMiddleware.php @@ -0,0 +1,36 @@ + + */ + private readonly 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/Middleware/RouteMatcherMiddleware.php b/src/Middleware/RouteMatcherMiddleware.php index 43475a1..d1dd6bc 100644 --- a/src/Middleware/RouteMatcherMiddleware.php +++ b/src/Middleware/RouteMatcherMiddleware.php @@ -4,75 +4,19 @@ 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 readonly 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 +25,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/Middleware/SlimCallbackMiddleware.php b/src/Middleware/SlimCallbackMiddleware.php index b95ab5b..a5540dd 100644 --- a/src/Middleware/SlimCallbackMiddleware.php +++ b/src/Middleware/SlimCallbackMiddleware.php @@ -12,14 +12,14 @@ final class SlimCallbackMiddleware implements MiddlewareInterface { - private const ATTRIBUTE_RESPONSE = 'response'; + private const string ATTRIBUTE_RESPONSE = 'response'; /** * @var callable */ - private $slimCallable; + private readonly mixed $slimCallable; - public function __construct(callable $slimCallable, private ResponseFactoryInterface $responseFactory) + public function __construct(callable $slimCallable, private readonly ResponseFactoryInterface $responseFactory) { $this->slimCallable = $slimCallable; } @@ -29,7 +29,9 @@ 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 fn (ServerRequestInterface $request, ResponseInterface $response) => $handler->handle( + $request->withAttribute(self::ATTRIBUTE_RESPONSE, $response) + ) ); } diff --git a/src/Middleware/SlimLazyMiddleware.php b/src/Middleware/SlimLazyMiddleware.php index 832253e..cdaaa93 100644 --- a/src/Middleware/SlimLazyMiddleware.php +++ b/src/Middleware/SlimLazyMiddleware.php @@ -13,16 +13,18 @@ final class SlimLazyMiddleware implements MiddlewareInterface { - public function __construct(private ContainerInterface $container, private string $id, private ResponseFactoryInterface $responseFactory) - { - } + public function __construct( + private readonly ContainerInterface $container, + private readonly string $id, + private readonly ResponseFactoryInterface $responseFactory + ) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $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/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/LazyRequestHandler.php b/src/RequestHandler/LazyRequestHandler.php index e5fce3d..efe1940 100644 --- a/src/RequestHandler/LazyRequestHandler.php +++ b/src/RequestHandler/LazyRequestHandler.php @@ -11,16 +11,14 @@ 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 { $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/RouteRequestHandler.php b/src/RequestHandler/RouteRequestHandler.php index 87a635e..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,10 +13,6 @@ final class RouteRequestHandler implements RequestHandlerInterface { - public function __construct(private MiddlewareDispatcherInterface $middlewareDispatcher) - { - } - public function handle(ServerRequestInterface $request): ResponseInterface { $route = $request->getAttribute('route'); @@ -25,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/RequestHandler/SlimCallbackRequestHandler.php b/src/RequestHandler/SlimCallbackRequestHandler.php index 621b224..2147787 100644 --- a/src/RequestHandler/SlimCallbackRequestHandler.php +++ b/src/RequestHandler/SlimCallbackRequestHandler.php @@ -11,14 +11,14 @@ final class SlimCallbackRequestHandler implements RequestHandlerInterface { - private const ATTRIBUTE_RESPONSE = 'response'; + private const string ATTRIBUTE_RESPONSE = 'response'; /** * @var callable */ - private $slimCallable; + private readonly mixed $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 354c57b..2760fe5 100644 --- a/src/RequestHandler/SlimLazyRequestHandler.php +++ b/src/RequestHandler/SlimLazyRequestHandler.php @@ -12,16 +12,18 @@ final class SlimLazyRequestHandler implements RequestHandlerInterface { - public function __construct(private ContainerInterface $container, private string $id, private ResponseFactoryInterface $responseFactory) - { - } + public function __construct( + private readonly ContainerInterface $container, + private readonly string $id, + private readonly ResponseFactoryInterface $responseFactory + ) {} 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/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/MissingRouteAttributeOnRequestException.php b/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php index 29e3070..762d3fb 100644 --- a/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php +++ b/src/Router/Exceptions/MissingRouteAttributeOnRequestException.php @@ -13,15 +13,15 @@ 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( - '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/MissingRouteByNameException.php b/src/Router/Exceptions/MissingRouteByNameException.php index a6ed851..5bdaa11 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..e825664 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,16 @@ 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( - '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; + 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), + $previous instanceof \Throwable ? ' '.$previous->getMessage() : '', + ), + 3, + $previous + ); } } diff --git a/src/Router/Exceptions/RouterException.php b/src/Router/Exceptions/RouterException.php index 599dfe6..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 \RuntimeException -{ -} +abstract class RouterException extends \LogicException {} diff --git a/src/Router/Group.php b/src/Router/Group.php index efebce0..01ffd03 100644 --- a/src/Router/Group.php +++ b/src/Router/Group.php @@ -4,31 +4,21 @@ namespace Chubbyphp\Framework\Router; -use Chubbyphp\Framework\Collection; use Psr\Http\Server\MiddlewareInterface; final class Group implements GroupInterface { - /** - * @var array - */ - private array $middlewares; - - /** - * @var array - */ - private array $children; - /** * @param array $children * @param array $middlewares * @param 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(); - } + private function __construct( + private readonly string $path, + private readonly array $children = [], + private readonly array $middlewares = [], + private readonly array $pathOptions = [] + ) {} /** * @param array $children diff --git a/src/Router/Route.php b/src/Router/Route.php index d0fedc3..3bfdb1e 100644 --- a/src/Router/Route.php +++ b/src/Router/Route.php @@ -4,18 +4,11 @@ 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; final class Route implements RouteInterface { - /** - * @var array - */ - private array $middlewares; - /** * @var array */ @@ -26,15 +19,13 @@ final class Route implements RouteInterface * @param array $pathOptions */ private function __construct( - private string $method, - private string $path, - private string $name, - private RequestHandlerInterface $requestHandler, - array $middlewares = [], - private array $pathOptions = [] - ) { - $this->middlewares = (new Collection($middlewares, [MiddlewareInterface::class]))->toArray(); - } + private readonly string $method, + private readonly string $path, + private readonly string $name, + private readonly RequestHandlerInterface $requestHandler, + private readonly array $middlewares = [], + private readonly array $pathOptions = [] + ) {} /** * @param array $middlewares @@ -62,7 +53,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 +67,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 +81,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 +95,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 +109,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 +123,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 +137,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/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/Routes.php deleted file mode 100644 index ee3a4ea..0000000 --- a/src/Router/Routes.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ - private array $routes = []; - - /** - * @param array $routes - */ - 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; - } - } - - /** - * @return array - */ - public function getRoutesByName(): array - { - return $this->routes; - } -} diff --git a/src/Router/RoutesByName.php b/src/Router/RoutesByName.php new file mode 100644 index 0000000..3ab6f9c --- /dev/null +++ b/src/Router/RoutesByName.php @@ -0,0 +1,34 @@ + + */ + private readonly array $routesByName; + + /** + * @param array $routes + */ + public function __construct(array $routes) + { + $routesByName = []; + foreach ($routes as $route) { + $routesByName[$route->getName()] = $route; + } + + $this->routesByName = $routesByName; + } + + /** + * @return array + */ + public function getRoutesByName(): array + { + return $this->routesByName; + } +} 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..dfae686 100644 --- a/src/Router/UrlGeneratorInterface.php +++ b/src/Router/UrlGeneratorInterface.php @@ -19,7 +19,7 @@ public function generateUrl( ServerRequestInterface $request, string $name, array $attributes = [], - array $queryParams = [] + array $queryParams = [], ): string; /** 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(), diff --git a/tests/Integration/MiddlewareDispatcherTest.php b/tests/Integration/MiddlewareDispatcherTest.php deleted file mode 100644 index cfaab80..0000000 --- a/tests/Integration/MiddlewareDispatcherTest.php +++ /dev/null @@ -1,161 +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(RequestMethod::METHOD_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(RequestMethod::METHOD_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(RequestMethod::METHOD_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/Integration/RouteMatcherLessTest.php b/tests/Integration/RouteMatcherLessTest.php index 3a3351b..d895056 100644 --- a/tests/Integration/RouteMatcherLessTest.php +++ b/tests/Integration/RouteMatcherLessTest.php @@ -4,25 +4,23 @@ 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; 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; 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 @@ -31,39 +29,7 @@ */ final class RouteMatcherLessTest extends TestCase { - public function providePsr7Implementations(): array - { - return [ - 'bitty' => [ - 'responseFactory' => new BittyResponseFactory(), - 'serverRequestFactory' => new BittyServerRequestFactory(), - ], - '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 - */ + #[DataProvider('providePsr7Implementations')] public function testMissingRouteMatcherMiddleware( ResponseFactoryInterface $responseFactory, ServerRequestFactoryInterface $serverRequestFactory @@ -72,7 +38,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); @@ -87,9 +53,7 @@ public function testMissingRouteMatcherMiddleware( ); } - /** - * @dataProvider providePsr7Implementations - */ + #[DataProvider('providePsr7Implementations')] public function testMissingRouteMatcherMiddlewareWithoutExceptionMiddleware( ResponseFactoryInterface $responseFactory, ServerRequestFactoryInterface $serverRequestFactory @@ -102,8 +66,34 @@ public function testMissingRouteMatcherMiddlewareWithoutExceptionMiddleware( $app = new Application([]); - $request = $serverRequestFactory->createServerRequest(RequestMethod::METHOD_GET, '/hello/test'); + $request = $serverRequestFactory->createServerRequest('GET', '/hello/test'); $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(), + ], + ]; + } } diff --git a/tests/Unit/ApplicationTest.php b/tests/Unit/ApplicationTest.php index f09f0db..3247a78 100644 --- a/tests/Unit/ApplicationTest.php +++ b/tests/Unit/ApplicationTest.php @@ -6,10 +6,11 @@ use Chubbyphp\Framework\Application; 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,107 +25,137 @@ */ 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|MockObject $middleware */ - $middleware = $this->getMockByCalls(MiddlewareInterface::class); + /** @var MockObject|ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); + + /** @var MiddlewareInterface $middleware */ + $middleware = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) + ), + ]); /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithCallback( + 'handle', + static fn ( + ServerRequestInterface $request, + ) => $response, + ), + ]); /** @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), + /** @var MiddlewareInterface $routeIndependentMiddleware */ + $routeIndependentMiddleware = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) + ), ]); $application = new Application([ - $routeIndependMiddleware, - ], $middlewareDispatcher); + $routeIndependentMiddleware, + ]); self::assertSame($response, $application($request)); } public function testHandle(): void { - /** @var MiddlewareInterface|MockObject $routeIndependMiddleware */ - $routeIndependMiddleware = $this->getMockByCalls(MiddlewareInterface::class); - - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $builder = new MockObjectBuilder(); /** @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 MiddlewareInterface $middleware */ + $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, [ + new WithCallback( + 'handle', + static fn ( + ServerRequestInterface $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 MockObject|RequestHandlerInterface $requestHandler */ - $requestHandler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var MiddlewareInterface $routeIndependentMiddleware */ + $routeIndependentMiddleware = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) + ), ]); $application = new Application([ - $routeIndependMiddleware, - ], $middlewareDispatcher, $requestHandler); + $routeIndependentMiddleware, + ]); 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 RequestHandlerInterface $routeRequestHandler */ + $routeRequestHandler = $builder->create(RequestHandlerInterface::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); + $application = new Application([], $routeRequestHandler, $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/Emitter/EmitterTest.php b/tests/Unit/Emitter/EmitterTest.php index fde811d..f40abf7 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); @@ -44,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; @@ -58,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 a12b6f6..7b7248c 100644 --- a/tests/Unit/Middleware/ExceptionMiddlewareTest.php +++ b/tests/Unit/Middleware/ExceptionMiddlewareTest.php @@ -5,9 +5,12 @@ namespace Chubbyphp\Tests\Framework\Unit\Middleware; use Chubbyphp\Framework\Middleware\ExceptionMiddleware; -use Chubbyphp\Mock\Argument\ArgumentCallback; -use Chubbyphp\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; +use Chubbyphp\HttpException\HttpException; +use Chubbyphp\Mock\MockMethod\WithCallback; +use Chubbyphp\Mock\MockMethod\WithException; +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; @@ -24,94 +27,207 @@ */ 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); 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)); + + $builder = new MockObjectBuilder(); /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(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; /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write')->with($expectedBody), + $responseBody = $builder->create(StreamInterface::class, [ + new WithReturn('write', [$expectedBody], \strlen($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($exception), + $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', [418, ''], $response), ]); $middleware = new ExceptionMiddleware($responseFactory); @@ -119,101 +235,221 @@ 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)); + + $builder = new MockObjectBuilder(); /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(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; /** @var MockObject|StreamInterface $responseBody */ - $responseBody = $this->getMockByCalls(StreamInterface::class, [ - Call::create('write')->with($expectedBody), + $responseBody = $builder->create(StreamInterface::class, [ + new WithReturn('write', [$expectedBody], \strlen($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($exception), + $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', [404, ''], $response), ]); /** @var LoggerInterface|MockObject $logger */ - $logger = $this->getMockByCalls(LoggerInterface::class, [ - Call::create('error')->with( - 'Exception', - new ArgumentCallback(static function (array $context): void { - 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']); - }) - ), + $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); @@ -221,55 +457,221 @@ 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)); + + $builder = new MockObjectBuilder(); /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class); + $request = $builder->create(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); - })), + $responseBody = $builder->create(StreamInterface::class, [ + new WithReturn('write', [$expectedBody], \strlen($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($exception), + $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), ]); - $middleware = new ExceptionMiddleware($responseFactory, true); + /** @var LoggerInterface|MockObject $logger */ + $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); self::assertSame($response, $middleware->process($request, $handler)); } @@ -278,78 +680,96 @@ 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('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); - })), + $responseBody = $builder->create(StreamInterface::class, [ + new WithCallback('write', static function (string $html): int { + 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); + + return \strlen($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( - 'Exception', - new ArgumentCallback(static function (array $context): void { - 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']); - }) - ), + $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 deleted file mode 100644 index 8f989cd..0000000 --- a/tests/Unit/Middleware/MiddlewareDispatcherTest.php +++ /dev/null @@ -1,95 +0,0 @@ -getMockByCalls(ServerRequestInterface::class); - - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), - ]); - - $middlewareDispatcher = new MiddlewareDispatcher(); - - self::assertSame($response, $middlewareDispatcher->dispatch([], $handler, $request)); - } - - 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(), - ]); - - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($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|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); - } - ), - ]); - - $middlewareDispatcher = new MiddlewareDispatcher(); - - self::assertSame( - $response, - $middlewareDispatcher->dispatch([$middleware1, $middleware2], $handler, $request) - ); - } -} 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/PipeMiddlewareTest.php b/tests/Unit/Middleware/PipeMiddlewareTest.php new file mode 100644 index 0000000..b758aa3 --- /dev/null +++ b/tests/Unit/Middleware/PipeMiddlewareTest.php @@ -0,0 +1,100 @@ +create(ServerRequestInterface::class, []); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), + ]); + + $pipeMiddleware = new PipeMiddleware([]); + + self::assertSame($response, $pipeMiddleware->process($request, $handler)); + } + + public function testWithMiddlewares(): void + { + $builder = new MockObjectBuilder(); + + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturnSelf('withAttribute', ['middleware', 1]), + new WithReturnSelf('withAttribute', ['middleware', 2]), + ]); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); + + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), + ]); + + /** @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 $middleware2 */ + $middleware2 = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static function ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) { + $request = $request->withAttribute('middleware', 2); + + return $requestHandler->handle($request); + }, + ), + ]); + + $pipeMiddleware = new PipeMiddleware([$middleware1, $middleware2]); + + self::assertSame( + $response, + $pipeMiddleware->process($request, $handler) + ); + } +} diff --git a/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php b/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php index c30f560..3c0f81a 100644 --- a/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php +++ b/tests/Unit/Middleware/RouteMatcherMiddlewareTest.php @@ -5,19 +5,17 @@ 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\Mock\Call; -use Chubbyphp\Mock\MockByCallsTrait; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\HttpException\HttpException; +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\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 @@ -26,198 +24,62 @@ */ 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']), - ]); - - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('withAttribute')->with('route', $route)->willReturnSelf(), - Call::create('withAttribute')->with('key', 'value')->willReturnSelf(), - ]); - - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $builder = new MockObjectBuilder(); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ - Call::create('handle')->with($request)->willReturn($response), + /** @var RouteInterface $route */ + $route = $builder->create(RouteInterface::class, [ + new WithReturn('getAttributes', [], ['key' => 'value']), ]); - /** @var MockObject|RouteMatcherInterface $router */ - $router = $this->getMockByCalls(RouteMatcherInterface::class, [ - Call::create('match')->with($request)->willReturn($route), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturnSelf('withAttribute', ['route', $route]), + new WithReturnSelf('withAttribute', ['key', 'value']), ]); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - $middleware = new RouteMatcherMiddleware($router, $responseFactory); - - self::assertSame($response, $middleware->process($request, $handler)); - } - - public function testProcessMissingRouteWithoutLogger(): 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 RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, [ + new WithReturn('handle', [$request], $response), ]); - /** @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 RouteMatcherInterface $router */ + $router = $builder->create(RouteMatcherInterface::class, [ + new WithReturn('match', [$request], $route), ]); - /** @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), - ]); - - $middleware = new RouteMatcherMiddleware($router, $responseFactory); + $middleware = new RouteMatcherMiddleware($router); self::assertSame($response, $middleware->process($request, $handler)); } - public function testProcessMissingRouteWithLogger(): 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), + $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.', ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class, [ - Call::create('withHeader')->with('Content-Type', 'text/html')->willReturnSelf(), - Call::create('getBody')->with()->willReturn($responseBody), - ]); + $this->expectExceptionObject($httpException); - /** @var MockObject|RequestHandlerInterface $handler */ - $handler = $this->getMockByCalls(RequestHandlerInterface::class); + $builder = new MockObjectBuilder(); - /** @var MockObject|RouteMatcherInterface $router */ - $router = $this->getMockByCalls(RouteMatcherInterface::class, [ - Call::create('match')->with($request)->willThrowException($routerException), - ]); + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, []); - /** @var MockObject|ResponseFactoryInterface $responseFactory */ - $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ - Call::create('createResponse')->with(404, '')->willReturn($response), - ]); + /** @var RequestHandlerInterface $handler */ + $handler = $builder->create(RequestHandlerInterface::class, []); - /** @var LoggerInterface|MockObject $logger */ - $logger = $this->getMockByCalls(LoggerInterface::class, [ - Call::create('info')->with('Route exception', [ - 'title' => $routerException->getTitle(), - 'message' => $routerException->getMessage(), - 'code' => $routerException->getCode(), - ]), + /** @var RouteMatcherInterface $router */ + $router = $builder->create(RouteMatcherInterface::class, [ + new WithException('match', [$request], $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/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..c6d78d5 100644 --- a/tests/Unit/RequestHandler/RouteRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/RouteRequestHandlerTest.php @@ -4,13 +4,12 @@ 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\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; @@ -24,8 +23,6 @@ */ final class RouteRequestHandlerTest extends TestCase { - use MockByCallsTrait; - public function testHandleWithoutRoute(): void { $this->expectException(MissingRouteAttributeOnRequestException::class); @@ -34,50 +31,61 @@ 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 MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['route', null], null), + ]); - /** @var MiddlewareDispatcherInterface|MockObject $middlewareDispatcher */ - $middlewareDispatcher = $this->getMockByCalls(MiddlewareDispatcherInterface::class); + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); - $requestHandler = new RouteRequestHandler($middlewareDispatcher); + $requestHandler = new RouteRequestHandler(); self::assertSame($response, $requestHandler->handle($request)); } public function testHandleWithRoute(): void { - /** @var MiddlewareInterface|MockObject $middleware */ - $middleware = $this->getMockByCalls(MiddlewareInterface::class); - - /** @var MockObject|RequestHandlerInterface $requestHandler */ - $requestHandler = $this->getMockByCalls(RequestHandlerInterface::class); - - /** @var MockObject|RouteInterface $route */ - $route = $this->getMockByCalls(RouteInterface::class, [ - Call::create('getMiddlewares')->with()->willReturn([$middleware]), - Call::create('getRequestHandler')->with()->willReturn($requestHandler), + $builder = new MockObjectBuilder(); + + /** @var ResponseInterface $response */ + $response = $builder->create(ResponseInterface::class, []); + + /** @var MiddlewareInterface $middleware */ + $middleware = $builder->create(MiddlewareInterface::class, [ + new WithCallback( + 'process', + static fn ( + ServerRequestInterface $request, + RequestHandlerInterface $requestHandler + ) => $requestHandler->handle($request) + ), ]); - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getAttribute')->with('route', null)->willReturn($route), + /** @var RequestHandlerInterface $innerRequestHandler */ + $innerRequestHandler = $builder->create(RequestHandlerInterface::class, [ + new WithCallback( + 'handle', + static fn ( + ServerRequestInterface $request, + ) => $response, + ), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var RouteInterface $route */ + $route = $builder->create(RouteInterface::class, [ + new WithReturn('getMiddlewares', [], [$middleware]), + new WithReturn('getRequestHandler', [], $innerRequestHandler), + ]); - /** @var MiddlewareDispatcherInterface|MockObject $middlewareDispatcher */ - $middlewareDispatcher = $this->getMockByCalls(MiddlewareDispatcherInterface::class, [ - Call::create('dispatch')->with([$middleware], $requestHandler, $request)->willReturn($response), + /** @var ServerRequestInterface $request */ + $request = $builder->create(ServerRequestInterface::class, [ + new WithReturn('getAttribute', ['route', null], $route), ]); - $requestHandler = new RouteRequestHandler($middlewareDispatcher); + $requestHandler = new RouteRequestHandler(); self::assertSame($response, $requestHandler->handle($request)); } 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/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/GroupTest.php b/tests/Unit/Router/GroupTest.php index 1356ebe..c95bd98 100644 --- a/tests/Unit/Router/GroupTest.php +++ b/tests/Unit/Router/GroupTest.php @@ -7,8 +7,7 @@ use Chubbyphp\Framework\Router\Group; use Chubbyphp\Framework\Router\Route; use Chubbyphp\Framework\Router\RouteInterface; -use Chubbyphp\Mock\MockByCallsTrait; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Server\MiddlewareInterface; @@ -21,8 +20,6 @@ */ final class GroupTest extends TestCase { - use MockByCallsTrait; - public function testMinimal(): void { $group = Group::create(''); @@ -30,30 +27,21 @@ 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 { - /** @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]+']]), @@ -73,21 +61,21 @@ public function testMaximal(): void self::assertCount(3, $routes); - /** @var RouteInterface */ + /** @var RouteInterface $route1 */ $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()); self::assertSame($handler, $route1->getRequestHandler()); - /** @var RouteInterface */ + /** @var RouteInterface $route2 */ $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]+']], @@ -96,11 +84,11 @@ 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()); - 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..58751b2 100644 --- a/tests/Unit/Router/RouteTest.php +++ b/tests/Unit/Router/RouteTest.php @@ -5,9 +5,7 @@ namespace Chubbyphp\Tests\Framework\Unit\Router; use Chubbyphp\Framework\Router\Route; -use Chubbyphp\Mock\MockByCallsTrait; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; -use PHPUnit\Framework\MockObject\MockObject; +use Chubbyphp\Mock\MockObjectBuilder; use PHPUnit\Framework\TestCase; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -19,17 +17,17 @@ */ 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(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()); @@ -39,17 +37,19 @@ 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( - RequestMethod::METHOD_GET, + 'GET', '/{id}', 'read', $handler, @@ -58,7 +58,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()); @@ -68,13 +68,15 @@ 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); 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()); @@ -84,19 +86,21 @@ 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+']]); 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()); @@ -106,13 +110,15 @@ 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); 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()); @@ -122,19 +128,21 @@ 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+']]); 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()); @@ -144,13 +152,15 @@ 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); 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()); @@ -160,19 +170,21 @@ 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+']]); 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()); @@ -182,13 +194,15 @@ 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); 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()); @@ -198,19 +212,21 @@ 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+']]); 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()); @@ -220,13 +236,15 @@ 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); 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()); @@ -236,19 +254,21 @@ 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+']]); 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()); @@ -258,13 +278,15 @@ 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); 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()); @@ -274,19 +296,21 @@ 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+']]); 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()); @@ -296,13 +320,15 @@ 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); 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()); @@ -312,19 +338,21 @@ 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+']]); 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()); @@ -334,10 +362,12 @@ 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(RequestMethod::METHOD_GET, '/{id}', 'read', $handler); + $route = Route::create('GET', '/{id}', 'read', $handler); $routeClone = $route->withAttributes(['id' => 5]); diff --git a/tests/Unit/Router/RoutesByNameTest.php b/tests/Unit/Router/RoutesByNameTest.php new file mode 100644 index 0000000..e64f52e --- /dev/null +++ b/tests/Unit/Router/RoutesByNameTest.php @@ -0,0 +1,43 @@ +create(RouteInterface::class, [ + new WithReturn('getName', [], 'name1'), + ]); + + $route2 = $builder->create(RouteInterface::class, [ + new WithReturn('getName', [], 'name2'), + ]); + + $routes = new RoutesByName([$route1, $route2]); + + self::assertSame(['name1' => $route1, 'name2' => $route2], $routes->getRoutesByName()); + } + + public function testWithoutRoutes(): void + { + $routes = new RoutesByName([]); + + self::assertSame([], $routes->getRoutesByName()); + } +} diff --git a/tests/Unit/Router/RoutesTest.php b/tests/Unit/Router/RoutesTest.php deleted file mode 100644 index a7b3cc0..0000000 --- a/tests/Unit/Router/RoutesTest.php +++ /dev/null @@ -1,56 +0,0 @@ -expectException(\TypeError::class); - $this->expectExceptionMessage( - 'Chubbyphp\Framework\Router\Routes::__construct() expects parameter 1' - .' at index 0 to be Chubbyphp\Framework\Router\RouteInterface, stdClass given' - ); - - $route = new \stdClass(); - - new Routes([$route]); - } - - public function testGetRoutes(): void - { - $route1 = $this->getMockByCalls(RouteInterface::class, [ - Call::create('getName')->with()->willReturn('name1'), - ]); - - $route2 = $this->getMockByCalls(RouteInterface::class, [ - Call::create('getName')->with()->willReturn('name2'), - ]); - - $routes = new Routes([$route1, $route2]); - - self::assertSame(['name1' => $route1, 'name2' => $route2], $routes->getRoutesByName()); - } - - public function testWithoutRoutes(): void - { - $routes = new Routes([]); - - self::assertSame([], $routes->getRoutesByName()); - } -}