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 JSONText 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 JSONText, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 35 | class JSONText extends \StringField | ||
| 36 | { | ||
| 37 | /** | ||
| 38 | * @var int | ||
| 39 | */ | ||
| 40 | const JSONTEXT_QUERY_OPERATOR = 1; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * @var int | ||
| 44 | */ | ||
| 45 | const JSONTEXT_QUERY_JSONPATH = 2; | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Which RDBMS backend are we using? The value set here changes the actual operators and operator-routines for the | ||
| 49 | * given backend. | ||
| 50 | * | ||
| 51 | * @var string | ||
| 52 | * @config | ||
| 53 | */ | ||
| 54 | private static $backend = 'postgres'; | ||
| 1 ignored issue–
                            show | |||
| 55 | |||
| 56 | /** | ||
| 57 | * @var array | ||
| 58 | * @config | ||
| 59 | * | ||
| 60 | * [<backend>] => [ | ||
| 61 | * [<method> => <operator>] | ||
| 62 | * ]; // For use in query() method. | ||
| 63 | */ | ||
| 64 | private static $allowed_operators = [ | ||
| 65 | 'postgres' => [ | ||
| 66 | 'matchOnInt' => '->', | ||
| 67 | 'matchOnStr' => '->>', | ||
| 68 | 'matchOnPath' => '#>' | ||
| 69 | ] | ||
| 70 | ]; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Legitimate query return types. | ||
| 74 | * | ||
| 75 | * @var array | ||
| 76 | */ | ||
| 77 | private static $return_types = [ | ||
| 78 | 'json', 'array', 'silverstripe' | ||
| 79 | ]; | ||
| 80 | |||
| 81 | /** | ||
| 82 | * @var string | ||
| 83 | */ | ||
| 84 | protected $returnType = 'json'; | ||
| 85 | |||
| 86 | /** | ||
| 87 | * @var \Peekmo\JsonPath\JsonStore | ||
| 88 | */ | ||
| 89 | protected $jsonStore; | ||
| 90 | |||
| 91 | /** | ||
| 92 |      * Taken from {@link TextField}. | ||
| 93 | * | ||
| 94 | * @see DBField::requireField() | ||
| 95 | * @return void | ||
| 96 | */ | ||
| 97 | public function requireField() | ||
| 113 | |||
| 114 | /** | ||
| 115 | * @param string $title | ||
| 116 | * @return HiddenField | ||
| 117 | */ | ||
| 118 | public function scaffoldSearchField($title = null) | ||
| 122 | |||
| 123 | /** | ||
| 124 | * @param string $title | ||
| 125 | * @return HiddenField | ||
| 126 | */ | ||
| 127 | public function scaffoldFormField($title = null) | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Tell all class methods to return data as JSON , an array or an array of SilverStripe DBField subtypes. | ||
| 134 | * | ||
| 135 | * @param string $type | ||
| 136 | * @return \JSONText | ||
| 137 | * @throws \JSONText\Exceptions\JSONTextException | ||
| 138 | */ | ||
| 139 | public function setReturnType($type) | ||
| 150 | |||
| 151 | /** | ||
| 152 | * @return string | ||
| 153 | */ | ||
| 154 | public function getReturnType() | ||
| 158 | |||
| 159 | /** | ||
| 160 | * Returns the value of this field as an iterable. | ||
| 161 | * | ||
| 162 | * @return \Peekmo\JsonPath\JsonStore | ||
| 163 | * @throws \JSONText\Exceptions\JSONTextException | ||
| 164 | */ | ||
| 165 | public function getJSONStore() | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Returns the JSON value of this field as an array. | ||
| 183 | * | ||
| 184 | * @return array | ||
| 185 | */ | ||
| 186 | public function getStoreAsArray() | ||
| 195 | |||
| 196 | /** | ||
| 197 | * Utility method to determine whether the data is really JSON or not. | ||
| 198 | * | ||
| 199 | * @param string $value | ||
| 200 | * @return boolean | ||
| 201 | */ | ||
| 202 | public function isJson($value) | ||
| 206 | |||
| 207 | /** | ||
| 208 | * Convert an array to JSON via json_encode(). | ||
| 209 | * | ||
| 210 | * @param array $value | ||
| 211 | * @return mixed null|string | ||
| 212 | */ | ||
| 213 | public function toJson(array $value) | ||
| 225 | |||
| 226 | /** | ||
| 227 | * Convert an array's values into an array of SilverStripe DBField subtypes ala: | ||
| 228 | * | ||
| 229 |      * - {@link Int} | ||
| 230 |      * - {@link Float} | ||
| 231 |      * - {@link Boolean} | ||
| 232 |      * - {@link Varchar} | ||
| 233 | * | ||
| 234 | * @param array $data | ||
| 235 | * @return array | ||
| 236 | */ | ||
| 237 | public function toSSTypes(array $data) | ||
| 250 | |||
| 251 | /** | ||
| 252 | * @param mixed $value | ||
| 253 | * @return array | ||
| 254 | * @throws \JSONText\Exceptions\JSONTextException | ||
| 255 | */ | ||
| 256 | public function toArray($value) | ||
| 267 | |||
| 268 | /** | ||
| 269 | * Return an array of the JSON key + value represented as first (top-level) JSON node. | ||
| 270 | * | ||
| 271 | * @return array | ||
| 272 | */ | ||
| 273 | public function first() | ||
| 286 | |||
| 287 | /** | ||
| 288 | * Return an array of the JSON key + value represented as last JSON node. | ||
| 289 | * | ||
| 290 | * @return array | ||
| 291 | */ | ||
| 292 | public function last() | ||
| 306 | |||
| 307 | /** | ||
| 308 | * Return an array of the JSON key + value represented as the $n'th JSON node. | ||
| 309 | * | ||
| 310 | * @param int $n | ||
| 311 | * @return mixed array | ||
| 312 | * @throws \JSONText\Exceptions\JSONTextException | ||
| 313 | */ | ||
| 314 | public function nth($n) | ||
| 337 | |||
| 338 | /** | ||
| 339 | * Return the key(s) + value(s) represented by $operator extracting relevant result from the source JSON's structure. | ||
| 340 | * N.b when using the path match operator '#>' with duplicate keys, an indexed array of results is returned. | ||
| 341 | * | ||
| 342 | * @param string $operator One of the legitimate operators for the current backend or a valid JSONPath expression. | ||
| 343 | * @param string $operand | ||
| 344 | * @return mixed null|array | ||
| 345 | * @throws \JSONText\Exceptions\JSONTextException | ||
| 346 | */ | ||
| 347 | public function query($operator, $operand = null) | ||
| 375 | |||
| 376 | /** | ||
| 377 | * Based on the passed operator or expression, ensure the correct backend matcher method is called. | ||
| 378 | * | ||
| 379 | * @param array $args | ||
| 380 | * @param integer $type | ||
| 381 | * @return array | ||
| 382 | * @throws \JSONText\Exceptions\JSONTextException | ||
| 383 | */ | ||
| 384 | private function marshallQuery($args, $type = 1) | ||
| 420 | |||
| 421 | /** | ||
| 422 | * Same as standard setValue() method except we can also accept a JSONPath expression. This expression will | ||
| 423 | * conditionally update the parts of the field's source JSON referenced by $expr with $value | ||
| 424 | * then re-set the entire JSON string as the field's new value. | ||
| 425 | * | ||
| 426 | * @param mixed $value | ||
| 427 | * @param array $record | ||
| 428 | * @param string $expr A valid JSONPath expression. | ||
| 429 | * @return JSONText | ||
| 430 | * @throws JSONTextException | ||
| 431 | */ | ||
| 432 | public function setValue($value, $record = null, $expr = null) | ||
| 455 | |||
| 456 | /** | ||
| 457 | * Determine the desired userland format to return all query API method results in. | ||
| 458 | * | ||
| 459 | * @param mixed | ||
| 460 | * @return mixed | ||
| 461 | * @throws \JSONText\Exceptions\JSONTextException | ||
| 462 | */ | ||
| 463 | private function returnAsType($data) | ||
| 494 | |||
| 495 | /** | ||
| 496 | * Is the passed JSON operator valid? | ||
| 497 | * | ||
| 498 | * @param string $operator | ||
| 499 | * @return boolean | ||
| 500 | */ | ||
| 501 | private function isValidOperator($operator) | ||
| 511 | |||
| 512 | /** | ||
| 513 | * @param string $arg | ||
| 514 | * @return bool | ||
| 515 | */ | ||
| 516 | private function isExpression($arg) | ||
| 520 | |||
| 521 | /** | ||
| 522 | * @param string $arg | ||
| 523 | * @return bool | ||
| 524 | */ | ||
| 525 | public function isOperator($arg) | ||
| 529 | |||
| 530 | /** | ||
| 531 | * Is the passed JSON expression valid? | ||
| 532 | * | ||
| 533 | * @param string $expr | ||
| 534 | * @return boolean | ||
| 535 | */ | ||
| 536 | public function isValidExpression($expr) | ||
| 540 | |||
| 541 | /** | ||
| 542 |      * Casts a value to a {@link DBField} subclass. | ||
| 543 | * | ||
| 544 | * @param mixed $val | ||
| 545 | * @return mixed DBField|array | ||
| 546 | */ | ||
| 547 | private function castToDBField($val) | ||
| 563 | |||
| 564 | } | ||
| 565 | |||
| 576 | 
This check marks private properties in classes that are never used. Those properties can be removed.