From 3ec4d1291fa21bc55983719acfac0f471e8c2e9c Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Sun, 9 Aug 2026 19:45:43 +0100 Subject: [PATCH 1/2] fix: persist POST data on redirects for #733 --- src/Dispatch/PathNormaliser.php | 11 +++++++++-- test/phpunit/Dispatch/DispatcherTest.php | 2 +- test/phpunit/Dispatch/PathNormaliserTest.php | 11 +++++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Dispatch/PathNormaliser.php b/src/Dispatch/PathNormaliser.php index 4c9d90cd..404620b3 100644 --- a/src/Dispatch/PathNormaliser.php +++ b/src/Dispatch/PathNormaliser.php @@ -2,6 +2,7 @@ namespace GT\WebEngine\Dispatch; use Closure; +use GT\Http\StatusCode; use Psr\Http\Message\UriInterface; class PathNormaliser { @@ -14,12 +15,18 @@ public function normaliseTrailingSlash( if($forceTrailingSlash) { if(!str_ends_with($path, "/")) { - $redirect($uri->withPath("$path/")); + $redirect( + $uri->withPath("$path/"), + StatusCode::PERMANENT_REDIRECT, + ); } } else { if(str_ends_with($path, "/") && $path !== "/") { - $redirect($uri->withPath(rtrim($path, "/"))); + $redirect( + $uri->withPath(rtrim($path, "/")), + StatusCode::PERMANENT_REDIRECT, + ); } } } diff --git a/test/phpunit/Dispatch/DispatcherTest.php b/test/phpunit/Dispatch/DispatcherTest.php index 1e2d8dca..87e130b9 100644 --- a/test/phpunit/Dispatch/DispatcherTest.php +++ b/test/phpunit/Dispatch/DispatcherTest.php @@ -82,7 +82,7 @@ function(Response $response)use(&$finishedResponse):void { $response = $sut->generateResponse(); - self::assertSame(303, $response->getStatusCode()); + self::assertSame(StatusCode::PERMANENT_REDIRECT, $response->getStatusCode()); self::assertSame("https://example.test/redirect-me/", $response->getHeaderLine("Location")); self::assertSame($response, $finishedResponse); self::assertNull($sut->getSessionInit()); diff --git a/test/phpunit/Dispatch/PathNormaliserTest.php b/test/phpunit/Dispatch/PathNormaliserTest.php index 0b4e6685..bf24ad71 100644 --- a/test/phpunit/Dispatch/PathNormaliserTest.php +++ b/test/phpunit/Dispatch/PathNormaliserTest.php @@ -2,6 +2,7 @@ namespace GT\WebEngine\Test\Dispatch; use GT\WebEngine\Dispatch\PathNormaliser; +use GT\Http\StatusCode; use GT\Http\Uri; use PHPUnit\Framework\TestCase; @@ -11,14 +12,17 @@ public function testForceTrailingSlash_addsWhenMissing():void { $uri = new Uri("https://example.test/section"); $called = false; $redirected = null; + $statusCode = null; - $sut->normaliseTrailingSlash($uri, true, function(Uri $redirectUri) use (&$called, &$redirected) { + $sut->normaliseTrailingSlash($uri, true, function(Uri $redirectUri, int $redirectStatusCode) use (&$called, &$redirected, &$statusCode) { $called = true; $redirected = $redirectUri; + $statusCode = $redirectStatusCode; }); self::assertTrue($called, "Expected redirect when missing trailing slash"); self::assertSame("/section/", $redirected->getPath()); + self::assertSame(StatusCode::PERMANENT_REDIRECT, $statusCode); } public function testForceTrailingSlash_noopWhenAlreadyPresent():void { @@ -66,12 +70,15 @@ public function testRemoveTrailingSlash_removesWhenPresent():void { $uri = new Uri("https://example.test/section/"); $called = false; $redirected = null; - $sut->normaliseTrailingSlash($uri, false, function(Uri $u) use (&$called, &$redirected) { + $statusCode = null; + $sut->normaliseTrailingSlash($uri, false, function(Uri $u, int $redirectStatusCode) use (&$called, &$redirected, &$statusCode) { $called = true; $redirected = $u; + $statusCode = $redirectStatusCode; }); self::assertTrue($called); self::assertSame("/section", $redirected->getPath()); + self::assertSame(StatusCode::PERMANENT_REDIRECT, $statusCode); } } From 849bbc8f511b0e06cc22312512cc9cf212e28eeb Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Sun, 9 Aug 2026 19:48:28 +0100 Subject: [PATCH 2/2] fix: ensure nested logic does not execute closes #733 --- router.default.php | 3 +- test/phpunit/DefaultRouterTest.php | 46 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/router.default.php b/router.default.php index 0d76d33d..da6f4e9c 100644 --- a/router.default.php +++ b/router.default.php @@ -183,7 +183,8 @@ private function assertNoAmbiguousPath( public function pathMatcherFilter(PathMatcher $pathMatcher):void { $pathMatcher->addFilter(function(string $filePath, string $uriPath, string $baseDir):bool { - foreach(glob($baseDir . $uriPath . ".*") as $globMatch) { + $staticUriPath = rtrim($uriPath, "/"); + foreach(glob($baseDir . $staticUriPath . ".*") as $globMatch) { $URI_CONTAINER = pathinfo($uriPath, PATHINFO_DIRNAME); $TRIM_THIS = $baseDir . $URI_CONTAINER; if(str_starts_with($globMatch, $TRIM_THIS)) { diff --git a/test/phpunit/DefaultRouterTest.php b/test/phpunit/DefaultRouterTest.php index 01ee5d3b..14cbd21d 100644 --- a/test/phpunit/DefaultRouterTest.php +++ b/test/phpunit/DefaultRouterTest.php @@ -271,6 +271,52 @@ public function testRoute_pageRequest_ignoresDynamicFilesWhenConcreteFileExists( self::assertSame(["page/article/read.html"], iterator_to_array($sut->getViewAssembly())); } + public function testRoute_pageRequestWithTrailingSlash_ignoresDynamicCommonLogicWhenConcretePageExists():void { + mkdir($this->tmpDir . "/page/shop/@category", recursive: true); + file_put_contents( + $this->tmpDir . "/page/shop/@category/_common.php", + "tmpDir . "/page/shop/@category/index.php", + "tmpDir . "/page/shop/static-product-page.html", + "
static product
", + ); + file_put_contents( + $this->tmpDir . "/page/shop/static-product-page.php", + "tmpDir); + + $request = self::createMock(Request::class); + $request->method("getMethod")->willReturn("GET"); + $request->method("getHeaderLine") + ->with("accept") + ->willReturn("text/html"); + $request->method("getUri")->willReturn( + new Uri("https://example.test/shop/static-product-page/"), + ); + + $sut = new DefaultRouter(new RouterConfig(307, "text/html")); + $container = new Container(); + $container->set($request); + $sut->setContainer($container); + $sut->route($request); + + self::assertSame( + ["page/shop/static-product-page.php"], + iterator_to_array($sut->getLogicAssembly()), + ); + self::assertSame( + ["page/shop/static-product-page.html"], + iterator_to_array($sut->getViewAssembly()), + ); + } + private function removeDirectory(string $dir):void { if(!is_dir($dir)) { return;