Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/foundation/src/Http/Contracts/MiddlewareContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, string>|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<int, string>|string $after
*/
public function addToMiddlewarePriorityAfter(string|array $after, string $middleware): static;

/**
* Get the priority-sorted list of middleware.
*/
Expand Down
78 changes: 67 additions & 11 deletions src/foundation/src/Http/Traits/HasMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ public function prependMiddleware(string $middleware): static
array_unshift($this->middleware, $middleware);
}

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -218,7 +218,7 @@ public function pushMiddleware(string $middleware): static
$this->middleware[] = $middleware;
}

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -238,7 +238,7 @@ public function prependMiddlewareToGroup(string $group, string $middleware): sta
array_unshift($this->middlewareGroups[$group], $middleware);
}

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -258,7 +258,7 @@ public function appendMiddlewareToGroup(string $group, string $middleware): stat
$this->middlewareGroups[$group][] = $middleware;
}

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -272,7 +272,7 @@ public function prependToMiddlewarePriority(string $middleware): static
array_unshift($this->middlewarePriority, $middleware);
}

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -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<int, string>|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<int, string>|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<int, string>|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)) {
Comment on lines +321 to +325
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using strict comparison (third parameter true) in in_array for better type safety. This ensures type-safe comparisons when checking if middleware exists in the priority list.

Suggested change
if (! in_array($middleware, $this->middlewarePriority)) {
$index = $after ? 0 : count($this->middlewarePriority);
foreach ((array) $existing as $existingMiddleware) {
if (in_array($existingMiddleware, $this->middlewarePriority)) {
if (! in_array($middleware, $this->middlewarePriority, true)) {
$index = $after ? 0 : count($this->middlewarePriority);
foreach ((array) $existing as $existingMiddleware) {
if (in_array($existingMiddleware, $this->middlewarePriority, true)) {

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@albertcht Same as above - Laravel's implementation doesn't use strict comparison for these in_array calls, and neither do the existing calls in this file (lines 195, 271, 285). The $middleware param is typed string and $middlewarePriority is string[], so there's no type coercion risk. If strict comparison is important, I think it should be a separate PR that updates all the calls consistently instead of just these new methods.

Comment on lines +321 to +325
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using strict comparison (third parameter true) in in_array for better type safety. This ensures type-safe comparisons when checking if middleware already exists in the priority list before adding it.

Suggested change
if (! in_array($middleware, $this->middlewarePriority)) {
$index = $after ? 0 : count($this->middlewarePriority);
foreach ((array) $existing as $existingMiddleware) {
if (in_array($existingMiddleware, $this->middlewarePriority)) {
if (! in_array($middleware, $this->middlewarePriority, true)) {
$index = $after ? 0 : count($this->middlewarePriority);
foreach ((array) $existing as $existingMiddleware) {
if (in_array($existingMiddleware, $this->middlewarePriority, true)) {

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@albertcht This seems to be a duplicate of the last suggestion

$middlewareIndex = array_search($existingMiddleware, $this->middlewarePriority);
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using strict comparison (third parameter true) in array_search for better type safety. This ensures that the search uses strict type checking (===) rather than loose comparison (==), which is particularly important for string-based middleware class names that could be confused with numeric indices.

Suggested change
$middlewareIndex = array_search($existingMiddleware, $this->middlewarePriority);
$middlewareIndex = array_search($existingMiddleware, $this->middlewarePriority, true);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@albertcht Laravel's implementation doesn't use strict comparison here either - their addToMiddlewarePriorityRelative uses plain array_search(). The type safety issue Copilot raises (string class names confused with numeric indices) isn't applicable since both $existingMiddleware and the array values are strings. The existing array_search calls in this file (lines 156, 203, 217, 237, 257) also don't use strict comparison, so adding it here would be inconsistent.


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;
}
Expand Down Expand Up @@ -314,7 +370,7 @@ public function setGlobalMiddleware(array $middleware): static
{
$this->middleware = $middleware;

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -334,7 +390,7 @@ public function setMiddlewareGroups(array $groups): static
{
$this->middlewareGroups = $groups;

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -350,7 +406,7 @@ public function addMiddlewareGroup(string $group, array $middleware): static

$this->middlewareGroups[$group] = $middleware;

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -370,7 +426,7 @@ public function setMiddlewareAliases(array $aliases): static
{
$this->middlewareAliases = $aliases;

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand All @@ -382,7 +438,7 @@ public function setMiddlewarePriority(array $priority): static
{
$this->middlewarePriority = $priority;

$this->cachedMiddleware[] = [];
$this->cachedMiddleware = [];

return $this;
}
Expand Down
199 changes: 199 additions & 0 deletions tests/Foundation/Http/KernelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down