diff --git a/src/foundation/src/Http/Contracts/MiddlewareContract.php b/src/foundation/src/Http/Contracts/MiddlewareContract.php index b002d28e..f6bba847 100644 --- a/src/foundation/src/Http/Contracts/MiddlewareContract.php +++ b/src/foundation/src/Http/Contracts/MiddlewareContract.php @@ -59,6 +59,20 @@ public function prependToMiddlewarePriority(string $middleware): static; */ public function appendToMiddlewarePriority(string $middleware): static; + /** + * Add the given middleware to the middleware priority list before other middleware. + * + * @param array|string $before + */ + public function addToMiddlewarePriorityBefore(string|array $before, string $middleware): static; + + /** + * Add the given middleware to the middleware priority list after other middleware. + * + * @param array|string $after + */ + public function addToMiddlewarePriorityAfter(string|array $after, string $middleware): static; + /** * Get the priority-sorted list of middleware. */ diff --git a/src/foundation/src/Http/Traits/HasMiddleware.php b/src/foundation/src/Http/Traits/HasMiddleware.php index d2b1d368..1364bfa0 100644 --- a/src/foundation/src/Http/Traits/HasMiddleware.php +++ b/src/foundation/src/Http/Traits/HasMiddleware.php @@ -204,7 +204,7 @@ public function prependMiddleware(string $middleware): static array_unshift($this->middleware, $middleware); } - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -218,7 +218,7 @@ public function pushMiddleware(string $middleware): static $this->middleware[] = $middleware; } - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -238,7 +238,7 @@ public function prependMiddlewareToGroup(string $group, string $middleware): sta array_unshift($this->middlewareGroups[$group], $middleware); } - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -258,7 +258,7 @@ public function appendMiddlewareToGroup(string $group, string $middleware): stat $this->middlewareGroups[$group][] = $middleware; } - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -272,7 +272,7 @@ public function prependToMiddlewarePriority(string $middleware): static array_unshift($this->middlewarePriority, $middleware); } - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -286,7 +286,63 @@ public function appendToMiddlewarePriority(string $middleware): static $this->middlewarePriority[] = $middleware; } - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; + + return $this; + } + + /** + * Add the given middleware to the middleware priority list before other middleware. + * + * @param array|string $before + */ + public function addToMiddlewarePriorityBefore(string|array $before, string $middleware): static + { + return $this->addToMiddlewarePriorityRelative($before, $middleware, after: false); + } + + /** + * Add the given middleware to the middleware priority list after other middleware. + * + * @param array|string $after + */ + public function addToMiddlewarePriorityAfter(string|array $after, string $middleware): static + { + return $this->addToMiddlewarePriorityRelative($after, $middleware, after: true); + } + + /** + * Add the given middleware to the middleware priority list relative to other middleware. + * + * @param array|string $existing + */ + protected function addToMiddlewarePriorityRelative(string|array $existing, string $middleware, bool $after = true): static + { + if (! in_array($middleware, $this->middlewarePriority)) { + $index = $after ? 0 : count($this->middlewarePriority); + + foreach ((array) $existing as $existingMiddleware) { + if (in_array($existingMiddleware, $this->middlewarePriority)) { + $middlewareIndex = array_search($existingMiddleware, $this->middlewarePriority); + + if ($after && $middlewareIndex > $index) { + $index = $middlewareIndex + 1; + } elseif ($after === false && $middlewareIndex < $index) { + $index = $middlewareIndex; + } + } + } + + if ($index === 0 && $after === false) { + array_unshift($this->middlewarePriority, $middleware); + } elseif (($after && $index === 0) || $index === count($this->middlewarePriority)) { + $this->middlewarePriority[] = $middleware; + } else { + array_splice($this->middlewarePriority, $index, 0, $middleware); + } + } + + $this->cachedMiddleware = []; return $this; } @@ -314,7 +370,7 @@ public function setGlobalMiddleware(array $middleware): static { $this->middleware = $middleware; - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -334,7 +390,7 @@ public function setMiddlewareGroups(array $groups): static { $this->middlewareGroups = $groups; - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -350,7 +406,7 @@ public function addMiddlewareGroup(string $group, array $middleware): static $this->middlewareGroups[$group] = $middleware; - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -370,7 +426,7 @@ public function setMiddlewareAliases(array $aliases): static { $this->middlewareAliases = $aliases; - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } @@ -382,7 +438,7 @@ public function setMiddlewarePriority(array $priority): static { $this->middlewarePriority = $priority; - $this->cachedMiddleware[] = []; + $this->cachedMiddleware = []; return $this; } diff --git a/tests/Foundation/Http/KernelTest.php b/tests/Foundation/Http/KernelTest.php index 28cadedc..9409e289 100644 --- a/tests/Foundation/Http/KernelTest.php +++ b/tests/Foundation/Http/KernelTest.php @@ -69,6 +69,205 @@ public function testMiddleware() ], array_map(fn (ParsedMiddleware $middleware) => $middleware->getSignature(), $result)); } + public function testAddToMiddlewarePriorityAfterWithSingleMiddleware(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + ]); + + $kernel->addToMiddlewarePriorityAfter('middleware_b', 'new_middleware'); + + $this->assertSame([ + 'middleware_a', + 'middleware_b', + 'new_middleware', + 'middleware_c', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityAfterWithArrayOfMiddleware(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + ]); + + // When array is given, it inserts after the LAST found middleware in the array + $kernel->addToMiddlewarePriorityAfter(['middleware_a', 'middleware_c'], 'new_middleware'); + + $this->assertSame([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + 'new_middleware', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityAfterWhenExistingNotFound(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + ]); + + // When target middleware not found, should append to end + $kernel->addToMiddlewarePriorityAfter('non_existent', 'new_middleware'); + + $this->assertSame([ + 'middleware_a', + 'middleware_b', + 'new_middleware', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityAfterDoesNotAddDuplicates(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + ]); + + $kernel->addToMiddlewarePriorityAfter('middleware_a', 'middleware_b'); + + // middleware_b already exists, should not be added again + $this->assertSame([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityBeforeWithSingleMiddleware(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + ]); + + $kernel->addToMiddlewarePriorityBefore('middleware_b', 'new_middleware'); + + $this->assertSame([ + 'middleware_a', + 'new_middleware', + 'middleware_b', + 'middleware_c', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityBeforeWithArrayOfMiddleware(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + ]); + + // When array is given, it inserts before the FIRST found middleware in the array + $kernel->addToMiddlewarePriorityBefore(['middleware_b', 'middleware_c'], 'new_middleware'); + + $this->assertSame([ + 'middleware_a', + 'new_middleware', + 'middleware_b', + 'middleware_c', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityBeforeWhenExistingNotFound(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + ]); + + // When target middleware not found, appends to end (same as After behavior) + // This matches Laravel's behavior - if target doesn't exist, append is the safe fallback + $kernel->addToMiddlewarePriorityBefore('non_existent', 'new_middleware'); + + $this->assertSame([ + 'middleware_a', + 'middleware_b', + 'new_middleware', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityBeforeDoesNotAddDuplicates(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + ]); + + $kernel->addToMiddlewarePriorityBefore('middleware_c', 'middleware_a'); + + // middleware_a already exists, should not be added again + $this->assertSame([ + 'middleware_a', + 'middleware_b', + 'middleware_c', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityBeforeAtBeginning(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + ]); + + $kernel->addToMiddlewarePriorityBefore('middleware_a', 'new_middleware'); + + $this->assertSame([ + 'new_middleware', + 'middleware_a', + 'middleware_b', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityAfterAtEnd(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority([ + 'middleware_a', + 'middleware_b', + ]); + + $kernel->addToMiddlewarePriorityAfter('middleware_b', 'new_middleware'); + + $this->assertSame([ + 'middleware_a', + 'middleware_b', + 'new_middleware', + ], $kernel->getMiddlewarePriority()); + } + + public function testAddToMiddlewarePriorityReturnsSelf(): void + { + $kernel = $this->getKernel(); + $kernel->setMiddlewarePriority(['middleware_a']); + + $result = $kernel->addToMiddlewarePriorityAfter('middleware_a', 'new_middleware'); + $this->assertSame($kernel, $result); + + $result = $kernel->addToMiddlewarePriorityBefore('middleware_a', 'another_middleware'); + $this->assertSame($kernel, $result); + } + protected function getKernel(): Kernel { return new Kernel(