Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
# PHP CSS Parser
2
 
3
[![Build Status](https://github.com/sabberworm/PHP-CSS-Parser/workflows/CI/badge.svg?branch=master)](https://github.com/sabberworm/PHP-CSS-Parser/actions/)
4
 
5
A Parser for CSS Files written in PHP. Allows extraction of CSS files into a data structure, manipulation of said structure and output as (optimized) CSS.
6
 
7
## Usage
8
 
9
### Installation using Composer
10
 
11
```bash
12
composer require sabberworm/php-css-parser
13
```
14
 
15
### Extraction
16
 
17
To use the CSS Parser, create a new instance. The constructor takes the following form:
18
 
19
```php
20
new \Sabberworm\CSS\Parser($css);
21
```
22
 
23
To read a file, for example, you’d do the following:
24
 
25
```php
26
$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
27
$cssDocument = $parser->parse();
28
```
29
 
30
The resulting CSS document structure can be manipulated prior to being output.
31
 
32
### Options
33
 
34
#### Charset
35
 
36
The charset option will only be used if the CSS file does not contain an `@charset` declaration. UTF-8 is the default, so you won’t have to create a settings object at all if you don’t intend to change that.
37
 
38
```php
39
$settings = \Sabberworm\CSS\Settings::create()
40
    ->withDefaultCharset('windows-1252');
41
$parser = new \Sabberworm\CSS\Parser($css, $settings);
42
```
43
 
44
#### Strict parsing
45
 
46
To have the parser throw an exception when encountering invalid/unknown constructs (as opposed to trying to ignore them and carry on parsing), supply a thusly configured `\Sabberworm\CSS\Settings` object:
47
 
48
```php
49
$parser = new \Sabberworm\CSS\Parser(
50
    file_get_contents('somefile.css'),
51
    \Sabberworm\CSS\Settings::create()->beStrict()
52
);
53
```
54
 
55
Note that this will also disable a workaround for parsing the unquoted variant of the legacy IE-specific `filter` rule.
56
 
57
#### Disable multibyte functions
58
 
59
To achieve faster parsing, you can choose to have PHP-CSS-Parser use regular string functions instead of `mb_*` functions. This should work fine in most cases, even for UTF-8 files, as all the multibyte characters are in string literals. Still it’s not recommended using this with input you have no control over as it’s not thoroughly covered by test cases.
60
 
61
```php
62
$settings = \Sabberworm\CSS\Settings::create()->withMultibyteSupport(false);
63
$parser = new \Sabberworm\CSS\Parser($css, $settings);
64
```
65
 
66
### Manipulation
67
 
68
The resulting data structure consists mainly of five basic types: `CSSList`, `RuleSet`, `Rule`, `Selector` and `Value`. There are two additional types used: `Import` and `Charset`, which you won’t use often.
69
 
70
#### CSSList
71
 
72
`CSSList` represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc.
73
 
74
To access the items stored in a `CSSList` – like the document you got back when calling `$parser->parse()` –, use `getContents()`, then iterate over that collection and use `instanceof` to check whether you’re dealing with another `CSSList`, a `RuleSet`, a `Import` or a `Charset`.
75
 
76
To append a new item (selector, media query, etc.) to an existing `CSSList`, construct it using the constructor for this class and use the `append($oItem)` method.
77
 
78
#### RuleSet
79
 
80
`RuleSet` is a container for individual rules. The most common form of a rule set is one constrained by a selector. The following concrete subtypes exist:
81
 
82
* `AtRuleSet` – for generic at-rules for generic at-rules which are not covered by specific classes, i.e., not `@import`, `@charset` or `@media`. A common example for this is `@font-face`.
83
* `DeclarationBlock` – a `RuleSet` constrained by a `Selector`; contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the matching elements.
84
 
85
Note: A `CSSList` can contain other `CSSList`s (and `Import`s as well as a `Charset`), while a `RuleSet` can only contain `Rule`s.
86
 
87
If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` (which accepts either a `Rule` or a rule name; optionally suffixed by a dash to remove all related rules).
88
 
89
#### Rule
90
 
91
`Rule`s just have a string key (the rule) and a `Value`.
92
 
93
#### Value
94
 
95
`Value` is an abstract class that only defines the `render` method. The concrete subclasses for atomic value types are:
96
 
97
* `Size` – consists of a numeric `size` value and a unit.
98
* `Color` – colors can be input in the form #rrggbb, #rgb or schema(val1, val2, …) but are always stored as an array of ('s' => val1, 'c' => val2, 'h' => val3, …) and output in the second form.
99
* `CSSString` – this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes.
100
* `URL` – URLs in CSS; always output in `URL("")` notation.
101
 
102
There is another abstract subclass of `Value`, `ValueList`: A `ValueList` represents a lists of `Value`s, separated by some separation character (mostly `,`, whitespace, or `/`).
103
 
104
There are two types of `ValueList`s:
105
 
106
* `RuleValueList` – The default type, used to represent all multivalued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;` (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list and a comma-separated list).
107
* `CSSFunction` – A special kind of value that also contains a function name and where the values are the function’s arguments. Also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`.
108
 
109
#### Convenience methods
110
 
111
There are a few convenience methods on `Document` to ease finding, manipulating and deleting rules:
112
 
113
* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested the selectors are. Aliased as `getAllSelectors()`.
114
* `getAllRuleSets()` – does what it says; no matter how deeply nested the rule sets are.
115
* `getAllValues()` – finds all `Value` objects inside `Rule`s.
116
 
117
## To-Do
118
 
119
* More convenience methods (like `selectorsWithElement($sId/Class/TagName)`, `attributesOfType($type)`, `removeAttributesOfType($type)`)
120
* Real multibyte support. Currently, only multibyte charsets whose first 255 code points take up only one byte and are identical with ASCII are supported (yes, UTF-8 fits this description).
121
* Named color support (using `Color` instead of an anonymous string literal)
122
 
123
## Use cases
124
 
125
### Use `Parser` to prepend an ID to all selectors
126
 
127
```php
128
$myId = "#my_id";
129
$parser = new \Sabberworm\CSS\Parser($css);
130
$cssDocument = $parser->parse();
131
foreach ($cssDocument->getAllDeclarationBlocks() as $block) {
132
    foreach ($block->getSelectors() as $selector) {
133
        // Loop over all selector parts (the comma-separated strings in a
134
        // selector) and prepend the ID.
135
        $selector->setSelector($myId.' '.$selector->getSelector());
136
    }
137
}
138
```
139
 
140
### Shrink all absolute sizes to half
141
 
142
```php
143
$parser = new \Sabberworm\CSS\Parser($css);
144
$cssDocument = $parser->parse();
145
foreach ($cssDocument->getAllValues() as $value) {
146
    if ($value instanceof CSSSize && !$value->isRelative()) {
147
        $value->setSize($value->getSize() / 2);
148
    }
149
}
150
```
151
 
152
### Remove unwanted rules
153
 
154
```php
155
$parser = new \Sabberworm\CSS\Parser($css);
156
$cssDocument = $parser->parse();
157
foreach($cssDocument->getAllRuleSets() as $oRuleSet) {
158
    // Note that the added dash will make this remove all rules starting with
159
    // `font-` (like `font-size`, `font-weight`, etc.) as well as a potential
160
    // `font` rule.
161
    $oRuleSet->removeRule('font-');
162
    $oRuleSet->removeRule('cursor');
163
}
164
```
165
 
166
### Output
167
 
168
To output the entire CSS document into a variable, just use `->render()`:
169
 
170
```php
171
$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
172
$cssDocument = $parser->parse();
173
print $cssDocument->render();
174
```
175
 
176
If you want to format the output, pass an instance of type `\Sabberworm\CSS\OutputFormat`:
177
 
178
```php
179
$format = \Sabberworm\CSS\OutputFormat::create()
180
    ->indentWithSpaces(4)->setSpaceBetweenRules("\n");
181
print $cssDocument->render($format);
182
```
183
 
184
Or use one of the predefined formats:
185
 
186
```php
187
print $cssDocument->render(Sabberworm\CSS\OutputFormat::createPretty());
188
print $cssDocument->render(Sabberworm\CSS\OutputFormat::createCompact());
189
```
190
 
191
To see what you can do with output formatting, look at the tests in `tests/OutputFormatTest.php`.
192
 
193
## Examples
194
 
195
### Example 1 (At-Rules)
196
 
197
#### Input
198
 
199
```css
200
@charset "utf-8";
201
 
202
@font-face {
203
  font-family: "CrassRoots";
204
  src: url("../media/cr.ttf");
205
}
206
 
207
html, body {
208
    font-size: 1.6em;
209
}
210
 
211
@keyframes mymove {
212
    from { top: 0px; }
213
    to { top: 200px; }
214
}
215
 
216
```
217
 
218
<details>
219
  <summary><b>Structure (<code>var_dump()</code>)</b></summary>
220
 
221
```php
222
class Sabberworm\CSS\CSSList\Document#4 (2) {
223
  protected $aContents =>
224
  array(4) {
225
    [0] =>
226
    class Sabberworm\CSS\Property\Charset#6 (2) {
227
      private $sCharset =>
228
      class Sabberworm\CSS\Value\CSSString#5 (2) {
229
        private $sString =>
230
        string(5) "utf-8"
231
        protected $iLineNo =>
232
        int(1)
233
      }
234
      protected $iLineNo =>
235
      int(1)
236
    }
237
    [1] =>
238
    class Sabberworm\CSS\RuleSet\AtRuleSet#7 (4) {
239
      private $sType =>
240
      string(9) "font-face"
241
      private $sArgs =>
242
      string(0) ""
243
      private $aRules =>
244
      array(2) {
245
        'font-family' =>
246
        array(1) {
247
          [0] =>
248
          class Sabberworm\CSS\Rule\Rule#8 (4) {
249
            private $sRule =>
250
            string(11) "font-family"
251
            private $mValue =>
252
            class Sabberworm\CSS\Value\CSSString#9 (2) {
253
              private $sString =>
254
              string(10) "CrassRoots"
255
              protected $iLineNo =>
256
              int(4)
257
            }
258
            private $bIsImportant =>
259
            bool(false)
260
            protected $iLineNo =>
261
            int(4)
262
          }
263
        }
