diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eba7166..10b872c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] + php-versions: [ '8.3', '8.4', '8.5' ] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 3b075ca..2a5caa3 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -15,10 +15,10 @@ jobs: fetch-depth: 0 # To avoid "Shallow clone detected" error in SonarCloud report #~ PHP Setup - - name: Setup PHP 8.1 # Minimum version + - name: Setup PHP 8.3 # Minimum version uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.3' #~ Composer Cache - name: Get Composer Cache Directory @@ -41,6 +41,6 @@ jobs: run: make php/tests # To generate the coverage report - name: SonarQube Scan - uses: SonarSource/sonarqube-scan-action@v5.2.0 + uses: SonarSource/sonarqube-scan-action@v7 env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 87d2ce9..1eb221a 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -4,7 +4,7 @@ //~ Rules ->setRules( [ - '@PER-CS2.0' => true, + '@PER-CS3x0' => true, ] ) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92ac6c4..a2a9c29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ``` ---- +## [6.0.0] - 2026-01-16 +[6.0.0]: https://github.com/eureka-framework/component-http/compare/5.4.0...6.0.0 +### Added +- PHP 8.5 compatibility +### Changed +- Fix bug with RequestHandler::withMiddleware() +- Add more tests +- Replace usage of deprecated attach() & detach() functions +### Removed +- Drop support of PHP versions < 8.3 + +---- + ## [5.4.0] - 2025-05-16 [5.4.0]: https://github.com/eureka-framework/component-http/compare/5.3.0...5.4.0 ### Added diff --git a/Makefile b/Makefile index ba66a38..199be3e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ .PHONY: validate install update php/deps php/check php/fix php/min-compatibility php/max-compatibility php/phpstan php/analyze php/tests php/test php/testdox ci clean +PHP_MIN_VERSION := "8.3" +PHP_MAX_VERSION := "8.5" COMPOSER_BIN := composer define header = @if [ -t 1 ]; then printf "\n\e[37m\e[100m \e[104m $(1) \e[0m\n"; else printf "\n### $(1)\n"; fi @@ -40,17 +42,17 @@ php/deps: composer.json php/check: vendor/bin/php-cs-fixer $(call header,Checking Code Style) - @PHP_CS_FIXER_IGNORE_ENV=1 ./vendor/bin/php-cs-fixer check -v --diff + @XDEBUG_MODE=off ./vendor/bin/php-cs-fixer check php/fix: vendor/bin/php-cs-fixer $(call header,Fixing Code Style) - @PHP_CS_FIXER_IGNORE_ENV=1 ./vendor/bin/php-cs-fixer fix -v + @XDEBUG_MODE=off ./vendor/bin/php-cs-fixer fix -v php/min-compatibility: vendor/bin/phpstan build/reports/phpstan - $(call header,Checking PHP 7.4 compatibility) + $(call header,Checking PHP ${PHP_MIN_VERSION} compatibility) @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --configuration=./ci/phpmin-compatibility.neon --error-format=table php/max-compatibility: vendor/bin/phpstan build/reports/phpstan #ci - $(call header,Checking PHP 8.4 compatibility) + $(call header,Checking PHP ${PHP_MAX_VERSION} compatibility) @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --configuration=./ci/phpmax-compatibility.neon --error-format=table php/analyze: vendor/bin/phpstan build/reports/phpstan #manual & ci @@ -59,7 +61,7 @@ php/analyze: vendor/bin/phpstan build/reports/phpstan #manual & ci php/tests: vendor/bin/phpunit build/reports/phpunit #ci $(call header,Running Unit Tests) - @XDEBUG_MODE=coverage php -dzend_extension=xdebug.so ./vendor/bin/phpunit --testsuite=unit --coverage-clover=./build/reports/phpunit/clover.xml --log-junit=./build/reports/phpunit/unit.xml --coverage-php=./build/reports/phpunit/unit.cov --coverage-html=./build/reports/coverage/ --fail-on-warning + @XDEBUG_MODE=coverage php ./vendor/bin/phpunit --testsuite=unit --coverage-clover=./build/reports/phpunit/clover.xml --log-junit=./build/reports/phpunit/unit.xml --coverage-php=./build/reports/phpunit/unit.cov --coverage-html=./build/reports/coverage/ --fail-on-warning php/test: php/tests diff --git a/README.md b/README.md index d7febca..a326192 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Component Http - PSR-15 implementation [![Current version](https://img.shields.io/packagist/v/eureka/component-http.svg?logo=composer)](https://packagist.org/packages/eureka/component-http) -[![Supported PHP version](https://img.shields.io/static/v1?logo=php&label=PHP&message=7.4%20-%208.4&color=777bb4)](https://packagist.org/packages/eureka/component-http) +[![Supported PHP version](https://img.shields.io/static/v1?logo=php&label=PHP&message=>%3D8.3&color=777bb4)](https://packagist.org/packages/eureka/component-http) ![CI](https://github.com/eureka-framework/component-http/workflows/CI/badge.svg) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=eureka-framework_component-http&metric=alert_status)](https://sonarcloud.io/dashboard?id=eureka-framework_component-http) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=eureka-framework_component-http&metric=coverage)](https://sonarcloud.io/dashboard?id=eureka-framework_component-http) diff --git a/ci/phpmax-compatibility.neon b/ci/phpmax-compatibility.neon index 31e7b6b..162415e 100644 --- a/ci/phpmax-compatibility.neon +++ b/ci/phpmax-compatibility.neon @@ -3,7 +3,7 @@ includes: - ./../vendor/phpstan/phpstan-phpunit/rules.neon parameters: - phpVersion: 80400 + phpVersion: 80500 level: 0 paths: - ./../src diff --git a/ci/phpmin-compatibility.neon b/ci/phpmin-compatibility.neon index 1082da1..729fb51 100644 --- a/ci/phpmin-compatibility.neon +++ b/ci/phpmin-compatibility.neon @@ -3,7 +3,7 @@ includes: - ./../vendor/phpstan/phpstan-phpunit/rules.neon parameters: - phpVersion: 70400 + phpVersion: 80300 level: 0 paths: - ./../src diff --git a/composer.json b/composer.json index 3c0475f..69662d5 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ }, "require": { - "php": "7.4.*||8.0.*||8.1.*||8.2.*||8.3.*||8.4.*", + "php": ">=8.3", "psr/http-message": "^1.0||^2.0", "psr/http-server-handler": "^1.0", @@ -37,12 +37,12 @@ }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.75.0", - "phpstan/phpstan": "^2.1.16", - "phpstan/phpstan-phpunit": "^2.0.6", - "phpstan/phpstan-strict-rules": "^2.0.4", - "phpunit/phpcov": "^8.2.1", - "phpunit/phpunit": "^9.6.23", - "shipmonk/composer-dependency-analyser": "^1.8.3" + "friendsofphp/php-cs-fixer": "^3.92.4", + "phpstan/phpstan": "^2.1.33", + "phpstan/phpstan-phpunit": "^2.0.11", + "phpstan/phpstan-strict-rules": "^2.0.7", + "phpunit/phpcov": "^11.0.3", + "phpunit/phpunit": "^12.5.4", + "shipmonk/composer-dependency-analyser": "^1.8.4" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f3d5e4c..112efca 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -6,7 +6,7 @@ includes: parameters: #~ Global conf - phpVersion: 70400 + phpVersion: 80300 level: max paths: - ./src @@ -23,6 +23,3 @@ parameters: - message: '`Parameter #2 \$middleware of class (.+?)RequestHandler constructor expects array.+`' path: ./tests/unit/RequestHandlerTest.php - - - message: '`Call to static method PHPUnit\\Framework\\Assert::assertInstanceOf.+ will always evaluate to true`' - path: ./tests/unit/ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4ba5312..0f4fcc5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,24 +1,12 @@ - - - ./src - - - - - - ./tests/unit - - - + diff --git a/src/Server/RequestHandler.php b/src/Server/RequestHandler.php index 84d3cf9..4c2b6c9 100644 --- a/src/Server/RequestHandler.php +++ b/src/Server/RequestHandler.php @@ -39,7 +39,7 @@ public function __construct(ResponseInterface $response, array $middleware = []) $this->storage = new \SplObjectStorage(); foreach ($middleware as $item) { - $this->storage->attach($item); + $this->storage->offsetSet($item); } $this->storage->rewind(); @@ -69,7 +69,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface public function withMiddleware(MiddlewareInterface $middleware): self { $handler = clone $this; - $handler->storage->detach($middleware); + $handler->storage->offsetSet($middleware); return $handler; } @@ -81,7 +81,7 @@ public function withMiddleware(MiddlewareInterface $middleware): self protected function withoutMiddleware(MiddlewareInterface $middleware): self { $handler = clone $this; - $handler->storage->detach($middleware); + $handler->storage->offsetUnset($middleware); $handler->storage->rewind(); return $handler; diff --git a/tests/unit/RequestHandlerTest.php b/tests/unit/RequestHandlerTest.php index e8945b5..b114383 100644 --- a/tests/unit/RequestHandlerTest.php +++ b/tests/unit/RequestHandlerTest.php @@ -13,7 +13,7 @@ use Eureka\Component\Http\Server\RequestHandler; use PHPUnit\Framework\MockObject\Exception; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -27,26 +27,14 @@ */ class RequestHandlerTest extends TestCase { - /** - * @return void - *@throws Exception -*/ - public function testCanInstantiateRequestHandler(): void - { - $responseMock = $this->createMock(ResponseInterface::class); - $requestHandler = new RequestHandler($responseMock); - - self::assertInstanceOf(RequestHandlerInterface::class, $requestHandler); - } - /** * @return void * @throws Exception */ public function testCanHandleRequest(): void { - $responseMock = $this->createMock(ResponseInterface::class); - $serverRequestMock = $this->createMock(ServerRequestInterface::class); + $responseStub = self::createStub(ResponseInterface::class); + $serverRequestStub = self::createStub(ServerRequestInterface::class); $middlewareMock = new class implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { @@ -54,13 +42,11 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } }; - $requestHandler = new RequestHandler($responseMock, [clone $middlewareMock]); - $requestHandler = $requestHandler->withMiddleware(clone $middlewareMock); + $requestHandler = new RequestHandler($responseStub, [clone $middlewareMock]); - /** @var ServerRequestInterface&MockObject $serverRequestMock */ - $response = $requestHandler->handle($serverRequestMock); + $response = $requestHandler->handle($serverRequestStub); - self::assertEquals($responseMock, $response); + self::assertEquals($responseStub, $response); } /** @@ -69,8 +55,8 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface */ public function testCanHandleRequestWithoutErrorWithNonMiddlewareInstance(): void { - $responseMock = $this->createMock(ResponseInterface::class); - $serverRequestMock = $this->createMock(ServerRequestInterface::class); + $responseStub = self::createStub(ResponseInterface::class); + $serverRequestStub = self::createStub(ServerRequestInterface::class); $middlewareMock = new class implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { @@ -84,11 +70,36 @@ public function process(ServerRequestInterface $serverRequest, RequestHandlerInt } }; - $requestHandler = new RequestHandler($responseMock, [clone $middlewareMock, clone $nonMiddlewareMock]); + $requestHandler = new RequestHandler($responseStub, [clone $middlewareMock, clone $nonMiddlewareMock]); + + $response = $requestHandler->handle($serverRequestStub); + + self::assertEquals($responseStub, $response); + } + + /** + * @return void + * @throws Exception + */ + public function testCanHandleRequestWithGivenMiddleware(): void + { + $responseStub = self::createStub(ResponseInterface::class); + $serverRequestStub = self::createStub(ServerRequestInterface::class); + $middlewareMock = new class implements MiddlewareInterface { + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + /** @var Stub&ResponseInterface $responseStub */ + $responseStub = $handler->handle($request); + $responseStub->method('getStatusCode')->willReturn(200); + return $responseStub; + } + }; + + $requestHandler = new RequestHandler($responseStub); + $requestHandler = $requestHandler->withMiddleware(clone $middlewareMock); - /** @var ServerRequestInterface&MockObject $serverRequestMock */ - $response = $requestHandler->handle($serverRequestMock); + $response = $requestHandler->handle($serverRequestStub); - self::assertEquals($responseMock, $response); + self::assertSame(200, $response->getStatusCode()); } }