-
Notifications
You must be signed in to change notification settings - Fork 11.7k
[Support] Fix Arr::array to return [] by default #57013
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
src/Illuminate/Collections/Arr.php
Outdated
| * Get an array item from an array using "dot" notation. | ||
| */ | ||
| public static function array(ArrayAccess|array $array, string|int|null $key, ?array $default = null): array | ||
| public static function array(ArrayAccess|array $array, string|int|null $key, ?array $default = []): array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if people who rely on this would want something like throwOnNotFound or the like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That’s a fair thought. My perspective is that since this method already has a strict : array return type, keeping it restrictive and throwing when the resolved value is not actually an array makes it safer and more predictable.
It guarantees that consumers of Arr::array() can confidently continue working with the result without additional type checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem with the return type per se. But throwing on non-array and throwing on non-existence doesn't need to conflict since there's the ItemNotFoundException that could be used for the latter case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point — I see the distinction. In my approach, I wanted missing keys to gracefully fall back to the provided default (or [] by default) to avoid forcing consumers into try/catch for common “not found” cases.
Using ItemNotFoundException for non-existence could work, but I feel it shifts the ergonomics — many developers just want a safe array back. The stricter exception path then remains focused on type mismatch, which keeps usage predictable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't either or—that's why I suggested a throwOnNotFound flag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about making a throwOnNotFound flag so we can support both approaches?
By default, the method could keep its current behavior — returning the provided default or an empty array — which keeps things ergonomic and avoids forcing try/catch for common cases.
But if throwOnNotFound is set to true, it could throw an ItemNotFoundException, giving developers who want stricter handling the option to opt in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thoughts exactly 👍🏻
This would be especially helpful to lower the impact of the breaking change even if we would remove the flag or revert the behavior in 14.x
src/Illuminate/Collections/Arr.php
Outdated
| { | ||
| $value = Arr::get($array, $key, $default); | ||
|
|
||
| if ($throwOnNotFound && $value === $default && ! Arr::has($array, $key)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't !Arr::has($array, $key) work without $value === $default or am I missing something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
! Arr::has($array, $key) on its own can’t distinguish between key is missing and key exists but its value equals the default.
For example:
$data = ['roles' => []];
// Key exists, value is [], same as default
Arr::array($data, 'roles', [], true);
If we only relied on ! Arr::has(), this would incorrectly throw. That’s why the check combines both:
$value === $default → catch when get() gave us the default
! Arr::has($array, $key) → confirm it’s truly missing, not just equal.
This way existing keys are respected, and only missing ones trigger ItemNotFoundException.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure? 🤨
framework/src/Illuminate/Collections/Arr.php
Lines 214 to 236 in 7b5e185
| /** | |
| * Determine if the given key exists in the provided array. | |
| * | |
| * @param \ArrayAccess|array $array | |
| * @param string|int|float $key | |
| * @return bool | |
| */ | |
| public static function exists($array, $key) | |
| { | |
| if ($array instanceof Enumerable) { | |
| return $array->has($key); | |
| } | |
| if ($array instanceof ArrayAccess) { | |
| return $array->offsetExists($key); | |
| } | |
| if (is_float($key)) { | |
| $key = (string) $key; | |
| } | |
| return array_key_exists($key, $array); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you’re right that Arr::exists() (and therefore Arr::has()) will correctly detect the presence of a key, even if its value is null or [].
The subtlety is that Arr::get() falls back to the default when the key is missing, so without the $value === $default guard, we can’t tell whether the returned value actually came from the array or from the default.
Example:
$data = ['roles' => []];
// Both `Arr::get($data, 'roles', [])` and `Arr::get([], 'roles', [])` return []
Here exists() will return true in the first case and false in the second — so combining ! Arr::has($array, $key) with $value === $default lets us safely tell the difference between key truly missing and key exists with default-like value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure? 🤨
framework/src/Illuminate/Collections/Arr.php
Lines 214 to 236 in 7b5e185
/** * Determine if the given key exists in the provided array. * * @param \ArrayAccess|array $array * @param string|int|float $key * @return bool */ public static function exists($array, $key) { if ($array instanceof Enumerable) { return $array->has($key); } if ($array instanceof ArrayAccess) { return $array->offsetExists($key); } if (is_float($key)) { $key = (string) $key; } return array_key_exists($key, $array); }
value === $default was mainly there as an extra safeguard — kind of a double-check to ensure we only throw when the key is truly missing.
In practice Arr::has() already guarantees that distinction, so it’s not strictly needed, but that was the reasoning behind having it in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you’re right that Arr::exists() (and therefore Arr::has()) will correctly detect the presence of a key, even if its value is null or [].
The subtlety is that Arr::get() falls back to the default when the key is missing, so without the $value === $default guard, we can’t tell whether the returned value actually came from the array or from the default.
Example:
$data = ['roles' => []]; // Both `Arr::get($data, 'roles', [])` and `Arr::get([], 'roles', [])` return []Here exists() will return true in the first case and false in the second — so combining ! Arr::has($array, $key) with $value === $default lets us safely tell the difference between key truly missing and key exists with default-like value.
Is this written by AI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no I was just trying to explain my perspective with an example. I thought it might help to catch any potential edge cases and thats why I initially left it in as a kind of double check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, thanks for the explanation 👍🏻
shaedrich
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review request, but since I'm not a maintainer, I cannot approve or reject your changes. Taylor will do that.
|
ty you for your time. i will wait for Taylor review and acceptance. |
|
We should fix the return type. |
This PR updates the Arr::array() method to use an empty array ([]) as the default return value when no key exists, instead of null.
Previously, the method signature allowed a null default (?array $default = null), but this created a mismatch with the strict : array return type. If a developer passed null as the default, the method would still throw an exception because null is not an array.