264
        'src' =>
265
        array(1) {
266
          [0] =>
267
          class Sabberworm\CSS\Rule\Rule#10 (4) {
268
            private $sRule =>
269
            string(3) "src"
270
            private $mValue =>
271
            class Sabberworm\CSS\Value\URL#11 (2) {
272
              private $oURL =>
273
              class Sabberworm\CSS\Value\CSSString#12 (2) {
274
                private $sString =>
275
                string(15) "../media/cr.ttf"
276
                protected $iLineNo =>
277
                int(5)
278
              }
279
              protected $iLineNo =>
280
              int(5)
281
            }
282
            private $bIsImportant =>
283
            bool(false)
284
            protected $iLineNo =>
285
            int(5)
286
          }
287
        }
288
      }
289
      protected $iLineNo =>
290
      int(3)
291
    }
292
    [2] =>
293
    class Sabberworm\CSS\RuleSet\DeclarationBlock#13 (3) {
294
      private $aSelectors =>
295
      array(2) {
296
        [0] =>
297
        class Sabberworm\CSS\Property\Selector#14 (2) {
298
          private $sSelector =>
299
          string(4) "html"
300
          private $iSpecificity =>
301
          NULL
302
        }
303
        [1] =>
304
        class Sabberworm\CSS\Property\Selector#15 (2) {
