@@ -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