Total Complexity | 42 |
Total Lines | 313 |
Duplicated Lines | 0 % |
Coverage | 0% |
Changes | 0 |
Complex classes like Validator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Validator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class Validator |
||
14 | { |
||
15 | use ValidationRules; |
||
16 | |||
17 | /** |
||
18 | * @var array |
||
19 | */ |
||
20 | public $errors = []; |
||
21 | |||
22 | /** |
||
23 | * The data under validation. |
||
24 | * @var array |
||
25 | */ |
||
26 | protected $data = []; |
||
27 | |||
28 | /** |
||
29 | * The failed validation rules. |
||
30 | * @var array |
||
31 | */ |
||
32 | protected $failedRules = []; |
||
33 | |||
34 | /** |
||
35 | * The rules to be applied to the data. |
||
36 | * @var array |
||
37 | */ |
||
38 | protected $rules = []; |
||
39 | |||
40 | /** |
||
41 | * The size related validation rules. |
||
42 | * @var array |
||
43 | */ |
||
44 | protected $sizeRules = [ |
||
45 | 'Between', 'Max', 'Min', |
||
46 | ]; |
||
47 | |||
48 | /** |
||
49 | * The validation rules that imply the field is required. |
||
50 | * @var array |
||
51 | */ |
||
52 | protected $implicitRules = [ |
||
53 | 'Required', |
||
54 | ]; |
||
55 | |||
56 | /** |
||
57 | * The numeric related validation rules. |
||
58 | * @var array |
||
59 | */ |
||
60 | protected $numericRules = [ |
||
61 | 'Numeric', |
||
62 | ]; |
||
63 | |||
64 | /** |
||
65 | * Run the validator's rules against its data. |
||
66 | * @param mixed $data |
||
67 | * @return array |
||
68 | */ |
||
69 | public function validate( $data, array $rules = [] ) |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * Validate a given attribute against a rule. |
||
84 | * @param string $attribute |
||
85 | * @param string $rule |
||
86 | * @return void |
||
87 | * @throws BadMethodCallException |
||
88 | */ |
||
89 | public function validateAttribute( $attribute, $rule ) |
||
90 | { |
||
91 | list( $rule, $parameters ) = $this->parseRule( $rule ); |
||
92 | if( $rule == '' )return; |
||
93 | $value = $this->getValue( $attribute ); |
||
94 | if( !method_exists( $this, $method = 'validate'.$rule )) { |
||
95 | throw new BadMethodCallException( "Method [$method] does not exist." ); |
||
96 | } |
||
97 | if( !$this->$method( $attribute, $value, $parameters )) { |
||
98 | $this->addFailure( $attribute, $rule, $parameters ); |
||
99 | } |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * Add an error message to the validator's collection of errors. |
||
104 | * @param string $attribute |
||
105 | * @param string $rule |
||
106 | * @return void |
||
107 | */ |
||
108 | protected function addError( $attribute, $rule, array $parameters ) |
||
109 | { |
||
110 | $message = $this->getMessage( $attribute, $rule, $parameters ); |
||
111 | $this->errors[$attribute][] = $message; |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * Add a failed rule and error message to the collection. |
||
116 | * @param string $attribute |
||
117 | * @param string $rule |
||
118 | * @return void |
||
119 | */ |
||
120 | protected function addFailure( $attribute, $rule, array $parameters ) |
||
121 | { |
||
122 | $this->addError( $attribute, $rule, $parameters ); |
||
123 | $this->failedRules[$attribute][$rule] = $parameters; |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Get the data type of the given attribute. |
||
128 | * @param string $attribute |
||
129 | * @return string |
||
130 | */ |
||
131 | protected function getAttributeType( $attribute ) |
||
132 | { |
||
133 | return $this->hasRule( $attribute, $this->numericRules ) |
||
134 | ? 'numeric' |
||
135 | : 'string'; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Get the validation message for an attribute and rule. |
||
140 | * @param string $attribute |
||
141 | * @param string $rule |
||
142 | * @return string|null |
||
143 | */ |
||
144 | protected function getMessage( $attribute, $rule, array $parameters ) |
||
145 | { |
||
146 | if( in_array( $rule, $this->sizeRules )) { |
||
147 | return $this->getSizeMessage( $attribute, $rule, $parameters ); |
||
148 | } |
||
149 | $lowerRule = glsr( Helper::class )->snakeCase( $rule ); |
||
150 | return $this->translator( $lowerRule, $rule, $parameters ); |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * Get a rule and its parameters for a given attribute. |
||
155 | * @param string $attribute |
||
156 | * @param string|array $rules |
||
157 | * @return array|null |
||
158 | */ |
||
159 | protected function getRule( $attribute, $rules ) |
||
160 | { |
||
161 | if( !array_key_exists( $attribute, $this->rules ))return; |
||
162 | $rules = (array) $rules; |
||
163 | foreach( $this->rules[$attribute] as $rule ) { |
||
164 | list( $rule, $parameters ) = $this->parseRule( $rule ); |
||
165 | if( in_array( $rule, $rules )) { |
||
166 | return [$rule, $parameters]; |
||
167 | } |
||
168 | } |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Get the size of an attribute. |
||
173 | * @param string $attribute |
||
174 | * @param mixed $value |
||
175 | * @return mixed |
||
176 | */ |
||
177 | protected function getSize( $attribute, $value ) |
||
178 | { |
||
179 | $hasNumeric = $this->hasRule( $attribute, $this->numericRules ); |
||
180 | if( is_numeric( $value ) && $hasNumeric ) { |
||
181 | return $value; |
||
182 | } |
||
183 | elseif( is_array( $value )) { |
||
184 | return count( $value ); |
||
185 | } |
||
186 | return mb_strlen( $value ); |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Get the proper error message for an attribute and size rule. |
||
191 | * @param string $attribute |
||
192 | * @param string $rule |
||
193 | * @return string|null |
||
194 | */ |
||
195 | protected function getSizeMessage( $attribute, $rule, array $parameters ) |
||
196 | { |
||
197 | $lowerRule = glsr( Helper::class )->snakeCase( $rule ); |
||
198 | $type = $this->getAttributeType( $attribute ); |
||
199 | $lowerRule .= '.'.$type; |
||
200 | return $this->translator( $lowerRule, $rule, $parameters ); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * Get the value of a given attribute. |
||
205 | * @param string $attribute |
||
206 | * @return mixed |
||
207 | */ |
||
208 | protected function getValue( $attribute ) |
||
209 | { |
||
210 | if( isset( $this->data[$attribute] )) { |
||
211 | return $this->data[$attribute]; |
||
212 | } |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Determine if the given attribute has a rule in the given set. |
||
217 | * @param string $attribute |
||
218 | * @param string|array $rules |
||
219 | * @return bool |
||
220 | */ |
||
221 | protected function hasRule( $attribute, $rules ) |
||
222 | { |
||
223 | return !is_null( $this->getRule( $attribute, $rules )); |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Normalize the provided data to an array. |
||
228 | * @param mixed $data |
||
229 | * @return void |
||
230 | */ |
||
231 | protected function normalizeData( $data ) |
||
232 | { |
||
233 | $this->data = is_object( $data ) |
||
234 | ? get_object_vars( $data ) |
||
235 | : $data; |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * Parse a parameter list. |
||
240 | * @param string $rule |
||
241 | * @param string $parameter |
||
242 | * @return array |
||
243 | */ |
||
244 | protected function parseParameters( $rule, $parameter ) |
||
245 | { |
||
246 | if( strtolower( $rule ) == 'regex' ) { |
||
247 | return [$parameter]; |
||
248 | } |
||
249 | return str_getcsv( $parameter ); |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Extract the rule name and parameters from a rule. |
||
254 | * @param string $rule |
||
255 | * @return array |
||
256 | */ |
||
257 | protected function parseRule( $rule ) |
||
258 | { |
||
259 | $parameters = []; |
||
260 | // example: {rule}:{parameters} |
||
1 ignored issue
–
show
|
|||
261 | if( strpos( $rule, ':' ) !== false ) { |
||
262 | list( $rule, $parameter ) = explode( ':', $rule, 2 ); |
||
263 | // example: {parameter1,parameter2,...} |
||
1 ignored issue
–
show
|
|||
264 | $parameters = $this->parseParameters( $rule, $parameter ); |
||
265 | } |
||
266 | $rule = ucwords( str_replace( ['-', '_'], ' ', trim( $rule ))); |
||
267 | $rule = str_replace( ' ', '', $rule ); |
||
268 | return [$rule, $parameters]; |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Set the validation rules. |
||
273 | * @return void |
||
274 | */ |
||
275 | protected function setRules( array $rules ) |
||
276 | { |
||
277 | foreach( $rules as $key => $rule ) { |
||
278 | $rules[$key] = is_string( $rule ) |
||
279 | ? explode( '|', $rule ) |
||
280 | : $rule; |
||
281 | } |
||
282 | $this->rules = $rules; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Check if we should stop further validations on a given attribute. |
||
287 | * @param string $attribute |
||
288 | * @return bool |
||
289 | */ |
||
290 | protected function shouldStopValidating( $attribute ) |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Returns a translated message for the attribute |
||
299 | * @param string $key |
||
300 | * @param string $rule |
||
301 | * @param string $attribute |
||
302 | * @return string|null |
||
303 | */ |
||
304 | protected function translator( $key, $rule, array $parameters ) |
||
326 | } |
||
327 | } |
||
328 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.