Total Complexity | 97 |
Total Lines | 545 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like ColorRepresentation 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 ColorRepresentation, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | class ColorRepresentation extends Representation |
||
31 | { |
||
32 | const COLOR_NAME = 1; |
||
33 | const COLOR_HEX_3 = 2; |
||
34 | const COLOR_HEX_6 = 3; |
||
35 | const COLOR_RGB = 4; |
||
36 | const COLOR_RGBA = 5; |
||
37 | const COLOR_HSL = 6; |
||
38 | const COLOR_HSLA = 7; |
||
39 | const COLOR_HEX_4 = 8; |
||
40 | const COLOR_HEX_8 = 9; |
||
41 | |||
42 | public static $color_map = array( |
||
43 | 'aliceblue' => 'f0f8ff', |
||
44 | 'antiquewhite' => 'faebd7', |
||
45 | 'aqua' => '00ffff', |
||
46 | 'aquamarine' => '7fffd4', |
||
47 | 'azure' => 'f0ffff', |
||
48 | 'beige' => 'f5f5dc', |
||
49 | 'bisque' => 'ffe4c4', |
||
50 | 'black' => '000000', |
||
51 | 'blanchedalmond' => 'ffebcd', |
||
52 | 'blue' => '0000ff', |
||
53 | 'blueviolet' => '8a2be2', |
||
54 | 'brown' => 'a52a2a', |
||
55 | 'burlywood' => 'deb887', |
||
56 | 'cadetblue' => '5f9ea0', |
||
57 | 'chartreuse' => '7fff00', |
||
58 | 'chocolate' => 'd2691e', |
||
59 | 'coral' => 'ff7f50', |
||
60 | 'cornflowerblue' => '6495ed', |
||
61 | 'cornsilk' => 'fff8dc', |
||
62 | 'crimson' => 'dc143c', |
||
63 | 'cyan' => '00ffff', |
||
64 | 'darkblue' => '00008b', |
||
65 | 'darkcyan' => '008b8b', |
||
66 | 'darkgoldenrod' => 'b8860b', |
||
67 | 'darkgray' => 'a9a9a9', |
||
68 | 'darkgreen' => '006400', |
||
69 | 'darkgrey' => 'a9a9a9', |
||
70 | 'darkkhaki' => 'bdb76b', |
||
71 | 'darkmagenta' => '8b008b', |
||
72 | 'darkolivegreen' => '556b2f', |
||
73 | 'darkorange' => 'ff8c00', |
||
74 | 'darkorchid' => '9932cc', |
||
75 | 'darkred' => '8b0000', |
||
76 | 'darksalmon' => 'e9967a', |
||
77 | 'darkseagreen' => '8fbc8f', |
||
78 | 'darkslateblue' => '483d8b', |
||
79 | 'darkslategray' => '2f4f4f', |
||
80 | 'darkslategrey' => '2f4f4f', |
||
81 | 'darkturquoise' => '00ced1', |
||
82 | 'darkviolet' => '9400d3', |
||
83 | 'deeppink' => 'ff1493', |
||
84 | 'deepskyblue' => '00bfff', |
||
85 | 'dimgray' => '696969', |
||
86 | 'dimgrey' => '696969', |
||
87 | 'dodgerblue' => '1e90ff', |
||
88 | 'firebrick' => 'b22222', |
||
89 | 'floralwhite' => 'fffaf0', |
||
90 | 'forestgreen' => '228b22', |
||
91 | 'fuchsia' => 'ff00ff', |
||
92 | 'gainsboro' => 'dcdcdc', |
||
93 | 'ghostwhite' => 'f8f8ff', |
||
94 | 'gold' => 'ffd700', |
||
95 | 'goldenrod' => 'daa520', |
||
96 | 'gray' => '808080', |
||
97 | 'green' => '008000', |
||
98 | 'greenyellow' => 'adff2f', |
||
99 | 'grey' => '808080', |
||
100 | 'honeydew' => 'f0fff0', |
||
101 | 'hotpink' => 'ff69b4', |
||
102 | 'indianred' => 'cd5c5c', |
||
103 | 'indigo' => '4b0082', |
||
104 | 'ivory' => 'fffff0', |
||
105 | 'khaki' => 'f0e68c', |
||
106 | 'lavender' => 'e6e6fa', |
||
107 | 'lavenderblush' => 'fff0f5', |
||
108 | 'lawngreen' => '7cfc00', |
||
109 | 'lemonchiffon' => 'fffacd', |
||
110 | 'lightblue' => 'add8e6', |
||
111 | 'lightcoral' => 'f08080', |
||
112 | 'lightcyan' => 'e0ffff', |
||
113 | 'lightgoldenrodyellow' => 'fafad2', |
||
114 | 'lightgray' => 'd3d3d3', |
||
115 | 'lightgreen' => '90ee90', |
||
116 | 'lightgrey' => 'd3d3d3', |
||
117 | 'lightpink' => 'ffb6c1', |
||
118 | 'lightsalmon' => 'ffa07a', |
||
119 | 'lightseagreen' => '20b2aa', |
||
120 | 'lightskyblue' => '87cefa', |
||
121 | 'lightslategray' => '778899', |
||
122 | 'lightslategrey' => '778899', |
||
123 | 'lightsteelblue' => 'b0c4de', |
||
124 | 'lightyellow' => 'ffffe0', |
||
125 | 'lime' => '00ff00', |
||
126 | 'limegreen' => '32cd32', |
||
127 | 'linen' => 'faf0e6', |
||
128 | 'magenta' => 'ff00ff', |
||
129 | 'maroon' => '800000', |
||
130 | 'mediumaquamarine' => '66cdaa', |
||
131 | 'mediumblue' => '0000cd', |
||
132 | 'mediumorchid' => 'ba55d3', |
||
133 | 'mediumpurple' => '9370db', |
||
134 | 'mediumseagreen' => '3cb371', |
||
135 | 'mediumslateblue' => '7b68ee', |
||
136 | 'mediumspringgreen' => '00fa9a', |
||
137 | 'mediumturquoise' => '48d1cc', |
||
138 | 'mediumvioletred' => 'c71585', |
||
139 | 'midnightblue' => '191970', |
||
140 | 'mintcream' => 'f5fffa', |
||
141 | 'mistyrose' => 'ffe4e1', |
||
142 | 'moccasin' => 'ffe4b5', |
||
143 | 'navajowhite' => 'ffdead', |
||
144 | 'navy' => '000080', |
||
145 | 'oldlace' => 'fdf5e6', |
||
146 | 'olive' => '808000', |
||
147 | 'olivedrab' => '6b8e23', |
||
148 | 'orange' => 'ffa500', |
||
149 | 'orangered' => 'ff4500', |
||
150 | 'orchid' => 'da70d6', |
||
151 | 'palegoldenrod' => 'eee8aa', |
||
152 | 'palegreen' => '98fb98', |
||
153 | 'paleturquoise' => 'afeeee', |
||
154 | 'palevioletred' => 'db7093', |
||
155 | 'papayawhip' => 'ffefd5', |
||
156 | 'peachpuff' => 'ffdab9', |
||
157 | 'peru' => 'cd853f', |
||
158 | 'pink' => 'ffc0cb', |
||
159 | 'plum' => 'dda0dd', |
||
160 | 'powderblue' => 'b0e0e6', |
||
161 | 'purple' => '800080', |
||
162 | 'rebeccapurple' => '663399', |
||
163 | 'red' => 'ff0000', |
||
164 | 'rosybrown' => 'bc8f8f', |
||
165 | 'royalblue' => '4169e1', |
||
166 | 'saddlebrown' => '8b4513', |
||
167 | 'salmon' => 'fa8072', |
||
168 | 'sandybrown' => 'f4a460', |
||
169 | 'seagreen' => '2e8b57', |
||
170 | 'seashell' => 'fff5ee', |
||
171 | 'sienna' => 'a0522d', |
||
172 | 'silver' => 'c0c0c0', |
||
173 | 'skyblue' => '87ceeb', |
||
174 | 'slateblue' => '6a5acd', |
||
175 | 'slategray' => '708090', |
||
176 | 'slategrey' => '708090', |
||
177 | 'snow' => 'fffafa', |
||
178 | 'springgreen' => '00ff7f', |
||
179 | 'steelblue' => '4682b4', |
||
180 | 'tan' => 'd2b48c', |
||
181 | 'teal' => '008080', |
||
182 | 'thistle' => 'd8bfd8', |
||
183 | 'tomato' => 'ff6347', |
||
184 | // To quote MDN: |
||
185 | // "Technically, transparent is a shortcut for rgba(0,0,0,0)." |
||
186 | 'transparent' => '00000000', |
||
187 | 'turquoise' => '40e0d0', |
||
188 | 'violet' => 'ee82ee', |
||
189 | 'wheat' => 'f5deb3', |
||
190 | 'white' => 'ffffff', |
||
191 | 'whitesmoke' => 'f5f5f5', |
||
192 | 'yellow' => 'ffff00', |
||
193 | 'yellowgreen' => '9acd32', |
||
194 | ); |
||
195 | |||
196 | public $r = 0; |
||
197 | public $g = 0; |
||
198 | public $b = 0; |
||
199 | public $a = 1.0; |
||
200 | public $variant; |
||
201 | public $implicit_label = true; |
||
202 | public $hints = array('color'); |
||
203 | |||
204 | public function __construct($value) |
||
205 | { |
||
206 | parent::__construct('Color'); |
||
207 | |||
208 | $this->contents = $value; |
||
209 | $this->setValues($value); |
||
210 | } |
||
211 | |||
212 | public function getColor($variant = null) |
||
213 | { |
||
214 | if (!$variant) { |
||
215 | $variant = $this->variant; |
||
216 | } |
||
217 | |||
218 | switch ($variant) { |
||
219 | case self::COLOR_NAME: |
||
220 | $hex = \sprintf('%02x%02x%02x', $this->r, $this->g, $this->b); |
||
221 | $hex_alpha = \sprintf('%02x%02x%02x%02x', $this->r, $this->g, $this->b, \round($this->a * 0xFF)); |
||
222 | |||
223 | return \array_search($hex, self::$color_map, true) ?: \array_search($hex_alpha, self::$color_map, true); |
||
224 | case self::COLOR_HEX_3: |
||
225 | if (0 === $this->r % 0x11 && 0 === $this->g % 0x11 && 0 === $this->b % 0x11) { |
||
226 | return \sprintf( |
||
227 | '#%1X%1X%1X', |
||
228 | \round($this->r / 0x11), |
||
229 | \round($this->g / 0x11), |
||
230 | \round($this->b / 0x11) |
||
231 | ); |
||
232 | } |
||
233 | |||
234 | return false; |
||
235 | case self::COLOR_HEX_6: |
||
236 | return \sprintf('#%02X%02X%02X', $this->r, $this->g, $this->b); |
||
237 | case self::COLOR_RGB: |
||
238 | if (1.0 === $this->a) { |
||
239 | return \sprintf('rgb(%d, %d, %d)', $this->r, $this->g, $this->b); |
||
240 | } |
||
241 | |||
242 | return \sprintf('rgb(%d, %d, %d, %s)', $this->r, $this->g, $this->b, \round($this->a, 4)); |
||
243 | case self::COLOR_RGBA: |
||
244 | return \sprintf('rgba(%d, %d, %d, %s)', $this->r, $this->g, $this->b, \round($this->a, 4)); |
||
245 | case self::COLOR_HSL: |
||
246 | $val = self::rgbToHsl($this->r, $this->g, $this->b); |
||
247 | if (1.0 === $this->a) { |
||
248 | return \vsprintf('hsl(%d, %d%%, %d%%)', $val); |
||
249 | } |
||
250 | |||
251 | return \sprintf('hsl(%d, %d%%, %d%%, %s)', $val[0], $val[1], $val[2], \round($this->a, 4)); |
||
252 | case self::COLOR_HSLA: |
||
253 | $val = self::rgbToHsl($this->r, $this->g, $this->b); |
||
254 | |||
255 | return \sprintf('hsla(%d, %d%%, %d%%, %s)', $val[0], $val[1], $val[2], \round($this->a, 4)); |
||
256 | case self::COLOR_HEX_4: |
||
257 | if (0 === $this->r % 0x11 && 0 === $this->g % 0x11 && 0 === $this->b % 0x11 && 0 === ($this->a * 255) % 0x11) { |
||
258 | return \sprintf( |
||
259 | '#%1X%1X%1X%1X', |
||
260 | \round($this->r / 0x11), |
||
261 | \round($this->g / 0x11), |
||
262 | \round($this->b / 0x11), |
||
263 | \round($this->a * 0xF) |
||
264 | ); |
||
265 | } |
||
266 | |||
267 | return false; |
||
268 | |||
269 | case self::COLOR_HEX_8: |
||
270 | return \sprintf('#%02X%02X%02X%02X', $this->r, $this->g, $this->b, \round($this->a * 0xFF)); |
||
271 | } |
||
272 | |||
273 | return false; |
||
274 | } |
||
275 | |||
276 | public function hasAlpha($variant = null) |
||
277 | { |
||
278 | if (null === $variant) { |
||
279 | $variant = $this->variant; |
||
280 | } |
||
281 | |||
282 | switch ($variant) { |
||
283 | case self::COLOR_NAME: |
||
284 | case self::COLOR_RGB: |
||
285 | case self::COLOR_HSL: |
||
286 | return \abs($this->a - 1) >= 0.0001; |
||
287 | case self::COLOR_RGBA: |
||
288 | case self::COLOR_HSLA: |
||
289 | case self::COLOR_HEX_4: |
||
290 | case self::COLOR_HEX_8: |
||
291 | return true; |
||
292 | default: |
||
293 | return false; |
||
294 | } |
||
295 | } |
||
296 | |||
297 | protected function setValues($value) |
||
298 | { |
||
299 | $value = \strtolower(\trim($value)); |
||
300 | // Find out which variant of color input it is |
||
301 | if (isset(self::$color_map[$value])) { |
||
302 | if (!$this->setValuesFromHex(self::$color_map[$value])) { |
||
|
|||
303 | return; |
||
304 | } |
||
305 | |||
306 | $variant = self::COLOR_NAME; |
||
307 | } elseif ('#' === $value[0]) { |
||
308 | $variant = $this->setValuesFromHex(\substr($value, 1)); |
||
309 | |||
310 | if (!$variant) { |
||
311 | return; |
||
312 | } |
||
313 | } else { |
||
314 | $variant = $this->setValuesFromFunction($value); |
||
315 | |||
316 | if (!$variant) { |
||
317 | return; |
||
318 | } |
||
319 | } |
||
320 | |||
321 | // If something has gone horribly wrong |
||
322 | if ($this->r > 0xFF || $this->g > 0xFF || $this->b > 0xFF || $this->a > 1) { |
||
323 | $this->variant = null; // @codeCoverageIgnore |
||
324 | } else { |
||
325 | $this->variant = $variant; |
||
326 | $this->r = (int) $this->r; |
||
327 | $this->g = (int) $this->g; |
||
328 | $this->b = (int) $this->b; |
||
329 | $this->a = (float) $this->a; |
||
330 | } |
||
331 | } |
||
332 | |||
333 | protected function setValuesFromHex($hex) |
||
334 | { |
||
335 | if (!\ctype_xdigit($hex)) { |
||
336 | return null; |
||
337 | } |
||
338 | |||
339 | switch (\strlen($hex)) { |
||
340 | case 3: |
||
341 | $variant = self::COLOR_HEX_3; |
||
342 | break; |
||
343 | case 6: |
||
344 | $variant = self::COLOR_HEX_6; |
||
345 | break; |
||
346 | case 4: |
||
347 | $variant = self::COLOR_HEX_4; |
||
348 | break; |
||
349 | case 8: |
||
350 | $variant = self::COLOR_HEX_8; |
||
351 | break; |
||
352 | default: |
||
353 | return null; |
||
354 | } |
||
355 | |||
356 | switch ($variant) { |
||
357 | case self::COLOR_HEX_4: |
||
358 | $this->a = \hexdec($hex[3]) / 0xF; |
||
359 | // no break |
||
360 | case self::COLOR_HEX_3: |
||
361 | $this->r = \hexdec($hex[0]) * 0x11; |
||
362 | $this->g = \hexdec($hex[1]) * 0x11; |
||
363 | $this->b = \hexdec($hex[2]) * 0x11; |
||
364 | break; |
||
365 | case self::COLOR_HEX_8: |
||
366 | $this->a = \hexdec(\substr($hex, 6, 2)) / 0xFF; |
||
367 | // no break |
||
368 | case self::COLOR_HEX_6: |
||
369 | $hex = \str_split($hex, 2); |
||
370 | $this->r = \hexdec($hex[0]); |
||
371 | $this->g = \hexdec($hex[1]); |
||
372 | $this->b = \hexdec($hex[2]); |
||
373 | break; |
||
374 | } |
||
375 | |||
376 | return $variant; |
||
377 | } |
||
378 | |||
379 | protected function setValuesFromFunction($value) |
||
380 | { |
||
381 | if (!\preg_match('/^((?:rgb|hsl)a?)\\s*\\(([0-9\\.%,\\s\\/\\-]+)\\)$/i', $value, $match)) { |
||
382 | return null; |
||
383 | } |
||
384 | |||
385 | switch (\strtolower($match[1])) { |
||
386 | case 'rgb': |
||
387 | $variant = self::COLOR_RGB; |
||
388 | break; |
||
389 | case 'rgba': |
||
390 | $variant = self::COLOR_RGBA; |
||
391 | break; |
||
392 | case 'hsl': |
||
393 | $variant = self::COLOR_HSL; |
||
394 | break; |
||
395 | case 'hsla': |
||
396 | $variant = self::COLOR_HSLA; |
||
397 | break; |
||
398 | default: |
||
399 | return null; // @codeCoverageIgnore |
||
400 | } |
||
401 | |||
402 | $params = \preg_replace('/[,\\s\\/]+/', ',', \trim($match[2])); |
||
403 | $params = \explode(',', $params); |
||
404 | $params = \array_map('trim', $params); |
||
405 | |||
406 | if (\count($params) < 3 || \count($params) > 4) { |
||
407 | return null; |
||
408 | } |
||
409 | |||
410 | foreach ($params as $i => &$color) { |
||
411 | if (false !== \strpos($color, '%')) { |
||
412 | $color = (float) \str_replace('%', '', $color); |
||
413 | |||
414 | if (3 === $i) { |
||
415 | $color = $color / 100; |
||
416 | } elseif (\in_array($variant, array(self::COLOR_RGB, self::COLOR_RGBA), true)) { |
||
417 | $color = \round($color / 100 * 0xFF); |
||
418 | } |
||
419 | } |
||
420 | |||
421 | $color = (float) $color; |
||
422 | |||
423 | if (0 === $i && \in_array($variant, array(self::COLOR_HSL, self::COLOR_HSLA), true)) { |
||
424 | $color = ($color % 360 + 360) % 360; |
||
425 | } |
||
426 | } |
||
427 | |||
428 | /** @var float[] Psalm bug workaround */ |
||
429 | $params = \array_map('floatval', $params); |
||
430 | |||
431 | switch ($variant) { |
||
432 | case self::COLOR_RGBA: |
||
433 | case self::COLOR_RGB: |
||
434 | if (\min($params) < 0 || \max($params) > 0xFF) { |
||
435 | return null; |
||
436 | } |
||
437 | break; |
||
438 | case self::COLOR_HSLA: |
||
439 | case self::COLOR_HSL: |
||
440 | if (\min($params) < 0 || $params[0] > 360 || \max($params[1], $params[2]) > 100) { |
||
441 | return null; |
||
442 | } |
||
443 | break; |
||
444 | } |
||
445 | |||
446 | if (4 === \count($params)) { |
||
447 | if ($params[3] > 1) { |
||
448 | return null; |
||
449 | } |
||
450 | |||
451 | $this->a = $params[3]; |
||
452 | } |
||
453 | |||
454 | if (self::COLOR_HSLA === $variant || self::COLOR_HSL === $variant) { |
||
455 | $params = self::hslToRgb($params[0], $params[1], $params[2]); |
||
456 | } |
||
457 | |||
458 | list($this->r, $this->g, $this->b) = $params; |
||
459 | |||
460 | return $variant; |
||
461 | } |
||
462 | |||
463 | /** |
||
464 | * Turns HSL color to RGB. Black magic. |
||
465 | * |
||
466 | * @param float $h Hue |
||
467 | * @param float $s Saturation |
||
468 | * @param float $l Lightness |
||
469 | * |
||
470 | * @return int[] RGB array |
||
471 | */ |
||
472 | public static function hslToRgb($h, $s, $l) |
||
493 | ); |
||
494 | } |
||
495 | |||
496 | /** |
||
497 | * Converts RGB to HSL. Color inversion of previous black magic is white magic? |
||
498 | * |
||
499 | * @param float|int $red Red |
||
500 | * @param float|int $green Green |
||
501 | * @param float|int $blue Blue |
||
502 | * |
||
503 | * @return float[] HSL array |
||
504 | */ |
||
505 | public static function rgbToHsl($red, $green, $blue) |
||
506 | { |
||
507 | if (\min($red, $green, $blue) < 0) { |
||
508 | throw new InvalidArgumentException('The parameters for rgbToHsl should be no less than 0'); |
||
509 | } |
||
510 | |||
511 | if (\max($red, $green, $blue) > 0xFF) { |
||
512 | throw new InvalidArgumentException('The parameters for rgbToHsl should be no more than 255'); |
||
513 | } |
||
514 | |||
515 | $clrMin = \min($red, $green, $blue); |
||
516 | $clrMax = \max($red, $green, $blue); |
||
517 | $deltaMax = $clrMax - $clrMin; |
||
518 | |||
519 | $L = ($clrMax + $clrMin) / 510; |
||
520 | |||
521 | if (0 == $deltaMax) { |
||
522 | $H = 0; |
||
523 | $S = 0; |
||
524 | } else { |
||
525 | if (0.5 > $L) { |
||
526 | $S = $deltaMax / ($clrMax + $clrMin); |
||
527 | } else { |
||
528 | $S = $deltaMax / (510 - $clrMax - $clrMin); |
||
529 | } |
||
530 | |||
531 | if ($clrMax === $red) { |
||
532 | $H = ($green - $blue) / (6.0 * $deltaMax); |
||
533 | |||
534 | if (0 > $H) { |
||
535 | $H += 1.0; |
||
536 | } |
||
537 | } elseif ($clrMax === $green) { |
||
538 | $H = 1 / 3 + ($blue - $red) / (6.0 * $deltaMax); |
||
539 | } else { |
||
540 | $H = 2 / 3 + ($red - $green) / (6.0 * $deltaMax); |
||
541 | } |
||
542 | } |
||
543 | |||
544 | return array( |
||
545 | (float) ($H * 360 % 360), |
||
546 | (float) ($S * 100), |
||
547 | (float) ($L * 100), |
||
548 | ); |
||
549 | } |
||
550 | |||
551 | /** |
||
552 | * Helper function for hslToRgb. Even blacker magic. |
||
553 | * |
||
554 | * |
||
555 | * @param float $m1 |
||
556 | * @param float $m2 |
||
557 | * @param float $hue |
||
558 | * |
||
559 | * @return float Color value |
||
560 | */ |
||
561 | private static function hueToRgb($m1, $m2, $hue) |
||
575 | } |
||
576 | } |
||
577 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
integer
values, zero is a special case, in particular the following results might be unexpected: