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 AbstractPropertyInput 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 AbstractPropertyInput, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 25 | abstract class AbstractPropertyInput implements |
||
| 26 | PropertyInputInterface, |
||
| 27 | LoggerAwareInterface |
||
| 28 | { |
||
| 29 | use LoggerAwareTrait; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * @var string $lang |
||
| 33 | */ |
||
| 34 | private $lang; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * @var string $ident |
||
| 38 | */ |
||
| 39 | private $ident; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * @var boolean $readOnly |
||
| 43 | */ |
||
| 44 | private $readOnly; |
||
| 45 | /** |
||
| 46 | * @var boolean $required |
||
| 47 | */ |
||
| 48 | private $required; |
||
| 49 | /** |
||
| 50 | * @var boolean $disabled |
||
| 51 | */ |
||
| 52 | private $disabled; |
||
| 53 | /** |
||
| 54 | * @var boolean $multiple |
||
| 55 | */ |
||
| 56 | private $multiple; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * @var string $type |
||
| 60 | */ |
||
| 61 | protected $type; |
||
| 62 | /** |
||
| 63 | * @var string $inputType |
||
| 64 | */ |
||
| 65 | protected $inputType; |
||
| 66 | |||
| 67 | /** |
||
| 68 | * @var string $inputId |
||
| 69 | */ |
||
| 70 | protected $inputId; |
||
| 71 | /** |
||
| 72 | * @var string $inputClass |
||
| 73 | */ |
||
| 74 | protected $inputClass = ''; |
||
| 75 | |||
| 76 | /** |
||
| 77 | * @var array $propertyData |
||
| 78 | */ |
||
| 79 | private $propertyData = []; |
||
| 80 | |||
| 81 | private $propertyVal; |
||
| 82 | |||
| 83 | /** |
||
| 84 | * @var PropertyInterface $property |
||
| 85 | */ |
||
| 86 | private $property; |
||
| 87 | |||
| 88 | /** |
||
| 89 | * @param array|\ArrayAccess $data Constructor data. |
||
| 90 | */ |
||
| 91 | View Code Duplication | public function __construct($data = null) |
|
| 98 | |||
| 99 | /** |
||
| 100 | * This function takes an array and fill the model object with its value. |
||
| 101 | * |
||
| 102 | * This method either calls a setter for each key (`set_{$key}()`) or sets a public member. |
||
| 103 | * |
||
| 104 | * For example, calling with `setData(['properties'=>$properties])` would call |
||
| 105 | * `setProperties($properties)`, becasue `setProperties()` exists. |
||
| 106 | * |
||
| 107 | * But calling with `setData(['foobar'=>$foo])` would set the `$foobar` member |
||
| 108 | * on the metadata object, because the method `set_foobar()` does not exist. |
||
| 109 | * |
||
| 110 | * @param array $data The input data. |
||
| 111 | * @return Input Chainable |
||
| 112 | */ |
||
| 113 | View Code Duplication | public function setData(array $data) |
|
| 129 | |||
| 130 | public function setPropertyVal($val) |
||
| 135 | |||
| 136 | public function propertyVal() |
||
| 140 | |||
| 141 | /** |
||
| 142 | * @ |
||
| 143 | */ |
||
| 144 | public function setLang($lang) |
||
| 149 | |||
| 150 | public function lang() |
||
| 157 | |||
| 158 | /** |
||
| 159 | * @param string $ident Input identifier. |
||
| 160 | * @throws InvalidArgumentException If the ident is not a string. |
||
| 161 | * @return Widget Chainable |
||
| 162 | */ |
||
| 163 | View Code Duplication | public function setIdent($ident) |
|
| 173 | |||
| 174 | /** |
||
| 175 | * @return string |
||
| 176 | */ |
||
| 177 | public function ident() |
||
| 181 | |||
| 182 | /** |
||
| 183 | * @param boolean $readOnly The read-only flag. |
||
| 184 | * @return Widget (Chainable) |
||
| 185 | */ |
||
| 186 | public function setReadOnly($readOnly) |
||
| 191 | |||
| 192 | /** |
||
| 193 | * @return boolean |
||
| 194 | */ |
||
| 195 | public function readOnly() |
||
| 199 | |||
| 200 | /** |
||
| 201 | * @param boolean $required Required flag. |
||
| 202 | * @return Widget (Chainable) |
||
| 203 | */ |
||
| 204 | public function setRequired($required) |
||
| 209 | |||
| 210 | /** |
||
| 211 | * @return boolean |
||
| 212 | */ |
||
| 213 | public function required() |
||
| 217 | |||
| 218 | |||
| 219 | /** |
||
| 220 | * @param boolean $disabled Disabled flag. |
||
| 221 | * @return Widget (Chainable) |
||
| 222 | */ |
||
| 223 | public function setDisabled($disabled) |
||
| 228 | |||
| 229 | /** |
||
| 230 | * @return boolean |
||
| 231 | */ |
||
| 232 | public function disabled() |
||
| 236 | |||
| 237 | /** |
||
| 238 | * @param boolean $multiple Multiple flag. |
||
| 239 | * @return Widget (Chainable) |
||
| 240 | */ |
||
| 241 | public function setMultiple($multiple) |
||
| 246 | |||
| 247 | /** |
||
| 248 | * @return boolean |
||
| 249 | */ |
||
| 250 | public function multiple() |
||
| 254 | |||
| 255 | /** |
||
| 256 | * @param string $inputId HTML input id attribute. |
||
| 257 | * @return Input Chainable |
||
| 258 | */ |
||
| 259 | public function setInputId($inputId) |
||
| 264 | |||
| 265 | /** |
||
| 266 | * Get the input ID. |
||
| 267 | * |
||
| 268 | * If none was previously set, than a unique random one will be generated. |
||
| 269 | * |
||
| 270 | * @return string |
||
| 271 | */ |
||
| 272 | public function inputId() |
||
| 279 | |||
| 280 | /** |
||
| 281 | * @param string $inputClass The input class attribute. |
||
| 282 | * @throws InvalidArgumentException If the class is not a string. |
||
| 283 | * @return AbstractPropertyInput Chainable |
||
| 284 | */ |
||
| 285 | public function setInputClass($inputClass) |
||
| 295 | |||
| 296 | /** |
||
| 297 | * @return string |
||
| 298 | */ |
||
| 299 | public function inputClass() |
||
| 303 | |||
| 304 | /** |
||
| 305 | * The input name should always be the property's ident. |
||
| 306 | * |
||
| 307 | * @return string |
||
| 308 | */ |
||
| 309 | View Code Duplication | public function inputName() |
|
| 320 | |||
| 321 | /** |
||
| 322 | * @uses AbstractProperty::inputVal() Must handle string sanitization of value. |
||
| 323 | * @throws Exception If the value is invalid. |
||
| 324 | * @return string |
||
| 325 | */ |
||
| 326 | public function inputVal() |
||
| 346 | |||
| 347 | /** |
||
| 348 | * @param string $inputType The input type. |
||
| 349 | * @throws InvalidArgumentException If provided argument is not of type 'string'. |
||
| 350 | * @return AbstractPropertyInput Chainable |
||
| 351 | */ |
||
| 352 | public function setInputType($inputType) |
||
| 362 | |||
| 363 | /** |
||
| 364 | * @return string |
||
| 365 | */ |
||
| 366 | public function inputType() |
||
| 373 | |||
| 374 | /** |
||
| 375 | * @param PropertyInterface $p The property. |
||
| 376 | * @return AbstractPropertyInput Chainable |
||
| 377 | */ |
||
| 378 | public function setProperty(PropertyInterface $p) |
||
| 383 | |||
| 384 | /** |
||
| 385 | * @return PropertyInterface |
||
| 386 | */ |
||
| 387 | public function property() |
||
| 391 | |||
| 392 | /** |
||
| 393 | * Alias of the `property` method. |
||
| 394 | * |
||
| 395 | * @return PropertyInterface |
||
| 396 | */ |
||
| 397 | public function p() |
||
| 401 | |||
| 402 | /** |
||
| 403 | * Allow an object to define how the key getter are called. |
||
| 404 | * |
||
| 405 | * @param string $key The key to get the getter from. |
||
| 406 | * @return string The getter method name, for a given key. |
||
| 407 | */ |
||
| 408 | protected function getter($key) |
||
| 413 | |||
| 414 | /** |
||
| 415 | * Allow an object to define how the key setter are called. |
||
| 416 | * |
||
| 417 | * @param string $key The key to get the setter from. |
||
| 418 | * @return string The setter method name, for a given key. |
||
| 419 | */ |
||
| 420 | protected function setter($key) |
||
| 426 | |||
| 427 | /** |
||
| 428 | * Transform a snake_case string to camelCase. |
||
| 429 | * |
||
| 430 | * @param string $str The snake_case string to camelize. |
||
| 431 | * @return string The camelCase string. |
||
| 432 | */ |
||
| 433 | private function camelize($str) |
||
| 437 | } |
||
| 438 |
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.