305
          private $sSelector =>
306
          string(4) "body"
307
          private $iSpecificity =>
308
          NULL
309
        }
310
      }
311
      private $aRules =>
312
      array(1) {
313
        'font-size' =>
314
        array(1) {
315
          [0] =>
316
          class Sabberworm\CSS\Rule\Rule#16 (4) {
317
            private $sRule =>
318
            string(9) "font-size"
319
            private $mValue =>
320
            class Sabberworm\CSS\Value\Size#17 (4) {
321
              private $fSize =>
322
              double(1.6)
323
              private $sUnit =>
324
              string(2) "em"
325
              private $bIsColorComponent =>
326
              bool(false)
327
              protected $iLineNo =>
328
              int(9)
329
            }
330
            private $bIsImportant =>
331
            bool(false)
332
            protected $iLineNo =>
333
            int(9)
334
          }
335
        }
336
      }
337
      protected $iLineNo =>
338
      int(8)
339
    }
340
    [3] =>
341
    class Sabberworm\CSS\CSSList\KeyFrame#18 (4) {
342
      private $vendorKeyFrame =>
343
      string(9) "keyframes"
344
      private $animationName =>
345
      string(6) "mymove"
346
      protected $aContents =>
347
      array(2) {
348
        [0] =>
349
        class Sabberworm\CSS\RuleSet\DeclarationBlock#19 (3) {
350
          private $aSelectors =>
351
          array(1) {
352
            [0] =>
353
            class Sabberworm\CSS\Property\Selector#20 (2) {
354
              private $sSelector =>
355
              string(4) "from"
356
              private $iSpecificity =>
357
              NULL
358
            }
359
          }
360
          private $aRules =>
