From a0dfc2d2280537e07420975c31c3f3ca61177221 Mon Sep 17 00:00:00 2001 From: Witold Wasiczko Date: Wed, 19 Aug 2026 19:00:28 +0200 Subject: [PATCH] Test on PHP 8.4/8.5 and run lowest deps on every PHP version Extend the CI matrix to PHP 8.0-8.5 with both highest and lowest dependency resolution, and drop the lowest-job excludes by pinning require-dev floors to releases that run on the whole range: - phpunit ^9.6.19, vfsstream ^1.6.12, var-dumper ^5.4.48 - laminas-diactoros ^2.18.1 || ^3.0 (2.x tops out at PHP 8.3) - slim/slim ^3 -> ^4.15.2: Slim 3 cannot cover the matrix (3.12.5 leaks deprecations into the output buffer on 8.1+ and makes App::run() throw, 3.13.0 requires PHP ^8.1), so the framework test now boots a Slim 4 App Slim 3 stays supported at runtime: extractPath() duck-types the Slim\Http\Uri::getBasePath() quirk via method_exists instead of instanceof, so PHPStan no longer needs the class, and the branch is covered by a new unit test with SlimUriStub. Verified in docker: lowest on 8.0/8.4/8.5, highest on 8.5, and PHPStan level 6 on 8.0 are all green. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QCFRdzoTxAMkVqfBK13Z4U --- .github/workflows/tests.yml | 13 +--- README.md | 12 +++- composer.json | 10 +-- src/PhpDebugBarMiddleware.php | 9 +-- test/PhpDebugBarMiddlewareTest.php | 23 ++++++ test/Slim3Test.php | 44 ------------ test/Slim4Test.php | 44 ++++++++++++ test/SlimUriStub.php | 112 +++++++++++++++++++++++++++++ 8 files changed, 202 insertions(+), 65 deletions(-) delete mode 100644 test/Slim3Test.php create mode 100644 test/Slim4Test.php create mode 100644 test/SlimUriStub.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ece2bc5..66d37f8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,17 +37,8 @@ jobs: - '8.1' - '8.2' - '8.3' - exclude: - # Lowest bounds of the dev dependencies (Slim 3.0, Pimple 3.0, - # vfsStream 1.6.8) were released before PHP 8.1 and fatal on load - # there. That is a limitation of those old releases, not of this - # library: every tested PHP version is covered by the highest jobs. - - dependencies: lowest - php-versions: '8.1' - - dependencies: lowest - php-versions: '8.2' - - dependencies: lowest - php-versions: '8.3' + - '8.4' + - '8.5' runs-on: ubuntu-latest steps: - name: Checkout diff --git a/README.md b/README.md index 63e4e7a..2eab93e 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,16 @@ $app->pipe(\PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware::class); For more - follow Mezzio [documentation](https://docs.mezzio.dev/mezzio/v3/features/modular-applications/). +### How to install on Slim 4? + +Register factories in a PSR-11 container of your choice, then add the middleware +resolved from the container: + +```php +$app = \Slim\Factory\AppFactory::create(); +$app->add($container->get(\PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware::class)); +``` + ### How to install on Slim 3? Register factories in container: @@ -115,6 +125,6 @@ return array_merge(PhpMiddleware\PhpDebugBar\ConfigProvider::getConfig(), $myOve Middleware tested on: * [Mezzio](https://github.com/mezzio/mezzio) -* [Slim 3.x](https://github.com/slimphp/Slim) +* [Slim 4.x](https://github.com/slimphp/Slim) And any other modern framework [supported PSR-17 middlewares and PSR-7](https://mwop.net/blog/2015-01-08-on-http-middleware-and-psr-7.html). diff --git a/composer.json b/composer.json index 974638e..ff2c31a 100644 --- a/composer.json +++ b/composer.json @@ -21,14 +21,14 @@ "psr/http-factory-implementation": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^9.1.4", - "mikey179/vfsstream": "^1.6.8", - "symfony/var-dumper": "^4.4.30", - "slim/slim": "^3.0", + "phpunit/phpunit": "^9.6.19", + "mikey179/vfsstream": "^1.6.12", + "symfony/var-dumper": "^5.4.48", + "slim/slim": "^4.15.2", "mezzio/mezzio": "^3.0", "mezzio/mezzio-fastroute": "^3.0.1", "laminas/laminas-servicemanager": "^3.3.2", - "laminas/laminas-diactoros": "^2.0", + "laminas/laminas-diactoros": "^2.18.1 || ^3.0", "phpstan/phpstan": "^1.4" }, "autoload": { diff --git a/src/PhpDebugBarMiddleware.php b/src/PhpDebugBarMiddleware.php index 82d1a0f..5f6c17b 100644 --- a/src/PhpDebugBarMiddleware.php +++ b/src/PhpDebugBarMiddleware.php @@ -12,7 +12,6 @@ use Psr\Http\Message\UriInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface as RequestHandler; -use Slim\Http\Uri as SlimUri; /** * @author Witold Wasiczko @@ -168,9 +167,11 @@ private function getStaticFile(UriInterface $uri): ?Response private function extractPath(UriInterface $uri): string { - // Slim3 compatibility - if ($uri instanceof SlimUri) { - $basePath = $uri->getBasePath(); + // Slim3 compatibility: Slim\Http\Uri is duck-typed so slim/slim is not + // needed at analysis time (dev dependencies ship Slim 4, which has no + // such class). + if (method_exists($uri, 'getBasePath')) { + $basePath = (string) $uri->getBasePath(); if (!empty($basePath)) { return $basePath; } diff --git a/test/PhpDebugBarMiddlewareTest.php b/test/PhpDebugBarMiddlewareTest.php index b9bc14b..d846ec2 100644 --- a/test/PhpDebugBarMiddlewareTest.php +++ b/test/PhpDebugBarMiddlewareTest.php @@ -338,6 +338,29 @@ public function testHandleStaticFile(string $extension, string $contentType): vo $this->assertSame('filecontent', (string) $result->getBody()); } + public function testHandleStaticFileUsingSlim3UriBasePath(): void + { + $root = vfsStream::setup('boo'); + + $this->debugbarRenderer->expects($this->any())->method('getBasePath')->willReturn(vfsStream::url('boo')); + + // Slim 3 serves the app through a front controller: the script itself is + // the base path and the URI path is only "/". + $uri = new SlimUriStub('/phpdebugbar/debugbar.js', '/'); + $request = new ServerRequest([], [], $uri, null, 'php://memory'); + $response = new Response\HtmlResponse(''); + + vfsStream::newFile('debugbar.js')->withContent('filecontent')->at($root); + + $requestHandler = new RequestHandlerStub($response); + + $result = $this->middleware->process($request, $requestHandler); + + $this->assertFalse($requestHandler->isCalled(), 'Request handler is called'); + $this->assertSame('text/javascript', $result->getHeaderLine('Content-type')); + $this->assertSame('filecontent', (string) $result->getBody()); + } + public function getContentTypes(): array { return [ diff --git a/test/Slim3Test.php b/test/Slim3Test.php deleted file mode 100644 index 9a7cefd..0000000 --- a/test/Slim3Test.php +++ /dev/null @@ -1,44 +0,0 @@ -getContainer(); - $container[ResponseFactoryInterface::class] = new ResponseFactory(); - $container[StreamFactoryInterface::class] = new StreamFactory(); - $container['environment'] = function() use ($server) { - return new Environment($server); - }; - - $config = ConfigProvider::getConfig(); - - foreach ($config['dependencies']['factories'] as $key => $factory) { - $container[$key] = new $factory(); - } - - $middleware = $container->get(PhpDebugBarMiddleware::class); - - $app->add($middleware); - - foreach ($pipe as $pattern => $middleware) { - $app->get($pattern, $middleware); - } - - return $app->run(true); - } -} diff --git a/test/Slim4Test.php b/test/Slim4Test.php new file mode 100644 index 0000000..6f7f9dd --- /dev/null +++ b/test/Slim4Test.php @@ -0,0 +1,44 @@ +setService(ResponseFactoryInterface::class, new ResponseFactory()); + $container->setService(StreamFactoryInterface::class, new StreamFactory()); + + $config = ConfigProvider::getConfig(); + + foreach ($config['dependencies']['factories'] as $name => $factory) { + $container->setFactory($name, $factory); + } + + $app = AppFactory::create(new ResponseFactory()); + + $app->add($container->get(PhpDebugBarMiddleware::class)); + + foreach ($pipe as $pattern => $handler) { + $app->get($pattern, function (ServerRequestInterface $request) use ($handler): ResponseInterface { + return $handler($request); + }); + } + + return $app->handle(ServerRequestFactory::fromGlobals($server)); + } +} diff --git a/test/SlimUriStub.php b/test/SlimUriStub.php new file mode 100644 index 0000000..3ceec51 --- /dev/null +++ b/test/SlimUriStub.php @@ -0,0 +1,112 @@ +basePath = $basePath; + $this->path = $path; + } + + public function getBasePath(): string + { + return $this->basePath; + } + + public function getScheme(): string + { + return 'http'; + } + + public function getAuthority(): string + { + return 'example.com'; + } + + public function getUserInfo(): string + { + return ''; + } + + public function getHost(): string + { + return 'example.com'; + } + + public function getPort(): ?int + { + return null; + } + + public function getPath(): string + { + return $this->path; + } + + public function getQuery(): string + { + return ''; + } + + public function getFragment(): string + { + return ''; + } + + public function withScheme($scheme): UriInterface + { + return $this; + } + + public function withUserInfo($user, $password = null): UriInterface + { + return $this; + } + + public function withHost($host): UriInterface + { + return $this; + } + + public function withPort($port): UriInterface + { + return $this; + } + + public function withPath($path): UriInterface + { + return $this; + } + + public function withQuery($query): UriInterface + { + return $this; + } + + public function withFragment($fragment): UriInterface + { + return $this; + } + + public function __toString(): string + { + return $this->basePath . $this->path; + } +}