Skip to content

Commit 03aa1f4

Browse files
committed
Merge branch 'develop'
2 parents cc5213d + c5e7fcc commit 03aa1f4

File tree

2 files changed

+287
-79
lines changed

2 files changed

+287
-79
lines changed

src/ValidationRuleSet.php

Lines changed: 175 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -45,126 +45,96 @@ public function __construct(ValidationRulesRegistrar $register)
4545
public function rules(...$rules): self
4646
{
4747
foreach ($rules as $rule) {
48-
if ($rule instanceof Closure) {
49-
$this->validateClosureRule($rule);
50-
$this->rules[] = $rule;
51-
} elseif ($rule instanceof ValidationRule) {
52-
$this->rules[] = $rule;
53-
} elseif (is_string($rule)) {
54-
$this->rules[] = $this->getRuleFromString($rule);
55-
} else {
56-
Config::throwInvalidArgumentException(
57-
sprintf(
58-
'Validation rule must be a string, instance of %s, or a closure',
59-
ValidationRule::class
60-
)
61-
);
62-
}
48+
$this->rules[] = $this->sanitizeRule($rule);
6349
}
6450

6551
return $this;
6652
}
6753

6854
/**
69-
* Validates that a closure rule has the proper parameters to be used as a validation rule.
55+
* Prepends a given rule to the start of the rules array.
7056
*
71-
* @since 1.0.0
57+
* @since 1.3.0
7258
*
73-
* @return void
59+
* @param string|ValidationRule|Closure $rule
7460
*/
75-
private function validateClosureRule(Closure $closure)
61+
public function prependRule($rule): self
7662
{
77-
try {
78-
$reflection = new ReflectionFunction($closure);
79-
} catch (ReflectionException $e) {
80-
Config::throwInvalidArgumentException(
81-
'Unable to validate closure parameters. Please ensure that the closure is valid.'
82-
);
83-
}
63+
array_unshift($this->rules, $this->sanitizeRule($rule));
8464

85-
$parameters = $reflection->getParameters();
86-
$parameterCount = count($parameters);
65+
return $this;
66+
}
8767

