Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 |
||
49 | class Validator extends Component implements ValidatorInterface, LoggerAwareInterface |
||
50 | { |
||
51 | use LoggerTrait, TranslatorTrait, SaturateTrait; |
||
52 | |||
53 | /** |
||
54 | * Return from validation rule to stop any future field validations. Internal contract. |
||
55 | */ |
||
56 | const STOP_VALIDATION = -99; |
||
57 | |||
58 | /** |
||
59 | * @invisible |
||
60 | * @var ValidatorConfig |
||
61 | */ |
||
62 | private $config = null; |
||
63 | |||
64 | /** |
||
65 | * @var array|\ArrayAccess |
||
66 | */ |
||
67 | private $data = []; |
||
68 | |||
69 | /** |
||
70 | * Validation rules, see class title for description. |
||
71 | * |
||
72 | * @var array |
||
73 | */ |
||
74 | private $rules = []; |
||
75 | |||
76 | /** |
||
77 | * Error messages raised while validation. |
||
78 | * |
||
79 | * @var array |
||
80 | */ |
||
81 | private $errors = []; |
||
82 | |||
83 | /** |
||
84 | * Errors provided from outside. |
||
85 | * |
||
86 | * @var array |
||
87 | */ |
||
88 | private $registeredErrors = []; |
||
89 | |||
90 | /** |
||
91 | * If rule has no definer error message this text will be used instead. Localizable. |
||
92 | * |
||
93 | * @invisible |
||
94 | * @var string |
||
95 | */ |
||
96 | protected $defaultMessage = "[[Condition '{condition}' does not meet.]]"; |
||
97 | |||
98 | /** |
||
99 | * @invisible |
||
100 | * @var ContainerInterface |
||
101 | */ |
||
102 | protected $container = null; |
||
103 | |||
104 | /** |
||
105 | * {@inheritdoc} |
||
106 | * |
||
107 | * @param array $rules Validation rules. |
||
108 | * @param array|\ArrayAccess $data Data or model to be validated. |
||
109 | * @param ValidatorConfig $config Saturated using shared container |
||
110 | * @param ContainerInterface $container Saturated using shared container |
||
111 | * |
||
112 | * @throws ScopeException |
||
113 | */ |
||
114 | public function __construct( |
||
126 | |||
127 | /** |
||
128 | * {@inheritdoc} |
||
129 | */ |
||
130 | View Code Duplication | public function setRules(array $rules): ValidatorInterface |
|
141 | |||
142 | /** |
||
143 | * {@inheritdoc} |
||
144 | */ |
||
145 | View Code Duplication | public function setData($data): ValidatorInterface |
|
156 | |||
157 | /** |
||
158 | * {@inheritdoc} |
||
159 | */ |
||
160 | public function getData() |
||
164 | |||
165 | /** |
||
166 | * {@inheritdoc} |
||
167 | */ |
||
168 | public function isValid(): bool |
||
174 | |||
175 | /** |
||
176 | * {@inheritdoc} |
||
177 | */ |
||
178 | public function registerError(string $field, string $error): ValidatorInterface |
||
184 | |||
185 | /** |
||
186 | * {@inheritdoc} |
||
187 | */ |
||
188 | public function flushRegistered(): ValidatorInterface |
||
194 | |||
195 | /** |
||
196 | * {@inheritdoc} |
||
197 | */ |
||
198 | public function hasErrors(): bool |
||
202 | |||
203 | /** |
||
204 | * {@inheritdoc} |
||
205 | */ |
||
206 | public function getErrors(): array |
||
212 | |||
213 | /** |
||
214 | * Receive field from context data or return default value. |
||
215 | * |
||
216 | * @param string $field |
||
217 | * @param mixed $default |
||
218 | * |
||
219 | * @return mixed |
||
220 | */ |
||
221 | public function getValue(string $field, $default = null) |
||
229 | |||
230 | /** |
||
231 | * Reset validation state. |
||
232 | */ |
||
233 | public function reset() |
||
238 | |||
239 | /** |
||
240 | * Validate context data with set of validation rules. |
||
241 | */ |
||
242 | protected function validate() |
||
303 | |||
304 | /** |
||
305 | * Check field with given condition. Can return instance of Checker (data is not valid) to |
||
306 | * clarify error. |
||
307 | * |
||
308 | * @param string $field |
||
309 | * @param mixed $value |
||
310 | * @param mixed $condition Reference, can be altered if alias exists. |
||
311 | * @param array $arguments Rule arguments if any. |
||
312 | * |
||
313 | * @return bool|CheckerInterface |
||
314 | * @throws ValidationException |
||
315 | */ |
||
316 | protected function check(string $field, $value, &$condition, array $arguments = []) |
||
355 | |||
356 | /** |
||
357 | * Get or create instance of validation checker. |
||
358 | * |
||
359 | * @param string $name |
||
360 | * |
||
361 | * @return CheckerInterface |
||
362 | * @throws ValidationException |
||
363 | */ |
||
364 | protected function makeChecker(string $name): CheckerInterface |
||
374 | |||
375 | /** |
||
376 | * Fetch validation rule arguments from rule definition. |
||
377 | * |
||
378 | * @param array $rule |
||
379 | * |
||
380 | * @return array |
||
381 | */ |
||
382 | private function fetchArguments(array $rule): array |
||
388 | |||
389 | /** |
||
390 | * Fetch error message from rule definition or use default message. Method will check "message" |
||
391 | * and "error" properties of definition. |
||
392 | * |
||
393 | * @param array $rule |
||
394 | * @param string $message Default message to use. |
||
395 | * |
||
396 | * @return string |
||
397 | */ |
||
398 | private function fetchMessage(array $rule, string $message): string |
||
410 | |||
411 | /** |
||
412 | * Register error message for specified field. Rule definition will be interpolated into |
||
413 | * message. |
||
414 | * |
||
415 | * @param string $field |
||
416 | * @param string $message |
||
417 | * @param mixed $condition |
||
418 | * @param array $arguments |
||
419 | */ |
||
420 | private function addMessage(string $field, string $message, $condition, array $arguments = []) |
||
435 | |||
436 | /** |
||
437 | * @param string $field |
||
438 | * @param array $condition |
||
439 | * @param \Throwable $e |
||
440 | */ |
||
441 | protected function logException(string $field, $condition, \Throwable $e) |
||
456 | } |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.