Complex classes like Schema 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 Schema, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class Schema implements \JsonSerializable { |
||
14 | /// Constants /// |
||
15 | |||
16 | /** |
||
17 | * Throw a notice when extraneous properties are encountered during validation. |
||
18 | */ |
||
19 | const FLAG_EXTRA_PROPERTIES_NOTICE = 0x1; |
||
20 | |||
21 | /** |
||
22 | * Throw a ValidationException when extraneous properties are encountered during validation. |
||
23 | */ |
||
24 | const FLAG_EXTRA_PROPERTIES_EXCEPTION = 0x2; |
||
25 | |||
26 | /// Properties /// |
||
27 | protected static $types = [ |
||
28 | 'a' => 'array', |
||
29 | 'o' => 'object', |
||
30 | 'i' => 'integer', |
||
31 | 'int' => 'integer', |
||
32 | 's' => 'string', |
||
33 | 'str' => 'string', |
||
34 | 'f' => 'float', |
||
35 | 'b' => 'boolean', |
||
36 | 'bool' => 'boolean', |
||
37 | 'ts' => 'timestamp', |
||
38 | 'dt' => 'datetime' |
||
39 | ]; |
||
40 | |||
41 | protected $schema = []; |
||
42 | |||
43 | /** |
||
44 | * @var int A bitwise combination of the various **Schema::FLAG_*** constants. |
||
45 | */ |
||
46 | protected $flags = 0; |
||
47 | |||
48 | /** |
||
49 | * @var array An array of callbacks that will custom validate the schema. |
||
50 | */ |
||
51 | protected $validators = []; |
||
52 | |||
53 | /// Methods /// |
||
54 | |||
55 | /** |
||
56 | * Initialize an instance of a new {@link Schema} class. |
||
57 | * |
||
58 | * @param array $schema The array schema to validate against. |
||
59 | */ |
||
60 | 96 | public function __construct($schema = []) { |
|
61 | 96 | $this->schema = $this->parse($schema); |
|
62 | 96 | } |
|
63 | |||
64 | /** |
||
65 | * Grab the schema's current description. |
||
66 | * |
||
67 | * @return string |
||
68 | */ |
||
69 | 1 | public function getDescription() { |
|
70 | 1 | return isset($this->schema['description']) ? $this->schema['description'] : ''; |
|
71 | } |
||
72 | |||
73 | /** |
||
74 | * Set the description for the schema. |
||
75 | * |
||
76 | * @param string $description The new description. |
||
77 | * @throws \InvalidArgumentException Throws an exception when the provided description is not a string. |
||
78 | * @return Schema |
||
79 | */ |
||
80 | 2 | public function setDescription($description) { |
|
81 | 2 | if (is_string($description)) { |
|
82 | 1 | $this->schema['description'] = $description; |
|
83 | 1 | } else { |
|
84 | 1 | throw new \InvalidArgumentException("The description is not a valid string.", 500); |
|
85 | } |
||
86 | |||
87 | 1 | return $this; |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * Return the validation flags. |
||
92 | * |
||
93 | * @return int Returns a bitwise combination of flags. |
||
94 | */ |
||
95 | 1 | public function getFlags() { |
|
96 | 1 | return $this->flags; |
|
97 | } |
||
98 | |||
99 | /** |
||
100 | * Set the validation flags. |
||
101 | * |
||
102 | * @param int $flags One or more of the **Schema::FLAG_*** constants. |
||
103 | * @return Schema Returns the current instance for fluent calls. |
||
104 | */ |
||
105 | 8 | public function setFlags($flags) { |
|
106 | 8 | if (!is_int($flags)) { |
|
107 | 1 | throw new \InvalidArgumentException('Invalid flags.', 500); |
|
108 | } |
||
109 | 7 | $this->flags = $flags; |
|
110 | |||
111 | 7 | return $this; |
|
112 | } |
||
113 | |||
114 | /** |
||
115 | * Whether or not the schema has a flag (or combination of flags). |
||
116 | * |
||
117 | * @param int $flag One or more of the **Schema::VALIDATE_*** constants. |
||
118 | * @return bool Returns **true** if all of the flags are set or **false** otherwise. |
||
119 | */ |
||
120 | 8 | public function hasFlag($flag) { |
|
123 | |||
124 | /** |
||
125 | * Set a flag. |
||
126 | * |
||
127 | * @param int $flag One or more of the **Schema::VALIDATE_*** constants. |
||
128 | * @param bool $value Either true or false. |
||
129 | * @return $this |
||
130 | */ |
||
131 | 1 | public function setFlag($flag, $value) { |
|
139 | |||
140 | /** |
||
141 | * Merge a schema with this one. |
||
142 | * |
||
143 | * @param Schema $schema A scheme instance. Its parameters will be merged into the current instance. |
||
144 | */ |
||
145 | 2 | public function merge(Schema $schema) { |
|
169 | |||
170 | /** |
||
171 | * Parse a schema in short form into a full schema array. |
||
172 | * |
||
173 | * @param array $arr The array to parse into a schema. |
||
174 | * @return array The full schema array. |
||
175 | * @throws \InvalidArgumentException Throws an exception when an item in the schema is invalid. |
||
176 | */ |
||
177 | 96 | public function parse(array $arr) { |
|
209 | |||
210 | /** |
||
211 | * @param array $node |
||
212 | * @param mixed $value |
||
213 | * @return array |
||
214 | */ |
||
215 | 91 | private function parseNode($node, $value = null) { |
|
252 | |||
253 | /** |
||
254 | * @param array $arr |
||
255 | * @return array |
||
256 | */ |
||
257 | 68 | private function parseProperties(array $arr) { |
|
283 | |||
284 | /** |
||
285 | * Parse a short parameter string into a full array parameter. |
||
286 | * |
||
287 | * @param string $key The short parameter string to parse. |
||
288 | * @param array $value An array of other information that might help resolve ambiguity. |
||
289 | * @return array Returns an array in the form `[string name, array param, bool required]`. |
||
290 | * @throws \InvalidArgumentException Throws an exception if the short param is not in the correct format. |
||
291 | */ |
||
292 | 91 | public function parseShortParam($key, $value = []) { |
|
332 | |||
333 | /** |
||
334 | * Add a custom validator to to validate the schema. |
||
335 | * |
||
336 | * @param string $fieldname The name of the field to validate, if any. |
||
337 | * |
||
338 | * If you are adding a validator to a deeply nested field then separate the path with dots. |
||
339 | * @param callable $callback The callback to validate with. |
||
340 | * @return Schema Returns `$this` for fluent calls. |
||
341 | */ |
||
342 | 2 | public function addValidator($fieldname, callable $callback) { |
|
346 | |||
347 | /** |
||
348 | * Require one of a given set of fields in the schema. |
||
349 | * |
||
350 | * @param array $required The field names to require. |
||
351 | * @param string $fieldname The name of the field to attach to. |
||
352 | * @param int $count The count of required items. |
||
353 | * @return Schema Returns `$this` for fluent calls. |
||
354 | */ |
||
355 | 1 | public function requireOneOf(array $required, $fieldname = '', $count = 1) { |
|
404 | |||
405 | /** |
||
406 | * Validate data against the schema. |
||
407 | * |
||
408 | * @param mixed $data The data to validate. |
||
409 | * @param bool $sparse Whether or not this is a sparse validation. |
||
410 | * @return mixed Returns a cleaned version of the data. |
||
411 | * @throws ValidationException Throws an exception when the data does not validate against the schema. |
||
412 | */ |
||
413 | 74 | public function validate($data, $sparse = false) { |
|
424 | |||
425 | /** |
||
426 | * Validate data against the schema and return the result. |
||
427 | * |
||
428 | * @param array &$data The data to validate. |
||
429 | * @param bool $sparse Whether or not to do a sparse validation. |
||
430 | * @return bool Returns true if the data is valid. False otherwise. |
||
431 | */ |
||
432 | 21 | public function isValid(array &$data, $sparse = false) { |
|
440 | |||
441 | /** |
||
442 | * Validate a field. |
||
443 | * |
||
444 | * @param mixed $value The value to validate. |
||
445 | * @param array|Schema $field Parameters on the field. |
||
446 | * @param Validation $validation A validation object to add errors to. |
||
447 | * @param string $name The name of the field being validated or an empty string for the root. |
||
448 | * @param bool $sparse Whether or not this is a sparse validation. |
||
449 | * @return mixed Returns a clean version of the value with all extra fields stripped out. |
||
450 | */ |
||
451 | 74 | private function validateField($value, $field, Validation $validation, $name = '', $sparse = false) { |
|
506 | |||
507 | /** |
||
508 | * Add an invalid type error. |
||
509 | * |
||
510 | * @param Validation $validation The validation to add the error to. |
||
511 | * @param string $name The full field name. |
||
512 | * @param string $type The type that was checked. |
||
513 | * @return $this |
||
514 | */ |
||
515 | 35 | protected function addTypeError(Validation $validation, $name, $type) { |
|
528 | |||
529 | /** |
||
530 | * Call all of the validators attached to a field. |
||
531 | * |
||
532 | * @param mixed $value The field value being validated. |
||
533 | * @param string $name The full path to the field. |
||
534 | * @param Validation $validation The validation object to add errors. |
||
535 | * @internal param array $field The field schema. |
||
536 | * @internal param bool $sparse Whether this is a sparse validation. |
||
537 | */ |
||
538 | 74 | private function callValidators($value, $name, Validation $validation) { |
|
547 | |||
548 | /** |
||
549 | * Validate an array. |
||
550 | * |
||
551 | * @param mixed &$value The value to validate. |
||
552 | * @param array $field The field definition. |
||
553 | * @param Validation $validation The validation results to add. |
||
554 | * @param string $name The name of the field being validated or an empty string for the root. |
||
555 | * @param bool $sparse Whether or not this is a sparse validation. |
||
556 | * @return bool Returns true if {@link $value} is valid or false otherwise. |
||
557 | */ |
||
558 | 10 | private function validateArray(&$value, array $field, Validation $validation, $name = '', $sparse = false) { |
|
583 | |||
584 | /** |
||
585 | * Validate a boolean value. |
||
586 | * |
||
587 | * @param mixed &$value The value to validate. |
||
588 | * @param array $field The field definition. |
||
589 | * @return bool Returns true if the value is valid or false otherwise. |
||
590 | * @internal param Validation $validation The validation results to add. |
||
591 | */ |
||
592 | 19 | private function validateBoolean(&$value, array $field) { |
|
609 | |||
610 | /** |
||
611 | * Validate a date time. |
||
612 | * |
||
613 | * @param mixed &$value The value to validate. |
||
614 | * @param array $field The field definition. |
||
615 | * @return bool Returns true if <a href='psi_element://$value'>$value</a> is valid or false otherwise. |
||
616 | * is valid or false otherwise. |
||
617 | * @internal param Validation $validation The validation results to add. |
||
618 | */ |
||
619 | 6 | private function validateDatetime(&$value, array $field) { |
|
642 | |||
643 | /** |
||
644 | * Validate a float. |
||
645 | * |
||
646 | * @param mixed &$value The value to validate. |
||
647 | * @param array $field The field definition. |
||
648 | * @return bool Returns true if <a href='psi_element://$value'>$value</a> is valid or false otherwise. |
||
649 | * is valid or false otherwise. |
||
650 | * @internal param Validation $validation The validation results to add. |
||
651 | */ |
||
652 | 7 | private function validateFloat(&$value, array $field) { |
|
663 | |||
664 | /** |
||
665 | * Validate and integer. |
||
666 | * |
||
667 | * @param mixed &$value The value to validate. |
||
668 | * @param array $field The field definition. |
||
669 | * @return bool Returns true if <a href='psi_element://$value'>$value</a> is valid or false otherwise. |
||
670 | * is valid or false otherwise. |
||
671 | * @internal param Validation $validation The validation results to add. |
||
672 | */ |
||
673 | 19 | private function validateInteger(&$value, array $field) { |
|
684 | |||
685 | /** |
||
686 | * Validate an object. |
||
687 | * |
||
688 | * @param mixed &$value The value to validate. |
||
689 | * @param array $field The field definition. |
||
690 | * @param Validation $validation The validation results to add. |
||
691 | * @param string $name The name of the field being validated or an empty string for the root. |
||
692 | * @param bool $sparse Whether or not this is a sparse validation. |
||
693 | * @return bool Returns true if {@link $value} is valid or false otherwise. |
||
694 | */ |
||
695 | 58 | private function validateObject(&$value, array $field, Validation $validation, $name = '', $sparse = false) { |
|
704 | |||
705 | /** |
||
706 | * Validate data against the schema and return the result. |
||
707 | * |
||
708 | * @param array $data The data to validate. |
||
709 | * @param array $field The schema array to validate against. |
||
710 | * @param Validation $validation This argument will be filled with the validation result. |
||
711 | * @param string $name The path to the current path for nested objects. |
||
712 | * @param bool $sparse Whether or not this is a sparse validation. |
||
713 | * @return array Returns a clean array with only the appropriate properties and the data coerced to proper types. |
||
714 | */ |
||
715 | 58 | private function validateProperties(array $data, array $field, Validation $validation, $name = '', $sparse = false) { |
|
765 | |||
766 | /** |
||
767 | * Validate a string. |
||
768 | * |
||
769 | * @param mixed &$value The value to validate. |
||
770 | * @param array $field The field definition. |
||
771 | * @param Validation $validation The validation results to add. |
||
772 | * @param string $name The name of the field being validated. |
||
773 | * @return bool Returns true if {@link $value} is valid or false otherwise. |
||
774 | */ |
||
775 | 19 | private function validateString(&$value, array $field, Validation $validation, $name = '') { |
|
833 | |||
834 | /** |
||
835 | * Validate a unix timestamp. |
||
836 | * |
||
837 | * @param mixed &$value The value to validate. |
||
838 | * @param array $field The field definition. |
||
839 | * @param Validation $validation The validation results to add. |
||
840 | * @return bool Returns true if {@link $value} is valid or false otherwise. |
||
841 | */ |
||
842 | 6 | private function validateTimestamp(&$value, array $field, Validation $validation) { |
|
853 | |||
854 | /** |
||
855 | * Specify data which should be serialized to JSON. |
||
856 | * |
||
857 | * @link http://php.net/manual/en/jsonserializable.jsonserialize.php |
||
858 | * @return mixed data which can be serialized by <b>json_encode</b>, |
||
859 | * which is a value of any type other than a resource. |
||
860 | */ |
||
861 | 20 | public function jsonSerialize() { |
|
870 | |||
871 | /** |
||
872 | * Look up a type based on its alias. |
||
873 | * |
||
874 | * @param string $alias The type alias or type name to lookup. |
||
875 | * @return mixed |
||
876 | */ |
||
877 | 9 | private function getType($alias) { |
|
887 | |||
888 | /** |
||
889 | * Look up a value in array. |
||
890 | * |
||
891 | * @param string|int $key The array key. |
||
892 | * @param array $arr The array to search. |
||
893 | * @param mixed $default The default if key is not found. |
||
894 | * @return mixed Returns the array value or the default. |
||
895 | */ |
||
896 | 17 | private static function val($key, array $arr, $default = null) { |
|
899 | } |
||
900 |
This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.
To visualize
will produce issues in the first and second line, while this second example
will produce no issues.