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 Manipulations 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 Manipulations, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
8 | class Manipulations |
||
9 | { |
||
10 | const CROP_TOP_LEFT = 'crop-top-left'; |
||
11 | const CROP_TOP = 'crop-top'; |
||
12 | const CROP_TOP_RIGHT = 'crop-top-right'; |
||
13 | const CROP_LEFT = 'crop-left'; |
||
14 | const CROP_CENTER = 'crop-center'; |
||
15 | const CROP_RIGHT = 'crop-right'; |
||
16 | const CROP_BOTTOM_LEFT = 'crop-bottom-left'; |
||
17 | const CROP_BOTTOM = 'crop-bottom'; |
||
18 | const CROP_BOTTOM_RIGHT = 'crop-bottom-right'; |
||
19 | |||
20 | const ORIENTATION_AUTO = 'auto'; |
||
21 | const ORIENTATION_90 = 90; |
||
22 | const ORIENTATION_180 = 180; |
||
23 | const ORIENTATION_270 = 270; |
||
24 | |||
25 | const FIT_CONTAIN = 'contain'; |
||
26 | const FIT_MAX = 'max'; |
||
27 | const FIT_FILL = 'fill'; |
||
28 | const FIT_STRETCH = 'stretch'; |
||
29 | const FIT_CROP = 'crop'; |
||
30 | |||
31 | const BORDER_OVERLAY = 'overlay'; |
||
32 | const BORDER_SHRINK = 'shrink'; |
||
33 | const BORDER_EXPAND = 'expand'; |
||
34 | |||
35 | const FORMAT_JPG = 'jpg'; |
||
36 | const FORMAT_PJPG = 'pjpg'; |
||
37 | const FORMAT_PNG = 'png'; |
||
38 | const FORMAT_GIF = 'gif'; |
||
39 | |||
40 | const FILTER_GREYSCALE = 'greyscale'; |
||
41 | const FILTER_SEPIA = 'sepia'; |
||
42 | |||
43 | /** @var \Spatie\Image\ManipulationSequence */ |
||
44 | protected $manipulationSequence; |
||
45 | |||
46 | public function __construct(array $manipulations = []) |
||
50 | |||
51 | /** |
||
52 | * @param string $orientation |
||
53 | * |
||
54 | * @return $this |
||
55 | */ |
||
56 | View Code Duplication | public function orientation(string $orientation) |
|
|
|||
57 | { |
||
58 | if (! $this->validateManipulation($orientation, 'orientation')) { |
||
59 | throw InvalidManipulation::invalidParameter( |
||
60 | 'orientation', |
||
61 | $orientation, |
||
62 | $this->getValidManipulationOptions('orientation') |
||
63 | ); |
||
64 | } |
||
65 | |||
66 | return $this->addManipulation('orientation', $orientation); |
||
67 | } |
||
68 | |||
69 | /** |
||
70 | * @param string $cropMethod |
||
71 | * @param int $width |
||
72 | * @param int $height |
||
73 | * |
||
74 | * @return $this |
||
75 | */ |
||
76 | View Code Duplication | public function crop(string $cropMethod, int $width, int $height) |
|
77 | { |
||
78 | if (! $this->validateManipulation($cropMethod, 'crop')) { |
||
79 | throw InvalidManipulation::invalidParameter( |
||
80 | 'cropmethod', |
||
81 | $cropMethod, |
||
82 | $this->getValidManipulationOptions('crop') |
||
83 | ); |
||
84 | } |
||
85 | |||
86 | $this->width($width); |
||
87 | $this->height($height); |
||
88 | |||
89 | return $this->addManipulation('crop', $cropMethod); |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * @param int $width |
||
94 | * @param int $height |
||
95 | * @param int $focalX Crop center X in percent |
||
96 | * @param int $focalY Crop center Y in percent |
||
97 | * |
||
98 | * @return $this |
||
99 | */ |
||
100 | public function focalCrop(int $width, int $height, int $focalX, int $focalY) |
||
101 | { |
||
102 | $this->width($width); |
||
103 | $this->height($height); |
||
104 | |||
105 | return $this->addManipulation('crop', "crop-{$focalX}-{$focalY}"); |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * @param int $width |
||
110 | * @param int $height |
||
111 | * @param int $x |
||
112 | * @param int $y |
||
113 | * |
||
114 | * @return $this |
||
115 | */ |
||
116 | public function manualCrop(int $width, int $height, int $x, int $y) |
||
117 | { |
||
118 | if ($width < 0) { |
||
119 | throw InvalidManipulation::invalidWidth($width); |
||
120 | } |
||
121 | |||
122 | if ($height < 0) { |
||
123 | throw InvalidManipulation::invalidWidth($height); |
||
124 | } |
||
125 | |||
126 | return $this->addManipulation('manualCrop', "{$width},{$height},{$x},{$y}"); |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * @param int $width |
||
131 | * |
||
132 | * @return $this |
||
133 | */ |
||
134 | public function width(int $width) |
||
135 | { |
||
136 | if ($width < 0) { |
||
137 | throw InvalidManipulation::invalidWidth($width); |
||
138 | } |
||
139 | |||
140 | return $this->addManipulation('width', $width); |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * @param int $height |
||
145 | * |
||
146 | * @return $this |
||
147 | */ |
||
148 | public function height(int $height) |
||
149 | { |
||
150 | if ($height < 0) { |
||
151 | throw InvalidManipulation::invalidWidth($height); |
||
152 | } |
||
153 | |||
154 | return $this->addManipulation('height', $height); |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * @param string $fitMethod |
||
159 | * @param int $width |
||
160 | * @param int $height |
||
161 | * |
||
162 | * @return $this |
||
163 | */ |
||
164 | View Code Duplication | public function fit(string $fitMethod, int $width, int $height) |
|
165 | { |
||
166 | if (! $this->validateManipulation($fitMethod, 'fit')) { |
||
167 | throw InvalidManipulation::invalidParameter( |
||
168 | 'fit', |
||
169 | $fitMethod, |
||
170 | $this->getValidManipulationOptions('fit') |
||
171 | ); |
||
172 | } |
||
173 | |||
174 | $this->width($width); |
||
175 | $this->height($height); |
||
176 | |||
177 | return $this->addManipulation('fit', $fitMethod); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @param int $ratio A value between 1 and 8 |
||
182 | * |
||
183 | * @return $this |
||
184 | */ |
||
185 | public function devicePixelRatio(int $ratio) |
||
186 | { |
||
187 | if ($ratio < 1 || $ratio > 8) { |
||
188 | throw InvalidManipulation::valueNotInRange('ratio', $ratio, 1, 8); |
||
189 | } |
||
190 | |||
191 | return $this->addManipulation('devicePixelRatio', $ratio); |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * @param int $brightness A value between -100 and 100 |
||
196 | * |
||
197 | * @return $this |
||
198 | */ |
||
199 | public function brightness(int $brightness) |
||
200 | { |
||
201 | if ($brightness < -100 || $brightness > 100) { |
||
202 | throw InvalidManipulation::valueNotInRange('brightness', $brightness, -100, 100); |
||
203 | } |
||
204 | |||
205 | return $this->addManipulation('brightness', $brightness); |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * @param float $gamma A value between 0.01 and 9.99 |
||
210 | * |
||
211 | * @return $this |
||
212 | */ |
||
213 | public function gamma(float $gamma) |
||
221 | |||
222 | /** |
||
223 | * @param int $contrast A value between -100 and 100 |
||
224 | * |
||
225 | * @return $this |
||
226 | */ |
||
227 | public function contrast(int $contrast) |
||
228 | { |
||
229 | if ($contrast < -100 || $contrast > 100) { |
||
230 | throw InvalidManipulation::valueNotInRange('contrast', $contrast, -100, 100); |
||
235 | |||
236 | /** |
||
237 | * @param int $sharpen A value between 0 and 100 |
||
238 | * |
||
239 | * @return $this |
||
240 | */ |
||
241 | public function sharpen(int $sharpen) |
||
249 | |||
250 | /** |
||
251 | * @param int $blur A value between 0 and 100 |
||
252 | * |
||
253 | * @return $this |
||
254 | */ |
||
255 | View Code Duplication | public function blur(int $blur) |
|
263 | |||
264 | /** |
||
265 | * @param int $pixelate A value between 0 and 1000 |
||
266 | * |
||
267 | * @return $this |
||
268 | */ |
||
269 | public function pixelate(int $pixelate) |
||
277 | |||
278 | /** |
||
279 | * @return $this |
||
280 | */ |
||
281 | public function greyscale() |
||
285 | |||
286 | /** |
||
287 | * @return $this |
||
288 | */ |
||
289 | public function sepia() |
||
293 | |||
294 | /** |
||
295 | * @param string $colorName |
||
296 | * |
||
297 | * @return $this |
||
298 | */ |
||
299 | public function background(string $colorName) |
||
303 | |||
304 | /** |
||
305 | * @param int $width |
||
306 | * @param string $color |
||
307 | * @param string $borderType |
||
308 | * |
||
309 | * @return $this |
||
310 | */ |
||
311 | public function border(int $width, string $color, string $borderType = 'overlay') |
||
327 | |||
328 | /** |
||
329 | * @param int $quality |
||
330 | * |
||
331 | * @return $this |
||
332 | */ |
||
333 | View Code Duplication | public function quality(int $quality) |
|
341 | |||
342 | /** |
||
343 | * @param string $format |
||
344 | * |
||
345 | * @return $this |
||
346 | */ |
||
347 | View Code Duplication | public function format(string $format) |
|
359 | |||
360 | /** |
||
361 | * @param string $filterName |
||
362 | * |
||
363 | * @return $this |
||
364 | */ |
||
365 | View Code Duplication | protected function filter(string $filterName) |
|
377 | |||
378 | /** |
||
379 | * @return $this |
||
380 | */ |
||
381 | public function apply() |
||
387 | |||
388 | public function removeManipulation(string $name) |
||
392 | |||
393 | public function hasManipulation(string $manipulationName): bool |
||
397 | |||
398 | /** |
||
399 | * @param string $manipulationName |
||
400 | * |
||
401 | * @return string|null |
||
402 | */ |
||
403 | public function getManipulationArgument(string $manipulationName) |
||
411 | |||
412 | protected function addManipulation(string $manipulationName, string $manipulationArgument) |
||
418 | |||
419 | public function mergeManipulations(Manipulations $manipulations) |
||
425 | |||
426 | public function getManipulationSequence(): ManipulationSequence |
||
430 | |||
431 | protected function validateManipulation(string $value, string $constantNamePrefix): bool |
||
435 | |||
436 | protected function getValidManipulationOptions(string $manipulation): array |
||
444 | } |
||
445 |
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.