361
          array(1) {
362
            'top' =>
363
            array(1) {
364
              [0] =>
365
              class Sabberworm\CSS\Rule\Rule#21 (4) {
366
                private $sRule =>
367
                string(3) "top"
368
                private $mValue =>
369
                class Sabberworm\CSS\Value\Size#22 (4) {
370
                  private $fSize =>
371
                  double(0)
372
                  private $sUnit =>
373
                  string(2) "px"
374
                  private $bIsColorComponent =>
375
                  bool(false)
376
                  protected $iLineNo =>
377
                  int(13)
378
                }
379
                private $bIsImportant =>
380
                bool(false)
381
                protected $iLineNo =>
382
                int(13)
383
              }
384
            }
385
          }
386
          protected $iLineNo =>
387
          int(13)
388
        }
389
        [1] =>
390
        class Sabberworm\CSS\RuleSet\DeclarationBlock#23 (3) {
391
          private $aSelectors =>
392
          array(1) {
393
            [0] =>
394
            class Sabberworm\CSS\Property\Selector#24 (2) {
395
              private $sSelector =>
396
              string(2) "to"
397
              private $iSpecificity =>
398
              NULL
399
            }
400
          }
401
          private $aRules =>
402
          array(1) {
403
            'top' =>
404
            array(1) {
405
              [0] =>
406
              class Sabberworm\CSS\Rule\Rule#25 (4) {
407
                private $sRule =>
408
                string(3) "top"
409
                private $mValue =>
410
                class Sabberworm\CSS\Value\Size#26 (4) {
411
                  private $fSize =>
412
                  double(200)
413
                  private $sUnit =>
414
                  string(2) "px"
415
                  private $bIsColorComponent =>
416
                  bool(false)
417
                  protected $iLineNo =>
418
                  int(14)
419
                }
420
                private $bIsImportant =>
421
                bool(false)
422
                protected $iLineNo =>
423
                int(14)
424
              }
425
            }
426
          }
427
          protected $iLineNo =>
428
          int(14)
429
        }
430
      }
431
      protected $iLineNo =>
432
      int(12)
433
    }
434
  }
435
  protected $iLineNo =>
436
  int(1)
437
}
438
 
439
```
440
</details>
441
 
442
#### Output (`render()`)
443
 
444
```css
445
@charset "utf-8";
446
@font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");}
447
html, body {font-size: 1.6em;}
448
@keyframes mymove {from {top: 0px;} to {top: 200px;}}
449
```
450
 
451
### Example 2 (Values)
452
 
453
#### Input
454
 
455
```css
456
#header {
457
    margin: 10px 2em 1cm 2%;
458
    font-family: Verdana, Helvetica, "Gill Sans", sans-serif;
459
    color: red !important;
460
}
461
 
462
```
463
 
464
<details>
465
  <summary><b>Structure (<code>var_dump()</code>)</b></summary>
466
 
467
```php
468
class Sabberworm\CSS\CSSList\Document#4 (2) {
469
  protected $aContents =>
470
  array(1) {
471
    [0] =>
472
    class Sabberworm\CSS\RuleSet\DeclarationBlock#5 (3) {
473
      private $aSelectors =>
474
      array(1) {
475
        [0] =>
476
        class Sabberworm\CSS\Property\Selector#6 (2) {
477
          private $sSelector =>
478
          string(7) "#header"
479
          private $iSpecificity =>
480
          NULL
481
        }
482
      }
483
      private $aRules =>
484
      array(3) {
485
        'margin' =>
486
        array(1) {
487
          [0] =>
488
          class Sabberworm\CSS\Rule\Rule#7 (4) {
489
            private $sRule =>
490
            string(6) "margin"
491
            private $mValue =>
492
            class Sabberworm\CSS\Value\RuleValueList#12 (3) {
493
              protected $aComponents =>
494
              array(4) {
495
                [0] =>
496
                class Sabberworm\CSS\Value\Size#8 (4) {
497
                  private $fSize =>
498
                  double(10)
499
                  private $sUnit =>
500
                  string(2) "px"
501
                  private $bIsColorComponent =>
502
                  bool(false)
503
                  protected $iLineNo =>
504
                  int(2)
505
                }
506
                [1] =>
507
                class Sabberworm\CSS\Value\Size#9 (4) {
508
                  private $fSize =>
509
                  double(2)
510
                  private $sUnit =>
511
                  string(2) "em"
512
                  private $bIsColorComponent =>
513
                  bool(false)
514
                  protected $iLineNo =>
515
                  int(2)
516
                }
517
                [2] =>
518
                class Sabberworm\CSS\Value\Size#10 (4) {
519
                  private $fSize =>
520
                  double(1)
521
                  private $sUnit =>
522
                  string(2) "cm"
523
                  private $bIsColorComponent =>
524
                  bool(false)
525
                  protected $iLineNo =>
526
                  int(2)
527
                }
