diff --git a/composer.json b/composer.json index 94fb391..7840b86 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "phpnomad/wordpress-integration", "description": "", - "version": "4.3.2", + "version": "4.3.3", "type": "library", "homepage": "https://github.com/phpnomad/core", "readme": "README.md", diff --git a/lib/Strategies/RestStrategy.php b/lib/Strategies/RestStrategy.php index f021924..249da7a 100644 --- a/lib/Strategies/RestStrategy.php +++ b/lib/Strategies/RestStrategy.php @@ -98,6 +98,18 @@ private function checkPermissions(Controller $controller, WP_REST_Request $wpReq return true; } + // WordPress invokes EVERY method's permission callback on the + // matched route while rest_send_allow_header builds the Allow + // header — including methods that are not being dispatched. Running + // another verb's middleware against a request shaped for this verb + // produces spurious validation failures, null-param warnings, and + // pointless queries. Only the dispatching method's callback can + // run, so only its middleware outcome matters; advertise the + // method and let a real request of that verb run its own chain. + if (strtoupper($wpRequest->get_method()) !== strtoupper($controller->getMethod())) { + return true; + } + $outcomes = $this->middlewareOutcomes[$wpRequest] ?? []; $controllerClass = get_class($controller); diff --git a/tests/Unit/Strategies/RestStrategyPermissionsTest.php b/tests/Unit/Strategies/RestStrategyPermissionsTest.php index 32b8734..666fdb4 100644 --- a/tests/Unit/Strategies/RestStrategyPermissionsTest.php +++ b/tests/Unit/Strategies/RestStrategyPermissionsTest.php @@ -247,4 +247,28 @@ public function process(Request $request): void $this->assertInstanceOf(WP_Error::class, $result); } + + public function testMismatchedMethodControllerSkipsMiddlewareEntirely(): void + { + // rest_send_allow_header checks the POST controller's permission + // callback during a GET request; running its middleware against a + // GET-shaped request fires validations with null params. + $middleware = new class implements Middleware { + public int $runs = 0; + + public function process(Request $request): void + { + $this->runs++; + } + }; + + $controller = $this->makeControllerWithMiddleware($middleware); // getMethod() === 'GET' + $strategy = $this->makeStrategy(); + $wpRequest = new WP_REST_Request('POST'); + + $result = $this->checkPermissions($strategy, $controller, $wpRequest); + + $this->assertTrue($result); + $this->assertSame(0, $middleware->runs); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d8f6a96..5d1f96d 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -120,6 +120,18 @@ public function __construct( if (!class_exists('WP_REST_Request')) { class WP_REST_Request { + private string $method; + + public function __construct(string $method = 'GET') + { + $this->method = $method; + } + + public function get_method(): string + { + return $this->method; + } + public function get_params(): array { return [];