Skip to content

[13.x] Fix Arr::forget() removing the wrong element - #61023

Merged
taylorotwell merged 2 commits into
laravel:13.xfrom
AlessioGiacobbe:fix/arr-forget-stale-nested-reference
Aug 4, 2026
Merged

[13.x] Fix Arr::forget() removing the wrong element#61023
taylorotwell merged 2 commits into
laravel:13.xfrom
AlessioGiacobbe:fix/arr-forget-stale-nested-reference

Conversation

@AlessioGiacobbe

Copy link
Copy Markdown
Contributor

Arr::forget() resets its working reference back to the top-level array ($array = &$original) only after the shortcut that unsets an exact top-level key. When a "dot" key is processed, the reference is left pointing at a nested array, so the next key in the list is resolved against that nested array instead of the top level.

$array = ['users' => ['name' => 'Joe', 'id' => 1], 'id' => 99];

Arr::forget($array, ['users.name', 'id']);

// before: ['users' => [], 'id' => 99]   <- removed users.id, kept the top-level id
// after:  ['users' => ['id' => 1]]

The wrong element is removed and the requested one is kept, silently. Two dotted keys in a row are affected the same way:

$array = ['a' => ['b' => ['c' => 1, 'd' => 2]], 'e' => ['d' => 3]];

Arr::forget($array, ['a.b.c', 'e.d']);

// before: ['a' => ['b' => ['d' => 2]], 'e' => ['d' => 3]]  <- e.d never removed
// after:  ['a' => ['b' => ['d' => 2]], 'e' => []]

Moving the reset to the top of the loop makes every key resolve from the top-level array, which is what the existing // clean up before each pass comment intends.

This also affects everything that delegates here: Arr::except(), Collection::except(), data_forget() and Uri::withoutQuery().

Tests covering both orderings are added to the existing testForget().

AlessioGiacobbe and others added 2 commits August 4, 2026 14:53
`Arr::forget()` resets its working reference back to the top-level array
(`$array = &$original`) only *after* the shortcut that unsets an exact
top-level key. When a "dot" key is processed, the reference is left
pointing at a nested array, so the next key in the list is resolved
against that nested array instead of the top level.

    $array = ['users' => ['name' => 'Joe', 'id' => 1], 'id' => 99];

    Arr::forget($array, ['users.name', 'id']);

    // before: ['users' => [], 'id' => 99]   <- removed users.id
    // after:  ['users' => ['id' => 1]]

Moving the reset to the top of the loop makes every key resolve from the
top-level array, as intended. This also affects `Arr::except()`,
`Collection::except()`, `data_forget()` and `Uri::withoutQuery()`, which
all delegate here.
@taylorotwell
taylorotwell merged commit 6f97f81 into laravel:13.x Aug 4, 2026
41 of 53 checks passed
@AlessioGiacobbe
AlessioGiacobbe deleted the fix/arr-forget-stale-nested-reference branch August 4, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants