Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
94 / 94 |
|
100.00% |
30 / 30 |
CRAP | |
100.00% |
1 / 1 |
Selector | |
100.00% |
94 / 94 |
|
100.00% |
30 / 30 |
57 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setName | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isElementSelector | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isIdSelector | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isClassSelector | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isMultipleSelector | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasDescendant | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setTabSize | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getTabSize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setProperty | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setProperties | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
hasProperty | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getProperties | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getProperty | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
removeProperty | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
minify | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isMinified | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getIterator | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
count | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
render | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
6 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__set | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__get | |
100.00% |
37 / 37 |
|
100.00% |
1 / 1 |
16 | |||
__isset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__unset | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
offsetExists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
offsetGet | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
offsetSet | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
offsetUnset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Css; |
15 | |
16 | use ArrayIterator; |
17 | |
18 | /** |
19 | * Pop CSS selector class |
20 | * |
21 | * @category Pop |
22 | * @package Pop\Css |
23 | * @author Nick Sagona, III <dev@nolainteractive.com> |
24 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
25 | * @license http://www.popphp.org/license New BSD License |
26 | * @version 2.0.0 |
27 | */ |
28 | class Selector implements \ArrayAccess, \Countable, \IteratorAggregate |
29 | { |
30 | |
31 | /** |
32 | * Trait declaration |
33 | */ |
34 | use CommentTrait; |
35 | |
36 | /** |
37 | * Selector name |
38 | * @var ?string |
39 | */ |
40 | protected ?string $name = null; |
41 | |
42 | /** |
43 | * Properties |
44 | * @var array |
45 | */ |
46 | protected array $properties = []; |
47 | |
48 | /** |
49 | * Tab size |
50 | * @var int |
51 | */ |
52 | protected int $tabSize = 4; |
53 | |
54 | /** |
55 | * Is ID selector flag |
56 | * @var bool |
57 | */ |
58 | protected bool $isId = false; |
59 | |
60 | /** |
61 | * Is class selector flag |
62 | * @var bool |
63 | */ |
64 | protected bool $isClass = false; |
65 | |
66 | /** |
67 | * Minify flag |
68 | * @var bool |
69 | */ |
70 | protected bool $minify = false; |
71 | |
72 | /** |
73 | * Constructor |
74 | * |
75 | * Instantiate the CSS selector object |
76 | * |
77 | * @param ?string $name |
78 | * @param int $tabSize |
79 | */ |
80 | public function __construct(?string $name = null, int $tabSize = 4) |
81 | { |
82 | if ($name !== null) { |
83 | $this->setName($name); |
84 | } |
85 | $this->setTabSize($tabSize); |
86 | } |
87 | |
88 | /** |
89 | * Set name |
90 | * |
91 | * @param string $name |
92 | * @return Selector |
93 | */ |
94 | public function setName(string $name): Selector |
95 | { |
96 | if (str_contains($name, '.')) { |
97 | $this->isClass = true; |
98 | } |
99 | if (str_contains($name, '#')) { |
100 | $this->isId = true; |
101 | } |
102 | $this->name = $name; |
103 | return $this; |
104 | } |
105 | |
106 | /** |
107 | * Get name |
108 | * |
109 | * @return string|null |
110 | */ |
111 | public function getName(): string|null |
112 | { |
113 | return $this->name; |
114 | } |
115 | |
116 | /** |
117 | * Check if is element selector |
118 | * |
119 | * @return bool |
120 | */ |
121 | public function isElementSelector(): bool |
122 | { |
123 | return (!($this->isId) && !($this->isClass)); |
124 | } |
125 | |
126 | /** |
127 | * Check if is ID selector |
128 | * |
129 | * @return bool |
130 | */ |
131 | public function isIdSelector(): bool |
132 | { |
133 | return $this->isId; |
134 | } |
135 | |
136 | /** |
137 | * Check if is class selector |
138 | * |
139 | * @return bool |
140 | */ |
141 | public function isClassSelector(): bool |
142 | { |
143 | return $this->isClass; |
144 | } |
145 | |
146 | /** |
147 | * Check if is multiple selector |
148 | * |
149 | * @return bool |
150 | */ |
151 | public function isMultipleSelector(): bool |
152 | { |
153 | return (str_contains($this->name, ',')); |
154 | } |
155 | |
156 | /** |
157 | * Check if selector has a descendant |
158 | * |
159 | * @return bool |
160 | */ |
161 | public function hasDescendant(): bool |
162 | { |
163 | return (str_contains($this->name, '>')); |
164 | } |
165 | |
166 | /** |
167 | * Set tab size |
168 | * |
169 | * @param int $tabSize |
170 | * @return Selector |
171 | */ |
172 | public function setTabSize(int $tabSize): Selector |
173 | { |
174 | $this->tabSize = $tabSize; |
175 | return $this; |
176 | } |
177 | |
178 | /** |
179 | * Get tab size |
180 | * |
181 | * @return int |
182 | */ |
183 | public function getTabSize(): int |
184 | { |
185 | return $this->tabSize; |
186 | } |
187 | |
188 | /** |
189 | * Set property |
190 | * |
191 | * @param string $property |
192 | * @param string $value |
193 | * @return Selector |
194 | */ |
195 | public function setProperty(string $property, string $value): Selector |
196 | { |
197 | $this->properties[$property] = $value; |
198 | return $this; |
199 | } |
200 | |
201 | /** |
202 | * Set properties |
203 | * |
204 | * @param array $properties |
205 | * @return Selector |
206 | */ |
207 | public function setProperties(array $properties): Selector |
208 | { |
209 | foreach ($properties as $property => $value) { |
210 | $this->setProperty($property, $value); |
211 | } |
212 | return $this; |
213 | } |
214 | |
215 | /** |
216 | * Check if selector has property |
217 | * |
218 | * @param string $property |
219 | * @return bool |
220 | */ |
221 | public function hasProperty(string $property): bool |
222 | { |
223 | return isset($this->properties[$property]); |
224 | } |
225 | |
226 | /** |
227 | * Get properties |
228 | * |
229 | * @return array |
230 | */ |
231 | public function getProperties(): array |
232 | { |
233 | return $this->properties; |
234 | } |
235 | |
236 | /** |
237 | * Get property |
238 | * |
239 | * @param string $property |
240 | * @return string|null |
241 | */ |
242 | public function getProperty(string $property): string|null |
243 | { |
244 | return $this->properties[$property] ?? null; |
245 | } |
246 | |
247 | /** |
248 | * Remove property |
249 | * |
250 | * @param string $property |
251 | * @return Selector |
252 | */ |
253 | public function removeProperty(string $property): Selector |
254 | { |
255 | if (isset($this->properties[$property])) { |
256 | unset($this->properties[$property]); |
257 | } |
258 | return $this; |
259 | } |
260 | |
261 | /** |
262 | * Set minify flag |
263 | * |
264 | * @param bool $minify |
265 | * @return Selector |
266 | */ |
267 | public function minify(bool $minify = true): Selector |
268 | { |
269 | $this->minify = $minify; |
270 | return $this; |
271 | } |
272 | |
273 | /** |
274 | * Check if minify flag is set |
275 | * |
276 | * @return bool |
277 | */ |
278 | public function isMinified(): bool |
279 | { |
280 | return $this->minify; |
281 | } |
282 | |
283 | /** |
284 | * Method to iterate over the properties |
285 | * |
286 | * @return ArrayIterator |
287 | */ |
288 | public function getIterator(): ArrayIterator |
289 | { |
290 | return new ArrayIterator($this->properties); |
291 | } |
292 | |
293 | /** |
294 | * Method to get the count of properties |
295 | * |
296 | * @return int |
297 | */ |
298 | public function count(): int |
299 | { |
300 | return count($this->properties); |
301 | } |
302 | |
303 | /** |
304 | * Method to render the selector CSS |
305 | * |
306 | * @return string |
307 | */ |
308 | public function render(): string |
309 | { |
310 | $css = ''; |
311 | |
312 | if (!$this->minify) { |
313 | foreach ($this->comments as $comment) { |
314 | $css .= (string)$comment . PHP_EOL; |
315 | } |
316 | } |
317 | |
318 | if (!$this->minify) { |
319 | $css .= $this->name . ' {' . PHP_EOL; |
320 | |
321 | foreach ($this->properties as $property => $value) { |
322 | $css .= str_repeat(' ', $this->tabSize) . $property . ': ' . $value . ';' . PHP_EOL; |
323 | } |
324 | |
325 | $css .= '}' . PHP_EOL; |
326 | } else { |
327 | $css .= $this->name . '{'; |
328 | foreach ($this->properties as $property => $value) { |
329 | $css .= $property . ':' . $value . ';'; |
330 | } |
331 | |
332 | $css .= '}'; |
333 | } |
334 | |
335 | return $css; |
336 | } |
337 | |
338 | /** |
339 | * To string method |
340 | * |
341 | * @return string |
342 | */ |
343 | public function __toString(): string |
344 | { |
345 | return $this->render(); |
346 | } |
347 | |
348 | /** |
349 | * Magic method to set the property to the value of $this->properties[$name] |
350 | * |
351 | * @param string $name |
352 | * @param mixed $value |
353 | * @return void |
354 | */ |
355 | public function __set(string $name, mixed $value): void |
356 | { |
357 | $this->properties[$name] = $value; |
358 | } |
359 | |
360 | /** |
361 | * Magic method to return the value of $this->properties[$name] |
362 | * |
363 | * @param string $name |
364 | * @return mixed |
365 | */ |
366 | public function __get(string $name): mixed |
367 | { |
368 | if ((str_contains($name, 'margin-')) && !isset($this->properties[$name]) && isset($this->properties['margin'])) { |
369 | $values = explode(' ', $this->properties['margin']); |
370 | $position = substr($name, strpos($name, '-') + 1); |
371 | $positionValues = ['top' => null, 'right' => null, 'bottom' => null, 'left' => null]; |
372 | |
373 | switch (count($values)) { |
374 | case 4: |
375 | $positionValues = ['top' => $values[0], 'right' => $values[1], 'bottom' => $values[2], 'left' => $values[3]]; |
376 | break; |
377 | case 3: |
378 | $positionValues = ['top' => $values[0], 'right' => $values[1], 'bottom' => $values[2], 'left' => $values[1]]; |
379 | break; |
380 | case 2: |
381 | $positionValues = ['top' => $values[0], 'right' => $values[1], 'bottom' => $values[0], 'left' => $values[1]]; |
382 | break; |
383 | case 1: |
384 | $positionValues = ['top' => $values[0], 'right' => $values[0], 'bottom' => $values[0], 'left' => $values[0]]; |
385 | break; |
386 | } |
387 | |
388 | return $positionValues[$position]; |
389 | } else if ((str_contains($name, 'padding-')) && !isset($this->properties[$name]) && isset($this->properties['padding'])) { |
390 | $values = explode(' ', $this->properties['padding']); |
391 | $position = substr($name, strpos($name, '-') + 1); |
392 | $positionValues = ['top' => null, 'right' => null, 'bottom' => null, 'left' => null]; |
393 | |
394 | switch (count($values)) { |
395 | case 4: |
396 | $positionValues = ['top' => $values[0], 'right' => $values[1], 'bottom' => $values[2], 'left' => $values[3]]; |
397 | break; |
398 | case 3: |
399 | $positionValues = ['top' => $values[0], 'right' => $values[1], 'bottom' => $values[2], 'left' => $values[1]]; |
400 | break; |
401 | case 2: |
402 | $positionValues = ['top' => $values[0], 'right' => $values[1], 'bottom' => $values[0], 'left' => $values[1]]; |
403 | break; |
404 | case 1: |
405 | $positionValues = ['top' => $values[0], 'right' => $values[0], 'bottom' => $values[0], 'left' => $values[0]]; |
406 | break; |
407 | } |
408 | |
409 | return $positionValues[$position]; |
410 | } else { |
411 | return (isset($this->properties[$name])) ? $this->properties[$name] : null; |
412 | } |
413 | } |
414 | |
415 | /** |
416 | * Magic method to return the isset value of $this->properties[$name] |
417 | * |
418 | * @param string $name |
419 | * @return bool |
420 | */ |
421 | public function __isset(string $name): bool |
422 | { |
423 | return isset($this->properties[$name]); |
424 | } |
425 | |
426 | /** |
427 | * Magic method to unset $this->properties[$name] |
428 | * |
429 | * @param string $name |
430 | * @return void |
431 | */ |
432 | public function __unset(string $name): void |
433 | { |
434 | if (isset($this->properties[$name])) { |
435 | unset($this->properties[$name]); |
436 | } |
437 | } |
438 | |
439 | /** |
440 | * ArrayAccess offsetExists |
441 | * |
442 | * @param mixed $offset |
443 | * @return bool |
444 | */ |
445 | public function offsetExists(mixed $offset): bool |
446 | { |
447 | return $this->__isset($offset); |
448 | } |
449 | |
450 | /** |
451 | * ArrayAccess offsetGet |
452 | * |
453 | * @param mixed $offset |
454 | * @return mixed |
455 | */ |
456 | public function offsetGet(mixed $offset): mixed |
457 | { |
458 | return $this->__get($offset); |
459 | } |
460 | |
461 | /** |
462 | * ArrayAccess offsetSet |
463 | * |
464 | * @param mixed $offset |
465 | * @param mixed $value |
466 | * @return void |
467 | */ |
468 | public function offsetSet(mixed $offset, mixed $value): void |
469 | { |
470 | $this->__set($offset, $value); |
471 | } |
472 | |
473 | /** |
474 | * ArrayAccess offsetUnset |
475 | * |
476 | * @param mixed $offset |
477 | * @return void |
478 | */ |
479 | public function offsetUnset(mixed $offset): void |
480 | { |
481 | $this->__unset($offset); |
482 | } |
483 | |
484 | } |