-
Notifications
You must be signed in to change notification settings - Fork 11.1k
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
[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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
false
will be returned fromruleIsRegex()
array_filter
will then clearfalse
out 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we adjust this code to actually explicitly pass an integer to explode
?
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.
Yes absolutely, one moment.
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'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.