[9.x] Patch regex rule parsing due to Rule::forEach() - #40941
Conversation
| } elseif (is_object($rule)) { | ||
| [$name] = static::parseStringRule($rule); | ||
|
|
||
| return explode('|', $rule, ...array_filter([static::ruleIsRegex($name)])); |
There was a problem hiding this comment.
I've used the spread operator to completely omit the third ($limit) parameter if the rule is not regex.
If the rule is not a regex:
falsewill be returned fromruleIsRegex()array_filterwill then clearfalseout of the array, resulting in an empty array- Spread operator will do nothing, as there are no elements of the array
I've added this, because explode() will take a null, false, or 0 parameter and set the $limit to 1.
We want $limit to be 1 when the rule is a regular expression, or $limit to be completely omitted when the rule is not.
There was a problem hiding this comment.
Can we adjust this code to actually explicitly pass an integer to explode?
There was a problem hiding this comment.
Yes absolutely, one moment.
There was a problem hiding this comment.
I've simplified it to:
return static::ruleIsRegex($name) ? [$rule] : explode('|', $rule);Since the explode('|', $rule, 1) will simply wrap the rule in an array. Let me know if you want me to change this. If I wanted to explicitly pass in an integer value, I'd have to use PHP_INT_MAX for the default value (which looked a bit off -- not sure of your opinion on this):
return explode('|', $rule, static::ruleIsRegex($name) ? 1 : PHP_INT_MAX);|
Hmm, a random test is failing. If we execute another run, we should be green 👍 |
|
I'm guessing this still doesn't work? 'attribute' => 'required|string|regex:/^(super|admin)$/i|another_rule|final_rule`, |
|
Yes that's correct -- the rule will fail to parse properly. I've added a test case so we have some visibility on this: |
|
@stevebauman can you explain how your original PR actually introduced this bug? I'm not sure I quite understand how and what broke. |
|
I introduced this bug by using The structure of // Previously:
^ array:1 [
0 => array:2 [
0 => "in:foo"
1 => "regex:/^(foo|bar)$/i"
]
]
// With Arr::flatten()
^ array:2 [
0 => "in:foo"
1 => "regex:/^(foo|bar)$/i"
]The arrays were previously multi-dimensional and are now single dimensional. This means, the first framework/src/Illuminate/Validation/ValidationRuleParser.php Lines 85 to 97 in 4a474ce This causes the explode to inadvertently target the regex rules, when it did not previously. For |
Description
This PR fixes #40924.
This also allows the ability to supply a regex validation rule inside of a single string-based rule, instead of requiring it to be an array.
Before:
This would previously fail in Laravel:
After:
Notes
I've added various test cases to ensure parsing is done properly. However, maybe we should introduce an exception when
preg_match()throws an exception due to a malformed regex which would indicate to the developer they must wrap the regex rule in an array when using multiple rules?Let me know your thoughts. If you like the idea, I will update my PR to include this exception.