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.