Skip to content

[13.x] Fix default handling in CookieJar::queued() - #60904

Merged
taylorotwell merged 1 commit into
laravel:13.xfrom
alies-dev:fix/cookie-jar-queued-default
Jul 27, 2026
Merged

[13.x] Fix default handling in CookieJar::queued()#60904
taylorotwell merged 1 commit into
laravel:13.xfrom
alies-dev:fix/cookie-jar-queued-default

Conversation

@alies-dev

@alies-dev alies-dev commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

CookieJar::queued() uses $default for two different jobs. It is the fallback for the Arr::get() lookup, and then whatever that lookup returned is treated as the queue bucket:

$queued = Arr::get($this->queued, $key, $default); // fallback return value

if ($path === null) {
    return Arr::last($queued, null, $default);     // now treated as array<path, Cookie>
}

return Arr::get($queued, $path, $default);

$this->queued is array<name, array<path, Cookie>>, so when the name is not queued the caller's default goes straight into Arr::last() as if it were a bucket of cookies. On 13.x today, with #60887 already applied:

$jar->queued('missing', new Cookie('fallback', 'x')); // bool(false)
$jar->queued('missing', 'fallback');                  // InvalidArgumentException
$jar->queued('missing', fn () => 'fallback');         // InvalidArgumentException

The bool(false) is Cookie::$partitioned, the last property left after Arr::from() casts the object with (array). The scalar and Closure cases still throw Items cannot be represented by a scalar value., because #60887 guarded Arr::last() against null only and Arr::from() rejects every other scalar. A Closure default is also resolved twice when $path is passed, since both Arr::get() calls run value() on it.

Only a null default ever worked, which is why nobody hit this. hasQueued() passes null, and so does every first-party caller. That case broke too in v13.22.0 and #60887 fixed it, but every other default is still broken on 13.x today. This PR fixes the queued() bug underneath, so CookieJar no longer depends on that guard.

Both lookups also go through Arr::get(), which falls back to segment traversal when the exact key is missing. Cookie names and paths are opaque strings, not key paths:

$jar->queue($jar->make('foo', 'x', 0, 'bar')); // name "foo", path "bar"

$jar->queued('foo.bar');    // the cookie queued as "foo", not null
$jar->hasQueued('foo.bar'); // true

That is also how $queued can hold a Cookie instead of a bucket and reach Arr::last() as a non-array. Both levels now read literal keys, and $default is resolved once, in one place.

A cookie whose name really does contain a dot is unaffected. Arr::get() checks the exact key before it splits on dots, so queued('foo.bar') already returned the cookie queued under the literal name foo.bar and still does. Only the accidental traversal fallback goes away, and testQueuedFindsCookieNamesContainingDots locks that in.

Docblocks: @return becomes Cookie|TQueuedDefault, since the method has always returned whatever $default was while documenting Cookie|null. @var on $queued becomes array<string, array<string, Cookie>>; the current Cookie[] describes a flat shape the property has not had since paths were added.

Compatibility

Nothing that previously returned a usable value changes. With a null default every branch returns exactly what it returned before. Any other default previously produced a wrong value or threw.

@alies-dev
alies-dev marked this pull request as draft July 27, 2026 13:31
@alies-dev
alies-dev force-pushed the fix/cookie-jar-queued-default branch from ed59c99 to ef96253 Compare July 27, 2026 13:36
@alies-dev
alies-dev marked this pull request as ready for review July 27, 2026 13:43
queued() passed $default as the fallback for the Arr::get() lookup and then
re-processed that same value as if it were the array<path, Cookie> bucket, so
a caller supplied default was fed back into Arr::last()/Arr::get(). Returning
a Cookie default gave back bool(false), returning a scalar default threw a
TypeError, and a Closure default was resolved twice.

Cookie names and paths are now looked up as literal array keys. Passing them
through Arr::get() dot notation meant a name such as "foo.bar" resolved to the
cookie queued for the name "foo" at the path "bar", which also let $queued
hold a Cookie instead of a bucket and reach Arr::last() as a non array.
@alies-dev
alies-dev force-pushed the fix/cookie-jar-queued-default branch from ef96253 to 3857c29 Compare July 27, 2026 13:51
@alies-dev alies-dev changed the title [13.x] Fix default handling in CookieJar::queued() [13.x] Fix default handling in CookieJar::queued() Jul 27, 2026
@taylorotwell
taylorotwell merged commit c6770c9 into laravel:13.x Jul 27, 2026
53 checks passed
@alies-dev
alies-dev deleted the fix/cookie-jar-queued-default branch July 27, 2026 15:10
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