-
Notifications
You must be signed in to change notification settings - Fork 11.8k
Description
Laravel Version
12.55.1
PHP Version
8.3.23
Database Driver & Version
N/A
Description
When a custom validation message is defined for a wildcard attribute key (e.g. students.*.grade) using a nested array of rule-specific messages, and a failing rule is not in that nested array, getFromLocalArray returns the entire array instead of null. This causes a TypeError crash in replaceInputPlaceholder, which was introduced in v12.55.1.
<?php
if (preg_match('#^'.$pattern.'\z#u', $key) === 1) {
$message = $source[$sourceKey];
if (is_array($message) && isset($message[$lowerRule])) {
return $message[$lowerRule];
}
return $message; // BUG: returns the array when $lowerRule is not in $message
}When $message is an array and $message[$lowerRule] is not set, return $message returns the whole array. This is then passed to replaceInputPlaceholder in v12.55.1, which now calls str_contains($message, ':input') — crashing with: TypeError: str_contains(): Argument #1 ($haystack) must be of type string, array given
This was silent in v12.54.1 because replaceInputPlaceholder did not have the str_contains guard and would just skip the replacement harmlessly.
Steps To Reproduce
- Define a custom message for only one rule on a wildcard attribute key:
<?php
// lang/en/validation.php (or returned from FormRequest::messages())
'custom' => [
'students.*.grade' => [
'required' => 'Custom required message.',
// 'in' is intentionally absent
],
],- Submit a value that fails the
inrule forstudents.*.grade:
<?php
'students.*.grade' => ['required', Rule::in(['1','2','3','4','5'])],- Observe a 500
TypeErrorinstaed of the expected 422 validation error.
###Expected Behavior
When a wildcard key matches but does not contain the specific failing rule, getFromLocalArray should return null so Laravel falls back to the default validation message for that rule.
###Suggested Fix
In getFromLocalArray, change the wildcard fallthrough to return null when the message is an array but doesn't contain the rule:
<?php
if (preg_match('#^'.$pattern.'\z#u', $key) === 1) {
$message = $source[$sourceKey];
if (is_array($message)) {
return $message[$lowerRule] ?? null; // return null instead of the whole array
}
return $message;
}This makes the wildcard branch consistent with the exact-match branch directly below it, which already does return $message[$lowerRule] ?? null.