528
                [3] =>
529
                class Sabberworm\CSS\Value\Size#11 (4) {
530
                  private $fSize =>
531
                  double(2)
532
                  private $sUnit =>
533
                  string(1) "%"
534
                  private $bIsColorComponent =>
535
                  bool(false)
536
                  protected $iLineNo =>
537
                  int(2)
538
                }
539
              }
540
              protected $sSeparator =>
541
              string(1) " "
542
              protected $iLineNo =>
543
              int(2)
544
            }
545
            private $bIsImportant =>
546
            bool(false)
547
            protected $iLineNo =>
548
            int(2)
549
          }
550
        }
551
        'font-family' =>
552
        array(1) {
553
          [0] =>
554
          class Sabberworm\CSS\Rule\Rule#13 (4) {
555
            private $sRule =>
556
            string(11) "font-family"
557
            private $mValue =>
558
            class Sabberworm\CSS\Value\RuleValueList#15 (3) {
559
              protected $aComponents =>
560
              array(4) {
561
                [0] =>
562
                string(7) "Verdana"
563
                [1] =>
564
                string(9) "Helvetica"
565
                [2] =>
566
                class Sabberworm\CSS\Value\CSSString#14 (2) {
567
                  private $sString =>
568
                  string(9) "Gill Sans"
569
                  protected $iLineNo =>
570
                  int(3)
571
                }
572
                [3] =>
573
                string(10) "sans-serif"
574
              }
575
              protected $sSeparator =>
576
              string(1) ","
577
              protected $iLineNo =>
578
              int(3)
579
            }
580
            private $bIsImportant =>
581
            bool(false)
582
            protected $iLineNo =>
583
            int(3)
584
          }
585
        }
586
        'color' =>
587
        array(1) {
588
          [0] =>
589
          class Sabberworm\CSS\Rule\Rule#16 (4) {
590
            private $sRule =>
591
            string(5) "color"
592
            private $mValue =>
593
            string(3) "red"
594
            private $bIsImportant =>
595
            bool(true)
596
            protected $iLineNo =>
597
            int(4)
598
          }
599
        }
600
      }
601
      protected $iLineNo =>
602
      int(1)
603
    }
604
  }
605
  protected $iLineNo =>
606
  int(1)
607
}
608
 
609
```
610
</details>
611
 
612
#### Output (`render()`)
613
 
614
```css
615
#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;}
616
```
617
 
618
## Contributors/Thanks to
619
 
620
* [oliverklee](https://github.com/oliverklee) for lots of refactorings, code modernizations and CI integrations
621
* [raxbg](https://github.com/raxbg) for contributions to parse `calc`, grid lines, and various bugfixes.
622
* [westonruter](https://github.com/westonruter) for bugfixes and improvements.
623
* [FMCorz](https://github.com/FMCorz) for many patches and suggestions, for being able to parse comments and IE hacks (in lenient mode).
624
* [Lullabot](https://github.com/Lullabot) for a patch that allows to know the line number for each parsed token.
625
* [ju1ius](https://github.com/ju1ius) for the specificity parsing code and the ability to expand/compact shorthand properties.
626
* [ossinkine](https://github.com/ossinkine) for a 150 time performance boost.
627
* [GaryJones](https://github.com/GaryJones) for lots of input and [https://css-specificity.info/](https://css-specificity.info/).
628
* [docteurklein](https://github.com/docteurklein) for output formatting and `CSSList->remove()` inspiration.
629
* [nicolopignatelli](https://github.com/nicolopignatelli) for PSR-0 compatibility.
630
* [diegoembarcadero](https://github.com/diegoembarcadero) for keyframe at-rule parsing.
631
* [goetas](https://github.com/goetas) for @namespace at-rule support.
632
* [View full list](https://github.com/sabberworm/PHP-CSS-Parser/contributors)
633
 
634
## Misc
635
 
636
* Legacy Support: The latest pre-PSR-0 version of this project can be checked with the `0.9.0` tag.
637
* Running Tests: To run all unit tests for this project, run `composer install` to install phpunit and use `./vendor/bin/phpunit`.