Completed
Push — master ( 5f4359...ab5164 )
by Mathieu
02:58
created

ColorProperty::parseArray()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
rs 8.439
cc 5
eloc 19
nc 5
nop 1
1
<?php
2
3
namespace Charcoal\Property;
4
5
// Dependencies from `PHP`
6
use \InvalidArgumentException;
7
8
// Dependencies from `PHP` extensions
9
use \PDO;
10
11
// Intra module (`charcoal-property`) dependencies
12
use \Charcoal\Property\AbstractProperty;
13
14
/**
15
 * Color Property
16
 */
17
class ColorProperty extends AbstractProperty
18
{
19
    /**
20
     * @var boolean $supportAlpha
21
     */
22
    private $supportAlpha = false;
23
24
    /**
25
     * @return string
26
     */
27
    public function type()
28
    {
29
        return 'color';
30
    }
31
32
    /**
33
     * @param boolean $support The alpha support flag.
34
     * @return ColorProperty Chainable
35
     */
36
    public function setSupportAlpha($support)
37
    {
38
        $this->supportAlpha = !!$support;
39
        return $this;
40
    }
41
42
    /**
43
     * @return boolean
44
     */
45
    public function supportAlpha()
46
    {
47
        return $this->supportAlpha;
48
    }
49
50
    /**
51
     * AbstractProperty > setVal(). Ensure proper hexadecimal value.
52
     *
53
     * @param mixed $val The value to set.
54
     * @return ColorProperty Chainable
55
     */
56
    public function setVal($val)
57
    {
58
        $this->val = $this->colorVal($val);
59
        return $this;
60
    }
61
62
    /**
63
     * @param string|array $val The color value to sanitize to an hexadecimal or rgba() value.
64
     * @return string The color string. Hexadecimal or rgba() if alpha is supported..
65
     */
66
    public function colorVal($val)
67
    {
68
        $parsed = $this->parseVal($val);
69
        if (!$this->supportAlpha()) {
70
            return '#'.$this->rgbToHexadecimal($parsed['r'], $parsed['g'], $parsed['b']);
71
        } else {
72
            return sprintf(
73
                'rgba(%d,%d,%d,%s)',
74
                $parsed['r'],
75
                $parsed['g'],
76
                $parsed['b'],
77
                $parsed['a']
78
            );
79
        }
80
    }
81
82
    /**
83
     * @param integer $r Red value (0 to 255).
84
     * @param integer $g Green value (0 to 255).
85
     * @param integer $b Blue value (0 to 255).
86
     * @return string Hexadecimal color value, as uppercased hexadecimal without the "#" prefix.
87
     */
88
    protected function rgbToHexadecimal($r, $g, $b)
89
    {
90
        $hex = '';
91
        $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT);
92
        $hex .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT);
93
        $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
94
        return strtoupper($hex);
95
    }
96
97
    /**
98
     * @return string
99
     */
100
    public function sqlExtra()
101
    {
102
        return '';
103
    }
104
105
    /**
106
     * Get the SQL type (Storage format)
107
     *
108
     * Stored as `VARCHAR` for maxLength under 255 and `TEXT` for other, longer strings
109
     *
110
     * @return string The SQL type
111
     */
112
    public function sqlType()
113
    {
114
        // Multiple strings are always stored as TEXT because they can hold multiple values
115
        if ($this->multiple()) {
116
            return 'TEXT';
117
        }
118
119
        if ($this->supportAlpha()) {
120
            return 'VARCHAR(32)';
121
        } else {
122
            return 'CHAR(7)';
123
        }
124
125
    }
126
127
    /**
128
     * @return integer
129
     */
130
    public function sqlPdoType()
131
    {
132
        return PDO::PARAM_STR;
133
    }
134
135
    /**
136
     * @return mixed
137
     */
138
    public function save()
139
    {
140
        return $this->val();
141
    }
142
143
    /**
144
     * @param string|array $val The color array or string to parse.
145
     * @return array The parsed `[r,g,b,a]` color array.
146
     */
147
    private function parseVal($val)
148
    {
149
        if (is_array($val)) {
150
            return $this->parseArray($val);
151
        } else {
152
            return $this->parseString($val);
153
        }
154
    }
155
156
    /**
157
     * @param array $val The color array to parse.
158
     * @throws InvalidArgumentException If the array does not have at least 3 items.
159
     * @return array The parsed `[r,g,b,a]` color array.
160
     */
161
    private function parseArray(array $val)
162
    {
163
        if (count($val) < 3) {
164
            throw new InvalidArgumentException(
165
                'Color value must have at least 3 items in array (r, g and b) to be parsed by parseArray()'
166
            );
167
        }
168
        if (isset($val['r'])) {
169
            $r = $val['r'];
170
            $g = $val['g'];
171
            $b = $val['b'];
172
            $a = isset($val['a']) ? $val['a'] : 0;
173
        } else {
174
            $r = $val[0];
175
            $g = $val[1];
176
            $b = $val[2];
177
            $a = isset($val[3]) ? $val[3] : 0;
178
        }
179
180
        return [
181
            'r' => (int)$r,
182
            'g' => (int)$g,
183
            'b' => (int)$b,
184
            'a' => $a
185
        ];
186
    }
