| Total Complexity | 43 |
| Total Lines | 331 |
| Duplicated Lines | 10.88 % |
| Coverage | 100% |
| Changes | 0 | ||
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 AbstractDaftObject 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.
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 AbstractDaftObject, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 17 | abstract class AbstractDaftObject implements DaftObject |
||
| 18 | { |
||
| 19 | /** |
||
| 20 | * List of properties that can be defined on an implementation. |
||
| 21 | * |
||
| 22 | * @var string[] |
||
| 23 | */ |
||
| 24 | const PROPERTIES = []; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * List of nullable properties that can be defined on an implementation. |
||
| 28 | * |
||
| 29 | * @var string[] |
||
| 30 | */ |
||
| 31 | const NULLABLE_PROPERTIES = []; |
||
| 32 | |||
| 33 | /** |
||
| 34 | * List of exportable properties that can be defined on an implementation. |
||
| 35 | * |
||
| 36 | * @var string[] |
||
| 37 | */ |
||
| 38 | const EXPORTABLE_PROPERTIES = []; |
||
| 39 | |||
| 40 | /** |
||
| 41 | * import/export definition for DaftJson. |
||
| 42 | */ |
||
| 43 | const JSON_PROPERTIES = []; |
||
| 44 | |||
| 45 | /** |
||
| 46 | * @var string[][] |
||
| 47 | */ |
||
| 48 | private static $publicGetters = []; |
||
| 49 | |||
| 50 | /** |
||
| 51 | * @var string[][] |
||
| 52 | */ |
||
| 53 | private static $publicSetters = []; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * Does some sanity checking. |
||
| 57 | * |
||
| 58 | * @see DefinesOwnIdPropertiesInterface |
||
| 59 | * @see self::CheckTypeDefinesOwnIdProperties() |
||
| 60 | */ |
||
| 61 | 187 | public function __construct() |
|
| 62 | { |
||
| 63 | if ( |
||
| 64 | 187 | ($this instanceof DefinesOwnIdPropertiesInterface) |
|
| 65 | ) { |
||
| 66 | 116 | self::CheckTypeDefinesOwnIdProperties($this); |
|
| 67 | } |
||
| 68 | 185 | } |
|
| 69 | |||
| 70 | /** |
||
| 71 | * {@inheritdoc} |
||
| 72 | */ |
||
| 73 | 40 | public function __get(string $property) |
|
| 74 | { |
||
| 75 | 40 | return $this->DoGetSet( |
|
| 76 | 40 | $property, |
|
| 77 | 40 | false, |
|
| 78 | 40 | NotPublicGetterPropertyException::class |
|
| 79 | ); |
||
| 80 | } |
||
| 81 | |||
| 82 | /** |
||
| 83 | * {@inheritdoc} |
||
| 84 | */ |
||
| 85 | 79 | public function __set(string $property, $v) |
|
| 86 | { |
||
| 87 | 79 | return $this->DoGetSet( |
|
| 88 | 79 | $property, |
|
| 89 | 79 | true, |
|
| 90 | 79 | NotPublicSetterPropertyException::class, |
|
| 91 | 79 | $v |
|
| 92 | ); |
||
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * {@inheritdoc} |
||
| 97 | * |
||
| 98 | * @see static::NudgePropertyValue() |
||
| 99 | */ |
||
| 100 | 20 | public function __unset(string $property) : void |
|
| 101 | { |
||
| 102 | 20 | $this->NudgePropertyValue($property, null); |
|
| 103 | 18 | } |
|
| 104 | |||
| 105 | /** |
||
| 106 | * {@inheritdoc} |
||
| 107 | */ |
||
| 108 | 31 | public function __debugInfo() : array |
|
| 109 | { |
||
| 110 | 31 | $out = []; |
|
| 111 | 31 | $publicGetters = static::DaftObjectPublicGetters(); |
|
| 112 | 31 | foreach (static::DaftObjectExportableProperties() as $prop) { |
|
| 113 | 30 | $expectedMethod = 'Get' . ucfirst($prop); |
|
| 114 | if ( |
||
| 115 | 30 | $this->__isset($prop) && |
|
| 116 | 30 | in_array($prop, $publicGetters, true) |
|
| 117 | ) { |
||
| 118 | 24 | $out[$prop] = $this->$expectedMethod(); |
|
| 119 | } |
||
| 120 | } |
||
| 121 | |||
| 122 | 31 | return $out; |
|
| 123 | } |
||
| 124 | |||
| 125 | /** |
||
| 126 | * List of properties that can be defined on an implementation. |
||
| 127 | * |
||
| 128 | * @return string[] |
||
| 129 | */ |
||
| 130 | 221 | final public static function DaftObjectProperties() : array |
|
| 131 | { |
||
| 132 | 221 | return static::PROPERTIES; |
|
| 133 | } |
||
| 134 | |||
| 135 | /** |
||
| 136 | * {@inheritdoc} |
||
| 137 | */ |
||
| 138 | 65 | final public static function DaftObjectNullableProperties() : array |
|
| 139 | { |
||
| 140 | 65 | return static::NULLABLE_PROPERTIES; |
|
| 141 | } |
||
| 142 | |||
| 143 | /** |
||
| 144 | * {@inheritdoc} |
||
| 145 | */ |
||
| 146 | 65 | final public static function DaftObjectExportableProperties() : array |
|
| 147 | { |
||
| 148 | 65 | return static::EXPORTABLE_PROPERTIES; |
|
| 149 | } |
||
| 150 | |||
| 151 | /** |
||
| 152 | * {@inheritdoc} |
||
| 153 | */ |
||
| 154 | 52 | final public static function DaftObjectPublicGetters() : array |
|
| 155 | { |
||
| 156 | 52 | static::CachePublicGettersAndSetters(); |
|
| 157 | |||
| 158 | 52 | return self::$publicGetters[static::class]; |
|
| 159 | } |
||
| 160 | |||
| 161 | /** |
||
| 162 | * {@inheritdoc} |
||
| 163 | */ |
||
| 164 | 77 | final public static function DaftObjectPublicSetters() : array |
|
| 165 | { |
||
| 166 | 77 | static::CachePublicGettersAndSetters(); |
|
| 167 | |||
| 168 | 77 | return self::$publicSetters[static::class]; |
|
| 169 | } |
||
| 170 | |||
| 171 | /** |
||
| 172 | * {@inheritdoc} |
||
| 173 | */ |
||
| 174 | 34 | View Code Duplication | final public static function DaftObjectJsonProperties() : array |
|
|
|||
| 175 | { |
||
| 176 | 34 | if (false === is_a(static::class, DaftJson::class, true)) { |
|
| 177 | 16 | throw new DaftObjectNotDaftJsonBadMethodCallException( |
|
| 178 | 16 | static::class |
|
| 179 | ); |
||
| 180 | } |
||
| 181 | |||
| 182 | 18 | return static::JSON_PROPERTIES; |
|
| 183 | } |
||
| 184 | |||
| 185 | 104 | protected static function CachePublicGettersAndSetters() : void |
|
| 186 | { |
||
| 187 | 104 | $refreshGetters = false === isset(self::$publicGetters[static::class]); |
|
| 188 | 104 | $refreshSetters = false === isset(self::$publicSetters[static::class]); |
|
| 189 | |||
| 190 | 104 | if ($refreshGetters) { |
|
| 191 | 12 | self::$publicGetters[static::class] = []; |
|
| 192 | |||
| 193 | if ( |
||
| 194 | 12 | is_a( |
|
| 195 | 12 | static::class, |
|
| 196 | 12 | DefinesOwnIdPropertiesInterface::class, |
|
| 197 | 12 | true |
|
| 198 | ) |
||
| 199 | ) { |
||
| 200 | 7 | self::$publicGetters[static::class][] = 'id'; |
|
| 201 | } |
||
| 202 | } |
||
| 203 | |||
| 204 | 104 | if ($refreshSetters) { |
|
| 205 | 12 | self::$publicSetters[static::class] = []; |
|
| 206 | } |
||
| 207 | |||
| 208 | 104 | if ($refreshGetters || $refreshSetters) { |
|
| 209 | 12 | $classReflection = new ReflectionClass(static::class); |
|
| 210 | |||
| 211 | 12 | foreach (static::DaftObjectProperties() as $property) { |
|
| 212 | 12 | $getter = 'Get' . ucfirst($property); |
|
| 213 | |||
| 214 | 12 | $setter = 'Set' . ucfirst($property); |
|
| 215 | |||
| 216 | View Code Duplication | if ( |
|
| 217 | 12 | $refreshGetters && |
|
| 218 | 12 | $classReflection->hasMethod($getter) && |
|
| 219 | ( |
||
| 220 | 10 | $methodReflection = new ReflectionMethod( |
|
| 221 | 10 | static::class, |
|
| 222 | 10 | $getter |
|
| 223 | ) |
||
| 224 | 10 | )->isPublic() && |
|
| 225 | 10 | false === $methodReflection->isStatic() |
|
| 226 | ) { |
||
| 227 | 10 | self::$publicGetters[static::class][] = $property; |
|
| 228 | } |
||
| 229 | |||
| 230 | View Code Duplication | if ( |
|
| 231 | 12 | $refreshSetters && |
|
| 232 | 12 | $classReflection->hasMethod($setter) && |
|
| 233 | ( |
||
| 234 | 10 | $methodReflection = new ReflectionMethod( |
|
| 235 | 10 | static::class, |
|
| 236 | 10 | $setter |
|
| 237 | ) |
||
| 238 | 10 | )->isPublic() && |
|
| 239 | 10 | false === $methodReflection->isStatic() |
|
| 240 | ) { |
||
| 241 | 10 | self::$publicSetters[static::class][] = $property; |
|
| 242 | } |
||
| 243 | } |
||
| 244 | } |
||
| 245 | 104 | } |
|
| 246 | |||
| 247 | /** |
||
| 248 | * Nudge the state of a given property, marking it as dirty. |
||
| 249 | * |
||
| 250 | * @param string $property property being nudged |
||
| 251 | * @param mixed $value value to nudge property with |
||
| 252 | * |
||
| 253 | * @throws UndefinedPropertyException if $property is not in static::DaftObjectProperties() |
||
| 254 | * @throws PropertyNotNullableException if $property is not in static::DaftObjectNullableProperties() |
||
| 255 | * @throws PropertyNotRewriteableException if class is write-once read-many and $property was already changed |
||
| 256 | */ |
||
| 257 | abstract protected function NudgePropertyValue( |
||
| 258 | string $property, |
||
| 259 | $value |
||
| 260 | ) : void; |
||
| 261 | |||
| 262 | /** |
||
| 263 | * Checks if a type correctly defines it's own id. |
||
| 264 | * |
||
| 265 | * @param DaftObject $object |
||
| 266 | * |
||
| 267 | * @throws ClassDoesNotImplementClassException if $object is not an implementation of DefinesOwnIdPropertiesInterface |
||
| 268 | * @throws ClassMethodReturnHasZeroArrayCountException if $object::DaftObjectIdProperties() does not contain at least one property |
||
| 269 | * @throws ClassMethodReturnIsNotArrayOfStringsException if $object::DaftObjectIdProperties() is not string[] |
||
| 270 | * @throws UndefinedPropertyException if an id property is not in $object::DaftObjectIdProperties() |
||
| 271 | */ |
||
| 272 | 117 | final protected static function CheckTypeDefinesOwnIdProperties( |
|
| 273 | DaftObject $object |
||
| 274 | ) : void { |
||
| 275 | 117 | $class = get_class($object); |
|
| 276 | 117 | if (false === ($object instanceof DefinesOwnIdPropertiesInterface)) { |
|
| 277 | 1 | throw new ClassDoesNotImplementClassException( |
|
| 278 | 1 | $class, |
|
| 279 | 1 | DefinesOwnIdPropertiesInterface::class |
|
| 280 | ); |
||
| 281 | } |
||
| 282 | |||
| 283 | /** |
||
| 284 | * @var DefinesOwnIdPropertiesInterface $object |
||
| 285 | */ |
||
| 286 | 116 | $object = $object; |
|
| 287 | |||
| 288 | 116 | $properties = $object::DaftObjectIdProperties(); |
|
| 289 | |||
| 290 | 116 | if (count($properties) < 1) { |
|
| 291 | 1 | throw new ClassMethodReturnHasZeroArrayCountException( |
|
| 292 | 1 | $class, |
|
| 293 | 1 | 'DaftObjectIdProperties' |
|
| 294 | ); |
||
| 295 | } |
||
| 296 | |||
| 297 | 115 | foreach ($properties as $property) { |
|
| 298 | 115 | if (false === is_string($property)) { |
|
| 299 | 1 | throw new ClassMethodReturnIsNotArrayOfStringsException( |
|
| 300 | 1 | $class, |
|
| 301 | 1 | 'DaftObjectIdProperties' |
|
| 302 | ); |
||
| 303 | } |
||
| 304 | } |
||
| 305 | 114 | } |
|
| 306 | |||
| 307 | /** |
||
| 308 | * @param mixed $v |
||
| 309 | * |
||
| 310 | * @return mixed |
||
| 311 | */ |
||
| 312 | 107 | protected function DoGetSet( |
|
| 348 | } |
||
| 349 | } |
||
| 350 |
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.