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 Image 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 Image, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 10 | final class Image |
||
| 11 | { |
||
| 12 | const CROP_ENTROPY = 'Entropy'; |
||
| 13 | const CROP_BALANCED = 'Balanced'; |
||
| 14 | const CROP_FACE = 'Face'; |
||
| 15 | |||
| 16 | private $adapter; |
||
| 17 | private $filename; |
||
| 18 | private $clientHints = [ |
||
| 19 | 'dpr' => null, |
||
| 20 | 'viewport-width' => null, |
||
| 21 | 'width' => null, |
||
| 22 | ]; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Create a new Imagecow instance from an image file. |
||
| 26 | */ |
||
| 27 | public static function fromFile(string $filename, string $adapter = null): Image |
||
| 46 | |||
| 47 | /** |
||
| 48 | * Create a new Imagecow instance from a string. |
||
| 49 | */ |
||
| 50 | public static function fromString(string $string, string $adapter = null): Image |
||
| 72 | |||
| 73 | /** |
||
| 74 | * Constructor. |
||
| 75 | */ |
||
| 76 | public function __construct(AdapterInterface $adapter, string $filename = null) |
||
| 81 | |||
| 82 | /** |
||
| 83 | * Set the available client hints. |
||
| 84 | */ |
||
| 85 | public function setClientHints(array $clientHints): self |
||
| 101 | |||
| 102 | /** |
||
| 103 | * Set a default background color used to fill in some transformation functions |
||
| 104 | * in rgb format, for example: array(0,127,34) |
||
| 105 | */ |
||
| 106 | public function setBackground(array $background): self |
||
| 112 | |||
| 113 | /** |
||
| 114 | * Get the fixed size according with the client hints. |
||
| 115 | */ |
||
| 116 | private function calculateClientSize(int $width, int $height): array |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Inverts the image vertically. |
||
| 136 | */ |
||
| 137 | public function flip(): self |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Inverts the image horizontally. |
||
| 146 | */ |
||
| 147 | public function flop(): self |
||
| 153 | |||
| 154 | /** |
||
| 155 | * Saves the image in a file (or override the previous opened file). |
||
| 156 | */ |
||
| 157 | public function save(string $filename = null): self |
||
| 163 | |||
| 164 | public function getAdapter(): AdapterInterface |
||
| 168 | |||
| 169 | /** |
||
| 170 | * Gets the image data in a string. |
||
| 171 | */ |
||
| 172 | public function getString(): string |
||
| 176 | |||
| 177 | /** |
||
| 178 | * Gets the mime type of the image. |
||
| 179 | */ |
||
| 180 | public function getMimeType(): string |
||
| 184 | |||
| 185 | /** |
||
| 186 | * Gets the width of the image in pixels. |
||
| 187 | */ |
||
| 188 | public function getWidth(): int |
||
| 192 | |||
| 193 | /** |
||
| 194 | * Gets the height of the image in pixels. |
||
| 195 | */ |
||
| 196 | public function getHeight(): int |
||
| 200 | |||
| 201 | /** |
||
| 202 | * Converts the image to other format (png, jpg, gif or webp). |
||
| 203 | */ |
||
| 204 | public function format($format): self |
||
| 210 | |||
| 211 | /** |
||
| 212 | * Resizes the image maintaining the proportion (A 800x600 image resized to 400x400 becomes to 400x300). |
||
| 213 | * |
||
| 214 | * @param int|string $width The max width of the image. It can be a number (pixels) or percentaje |
||
| 215 | * @param int|string $height The max height of the image. It can be a number (pixels) or percentaje |
||
| 216 | */ |
||
| 217 | public function resize($width, $height = 0, bool $cover = false): self |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Crops the image. |
||
| 239 | * |
||
| 240 | * @param int|string $width The new width of the image. It can be a number (pixels) or percentaje |
||
| 241 | * @param int|string $height The new height of the image. It can be a number (pixels) or percentaje |
||
| 242 | * @param int|string $x The "x" position to crop. It can be number (pixels), percentaje, [left, center, right] or one of the Image::CROP_* constants |
||
| 243 | * @param int|string $y The "y" position to crop. It can be number (pixels), percentaje or [top, middle, bottom] |
||
| 244 | */ |
||
| 245 | public function crop($width, $height, $x = 'center', $y = 'middle'): self |
||
| 266 | |||
| 267 | /** |
||
| 268 | * Adjust the image to the given dimmensions. Resizes and crops the image maintaining the proportions. |
||
| 269 | * |
||
| 270 | * @param int|string $width The new width in number (pixels) or percentaje |
||
| 271 | * @param int|string $height The new height in number (pixels) or percentaje |
||
| 272 | * @param int|string $x The "x" position to crop. It can be number (pixels), percentaje, [left, center, right] or one of the Image::CROP_* constants |
||
| 273 | * @param int|string $y The "y" position to crop. It can be number (pixels), percentaje or [top, middle, bottom] |
||
| 274 | */ |
||
| 275 | public function resizeCrop($width, $height, $x = 'center', $y = 'middle'): self |
||
| 282 | |||
| 283 | /** |
||
| 284 | * Rotates the image (in degrees, anticlockwise). |
||
| 285 | */ |
||
| 286 | public function rotate(int $angle): self |
||
| 294 | |||
| 295 | /** |
||
| 296 | * Apply blur to image |
||
| 297 | */ |
||
| 298 | public function blur(int $loops = 4): self |
||
| 304 | |||
| 305 | /** |
||
| 306 | * Define the image compression quality for jpg images (from 0 to 100). |
||
| 307 | */ |
||
| 308 | public function quality(int $quality): self |
||
| 322 | |||
| 323 | /** |
||
| 324 | * Add a watermark to current image. |
||
| 325 | * |
||
| 326 | * @param mixed $x Horizontal position |
||
| 327 | * @param mixed $y Vertical position |
||
| 328 | */ |
||
| 329 | public function watermark(Image $image, $x = 'right', $y = 'bottom'): self |
||
| 344 | |||
| 345 | /** |
||
| 346 | * Add opacity to image from 0 (transparent) to 100 (opaque). |
||
| 347 | */ |
||
| 348 | public function opacity(int $opacity): self |
||
| 354 | |||
| 355 | /** |
||
| 356 | * Set the image progressive or not |
||
| 357 | */ |
||
| 358 | public function progressive(bool $progressive = true): self |
||
| 364 | |||
| 365 | /** |
||
| 366 | * Reads the EXIF data from a JPEG and returns an associative array |
||
| 367 | * (requires the exif PHP extension enabled). |
||
| 368 | * |
||
| 369 | * @param null|string $key |
||
| 370 | * |
||
| 371 | * @return null|array |
||
| 372 | */ |
||
| 373 | public function getExifData($key = null) |
||
| 385 | |||
| 386 | /** |
||
| 387 | * Transform the image executing various operations of crop, resize, resizeCrop and format. |
||
| 388 | */ |
||
| 389 | public function transform(string $operations = null): self |
||
| 428 | |||
| 429 | /** |
||
| 430 | * Send the HTTP header with the content-type, output the image data and die. |
||
| 431 | */ |
||
| 432 | View Code Duplication | public function show() |
|
| 439 | |||
| 440 | /** |
||
| 441 | * Returns the image as base64 url. |
||
| 442 | * |
||
| 443 | * @return string|null |
||
| 444 | */ |
||
| 445 | View Code Duplication | public function base64() |
|
| 453 | |||
| 454 | /** |
||
| 455 | * Auto-rotate the image according with its exif data |
||
| 456 | * Taken from: http://php.net/manual/en/function.exif-read-data.php#76964. |
||
| 457 | */ |
||
| 458 | public function autoRotate(): self |
||
| 492 | |||
| 493 | /** |
||
| 494 | * Check whether the image is an animated gif. |
||
| 495 | * Copied from: https://github.com/Sybio/GifFrameExtractor/blob/master/src/GifFrameExtractor/GifFrameExtractor.php#L181. |
||
| 496 | * |
||
| 497 | * @param resource A stream pointer opened by fopen() |
||
| 498 | */ |
||
| 499 | private static function isAnimatedGif($stream): bool |
||
| 510 | |||
| 511 | /** |
||
| 512 | * Converts a string with operations in an array. |
||
| 513 | */ |
||
| 514 | private static function parseOperations(string $operations): array |
||
| 536 | |||
| 537 | /** |
||
| 538 | * Checks the library to use and returns its class. |
||
| 539 | * |
||
| 540 | * @throws ImageException if the image library does not exists. |
||
| 541 | */ |
||
| 542 | private static function getAdapterClass(string $adapter = null): string |
||
| 558 | } |
||
| 559 |
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.