187
188
    /**
189
     * @param string $val The colors string to parse.
190
     * @throws InvalidArgumentException If the color value is not a string.
191
     * @return array The parsed `[r,g,b,a]` color array.
192
     */
193
    private function parseString($val)
194
    {
195
        if (!is_string($val)) {
196
            throw new InvalidArgumentException(
197
                'Color value must be a sting to be parsed by parseString()'
198
            );
199
        }
200
201
        $val = str_replace('#', '', strtolower($val));
202
        if (ctype_xdigit($val)) {
203
            return $this->parseHexadecimal($val);
204
        } elseif (strstr($val, 'rgb(')) {
205
            return $this->parseRgb($val);
206
        } elseif (strstr($val, 'rgba(')) {
207
            return $this->parseRgba($val);
208
        } elseif (strstr($val, 'hsl(')) {
209
            return $this->parseHsl($val);
210
        } elseif (strstr($val, 'hsla(')) {
211
            return $this->parseHsla($val);
212
        } else {
213
            return $this->parseNamedColor($val);
214
        }
215
    }
216
217
    /**
218
     * @param string $val The hexadecimal color string to parse.
219
     * @return array The parsed `[r,g,b,a]` color array.
220
     */
221
    private function parseHexadecimal($val)
222
    {
223
        $val = str_replace('#', '', $val);
224
225
        if (strlen($val) == 3) {
226
            return [
227
                'r' => hexdec(substr($val, 0, 1).substr($val, 0, 1)),
228
                'g' => hexdec(substr($val, 1, 1).substr($val, 1, 1)),
229
                'b' => hexdec(substr($val, 2, 1).substr($val, 2, 1)),
230
                // Ignore alpha.
231
                'a' => 0
232
            ];
233
        } else {
234
            return [
235
                'r' => hexdec(substr($val, 0, 2)),
236
                'g' => hexdec(substr($val, 2, 2)),
237
                'b' => hexdec(substr($val, 4, 2)),
238
                // Ignore alpha.
239
                'a' => 0
240
            ];
241
        }
242
    }
243
244
    /**
245
     * @param string $val The rgb() color string to parse.
246
     * @return array The parsed `[r,g,b,a]` color array.
247
     */
248
    private function parseRgb($val)
249
    {
250
        $match = preg_match('/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i', $val, $m);
251
        if (!$match) {
252
            throw new InvalidArgumentException(
253
                'String does not match rgb() format to be parsed by parseRgb()'
254
            );
255
        }
256
        return [
257
            'r' => $m[1],
258
            'g' => $m[2],
259
            'b' => $m[3],
260
            // Ignore alpha.
261
            'a' => 0
262
        ];
263
    }
264
265
    /**
266
     * @param string $val The rgab() color string to parse.
267
     * @return array The parsed `[r,g,b,a]` color array.
268
     */
269
    private function parseRgba($val)
270
    {
271
        preg_match('/rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\(\d+)\s*\)/i', $val, $m);
272
        if (!$match) {
0 ignored issues
show
Bug introduced by
The variable $match does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
273
            throw new InvalidArgumentException(
274
                'String does not match rgba() format to be parsed by parseRgba()'
275
            );
276
        }
277
        return [
278
            'r' => (int)$m[1],
279
            'g' => (int)$m[2],
280
            'b' => (int)$m[3],
281
            'a' => $m[4]
282
        ];
283
    }
284
285
    /**
286
     * @param string $val The hsl() color string to parse.
287
     * @throws Exception This parse method is not yet supported.
288
     */
289
    private function parseHsl($val)
0 ignored issues
show
Unused Code introduced by
The parameter $val is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
290
    {
291
        throw new Exception(
292
            'HSL color value is not yet supported'
293
        );
294
    }
295
296
    /**
297
     * @param string $val The hsla() string color val to parse.
298
     * @throws Exception This parse method is not yet supported.
299
     */
300
    private function parseHsla($val)
0 ignored issues
show
Unused Code introduced by
The parameter $val is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
301
    {
302
        throw new Exception(
303
            'HSL color value is not yet supported'
304
        );
305
    }
306
307
    /**
308
     * @param string $val The named color val to parse.
309
     * @throws InvalidArgumentException If the string is not an existing SVG color.
310
     * @return array The parsed `[r,g,b,a]` color array.
311
     */
312
    private function parseNamedColor($val)
313
    {
314
        $colors = include 'data/colors.php';
315
        $val = strtolower($val);
316
        if (in_array($val, array_keys($colors))) {
317
            return $this->parseVal($colors[$val]);
318
        }
319
320
        throw new InvalidArgumentException(
321
            'Color "%s" is not a valid SVG (or CSS) color name.'
322
        );
323
    }
324
}
325