88-
if ($parameterCount < 2 || $parameterCount > 4) {
89-
Config::throwInvalidArgumentException(
90-
"Validation rule closure must accept between 2 and 4 parameters, $parameterCount given."
91-
);
92-
}
68+
/**
69+
* Replaces the given rule at the same index position or appends it if it doesn't exist.
70+
*
71+
* @since 1.3.0
72+
*
73+
* @param string|ValidationRule|Closure $rule
74+
*
75+
* @return bool True if the rule was replaced, false if it was appended.
76+
*/
77+
public function replaceOrAppendRule(string $ruleId, $rule): bool
78+
{
79+
$replaced = $this->replaceRule($ruleId, $rule);
9380

94-
$parameterType = $this->getParameterTypeName($parameters[1]);
95-
if ($parameterType !== null && $parameterType !== 'Closure') {
96-
Config::throwInvalidArgumentException(
97-
"Validation rule closure must accept a Closure as the second parameter, {$parameterType} given."
98-
);
99-
}
81+
if (!$replaced) {
82+
$this->rules($rule);
10083

101-
$parameterType = $parameterCount > 2 ? $this->getParameterTypeName($parameters[2]) : null;
102-
if ($parameterType !== null && $parameterType !== 'string') {
103-
Config::throwInvalidArgumentException(
104-
"Validation rule closure must accept a string as the third parameter, {$parameterType} given."
105-
);
84+
return false;
10685
}
10786

108-
$parameterType = $parameterCount > 3 ? $this->getParameterTypeName($parameters[3]) : null;
109-
if ($parameterType !== null && $parameterType !== 'array') {
110-
Config::throwInvalidArgumentException(
111-
"Validation rule closure must accept a array as the fourth parameter, {$parameterType} given."
112-
);
113-
}
87+
return true;
11488
}
11589

11690
/**
117-
* Retrieves the parameter type with PHP 7.0 compatibility.
91+
* Replaces the given rule at the same index position or prepends it if it doesn't exist.
11892
*
119-
* @since 1.0.0
93+
* @since 1.3.0
12094
*
121-
* @return string|null
95+
* @param string|ValidationRule|Closure $rule
96+
*
97+
* @return bool True if the rule was replaced, false if it was prepended.
12298
*/
123-
private function getParameterTypeName(ReflectionParameter $parameter)
99+
public function replaceOrPrependRule(string $ruleId, $rule): bool
124100
{
125-
$type = $parameter->getType();
101+
$replaced = $this->replaceRule($ruleId, $rule);
126102

127-
if ($type === null) {
128-
return null;
129-
}
103+
if (!$replaced) {
104+
$this->prependRule($rule);
130105

131-
// Check if the method exists for PHP 7.0 compatibility (it exits as of PHP 7.1)
132-
if (method_exists($type, 'getName')) {
133-
return $type->getName();
106+
return false;
134107
}
135108

136-
return (string)$type;
109+
return true;
137110
}
138111

139112
/**
140-
* Takes a validation rule string and returns the corresponding rule instance.
113+
* Replace a rule with the given id with the given rule at the same index position. Returns true if the rule was
114+
* replaced, false otherwise.
141115
*
142-
* @since 1.0.0
116+
* @since 1.3.0
117+
*
118+
* @param string|ValidationRule|Closure $rule
143119
*/
144-
private function getRuleFromString(string $rule): ValidationRule
120+
public function replaceRule(string $ruleId, $rule): bool
145121
{
146-
list($ruleId, $ruleOptions) = array_pad(explode(':', $rule, 2), 2, null);
147-
148-
/**
149-
* @var ValidationRule $ruleClass
150-
*/
151-
$ruleClass = $this->register->getRule($ruleId);
122+
foreach ($this->rules as $index => $validationRule) {
123+
if ($validationRule instanceof ValidationRule && $validationRule::id() === $ruleId) {
124+
$this->rules[$index] = $this->sanitizeRule($rule);
152125

153-
if (!$ruleClass) {
154-
Config::throwInvalidArgumentException(
155-
sprintf(
156-
'Validation rule with id %s has not been registered.',
157-
$ruleId
158-
)
159-
);
126+
return true;
127+
}
160128
}
161129

162-
return $ruleClass::fromString($ruleOptions);
130+
return false;
163131
}
164132

165133
/**
166134
* Finds and returns the validation rule by id. Does not work for Closure rules.
167135
*
136+
* @since 1.0.0
137+
*
168138
* @return ValidationRule|null
169139
*/
170140
public function getRule(string $rule)
@@ -268,4 +238,130 @@ public function jsonSerialize()
268238

269239
return $rules;
270240
}
241+
242+
/**
243+
* Sanitizes a given rule by validating the rule and making sure it's safe to use.
244+
*
245+
* @since 1.3.0
246+
*
247+
* @param mixed $rule
248+
*
249+
* @return Closure|ValidationRule
250+
*/
251+
private function sanitizeRule($rule)
252+
{
253+
if ($rule instanceof Closure) {
254+
$this->validateClosureRule($rule);
255+
256+
return $rule;
257+
} elseif ($rule instanceof ValidationRule) {
258+
return $rule;
259+
} elseif (is_string($rule)) {
260+
return $this->getRuleFromString($rule);
261+
} else {
262+
Config::throwInvalidArgumentException(
263+
sprintf(
264+
'Validation rule must be a string, instance of %s, or a closure',
265+
ValidationRule::class
266+
)
267+
);
268+
}
269+
}
270+
271+
/**
272+
* Validates that a closure rule has the proper parameters to be used as a validation rule.
273+
*
274+
* @since 1.0.0
275+
*
276+
* @return void
277+
*/
278+
private function validateClosureRule(Closure $closure)
279+
{
280+
try {
281+
$reflection = new ReflectionFunction($closure);
282+
} catch (ReflectionException $e) {
283+
Config::throwInvalidArgumentException(
284+
'Unable to validate closure parameters. Please ensure that the closure is valid.'
285+
);
286+
}
287+
288+
$parameters = $reflection->getParameters();
289+
$parameterCount = count($parameters);
290+
291+
if ($parameterCount < 2 || $parameterCount > 4) {
292+
Config::throwInvalidArgumentException(
293+
"Validation rule closure must accept between 2 and 4 parameters, $parameterCount given."
294+
);
295+
}
296+
297+
$parameterType = $this->getParameterTypeName($parameters[1]);
298+
if ($parameterType !== null && $parameterType !== 'Closure') {
299+
Config::throwInvalidArgumentException(
300+
"Validation rule closure must accept a Closure as the second parameter, {$parameterType} given."
301+
);
302+
}
303+
304+
$parameterType = $parameterCount > 2 ? $this->getParameterTypeName($parameters[2]) : null;
305+
if ($parameterType !== null && $parameterType !== 'string') {
306+
Config::throwInvalidArgumentException(
307+
"Validation rule closure must accept a string as the third parameter, {$parameterType} given."
308+
);
309+
}
310+
311+
$parameterType = $parameterCount > 3 ? $this->getParameterTypeName($parameters[3]) : null;
312+
if ($parameterType !== null && $parameterType !== 'array') {
313+
Config::throwInvalidArgumentException(
314+
"Validation rule closure must accept a array as the fourth parameter, {$parameterType} given."
315+
);
316+
}
317+
}
318+
319+
/**
320+
* Retrieves the parameter type with PHP 7.0 compatibility.
321+
*
322+
* @since 1.0.0
323+
*
324+
* @return string|null
325+
*/
326+
private function getParameterTypeName(ReflectionParameter $parameter)
327+
{
328+
$type = $parameter->getType();
329+
330+
if ($type === null) {
331+
return null;
332+
}
333+
334+
// Check if the method exists for PHP 7.0 compatibility (it exits as of PHP 7.1)
335+
if (method_exists($type, 'getName')) {
336+
return $type->getName();
337+
}
338+
339+
return (string)$type;
340+
}
341+
342+
/**
343+
* Takes a validation rule string and returns the corresponding rule instance.
344+
*
345+
* @since 1.0.0
346+
*/
347+
private function getRuleFromString(string $rule): ValidationRule
348+
{
349+
[$ruleId, $ruleOptions] = array_pad(explode(':', $rule, 2), 2, null);
350+
351+
/**
352+
* @var ValidationRule $ruleClass
353+
*/
354+
$ruleClass = $this->register->getRule($ruleId);
355+
356+
if (!$ruleClass) {
357+
Config::throwInvalidArgumentException(
358+
sprintf(
359+
'Validation rule with id %s has not been registered.',
360+
$ruleId
361+
)
362+
);
363+
}
364+
365+
return $ruleClass::fromString($ruleOptions);
366+
}
271367
}

0 commit comments

Comments
 (0)