GithubHelp home page GithubHelp logo

sabberworm / php-css-parser Goto Github PK

View Code? Open in Web Editor NEW
1.7K 27.0 141.0 1.64 MB

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

Home Page: http://www.sabberworm.com/blog/2010/6/10/php-css-parser

License: MIT License

PHP 100.00%
hacktoberfest composer-package css parser

php-css-parser's Introduction

PHP CSS Parser

Build Status

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.

Usage

Installation using Composer

composer require sabberworm/php-css-parser

Extraction

To use the CSS Parser, create a new instance. The constructor takes the following form:

new \Sabberworm\CSS\Parser($css);

To read a file, for example, you’d do the following:

$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
$cssDocument = $parser->parse();

The resulting CSS document structure can be manipulated prior to being output.

Options

Charset

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.

$settings = \Sabberworm\CSS\Settings::create()
    ->withDefaultCharset('windows-1252');
$parser = new \Sabberworm\CSS\Parser($css, $settings);

Strict parsing

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:

$parser = new \Sabberworm\CSS\Parser(
    file_get_contents('somefile.css'),
    \Sabberworm\CSS\Settings::create()->beStrict()
);

Note that this will also disable a workaround for parsing the unquoted variant of the legacy IE-specific filter rule.

Disable multibyte functions

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.

$settings = \Sabberworm\CSS\Settings::create()->withMultibyteSupport(false);
$parser = new \Sabberworm\CSS\Parser($css, $settings);

Manipulation

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.

CSSList

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.

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.

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.

RuleSet

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:

  • 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.
  • 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.

Note: A CSSList can contain other CSSLists (and Imports as well as a Charset), while a RuleSet can only contain Rules.

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).

Rule

Rules just have a string key (the rule) and a Value.

Value

Value is an abstract class that only defines the render method. The concrete subclasses for atomic value types are:

  • Size – consists of a numeric size value and a unit.
  • 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.
  • CSSString – this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes.
  • URL – URLs in CSS; always output in URL("") notation.

There is another abstract subclass of Value, ValueList: A ValueList represents a lists of Values, separated by some separation character (mostly ,, whitespace, or /).

There are two types of ValueLists:

  • 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).
  • 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);.

Convenience methods

There are a few convenience methods on Document to ease finding, manipulating and deleting rules:

  • getAllDeclarationBlocks() – does what it says; no matter how deeply nested the selectors are. Aliased as getAllSelectors().
  • getAllRuleSets() – does what it says; no matter how deeply nested the rule sets are.
  • getAllValues() – finds all Value objects inside Rules.

To-Do

  • More convenience methods (like selectorsWithElement($sId/Class/TagName), attributesOfType($type), removeAttributesOfType($type))
  • 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).
  • Named color support (using Color instead of an anonymous string literal)

Use cases

Use Parser to prepend an ID to all selectors

$myId = "#my_id";
$parser = new \Sabberworm\CSS\Parser($css);
$cssDocument = $parser->parse();
foreach ($cssDocument->getAllDeclarationBlocks() as $block) {
    foreach ($block->getSelectors() as $selector) {
        // Loop over all selector parts (the comma-separated strings in a
        // selector) and prepend the ID.
        $selector->setSelector($myId.' '.$selector->getSelector());
    }
}

Shrink all absolute sizes to half

$parser = new \Sabberworm\CSS\Parser($css);
$cssDocument = $parser->parse();
foreach ($cssDocument->getAllValues() as $value) {
    if ($value instanceof CSSSize && !$value->isRelative()) {
        $value->setSize($value->getSize() / 2);
    }
}

Remove unwanted rules

$parser = new \Sabberworm\CSS\Parser($css);
$cssDocument = $parser->parse();
foreach($cssDocument->getAllRuleSets() as $oRuleSet) {
    // Note that the added dash will make this remove all rules starting with
    // `font-` (like `font-size`, `font-weight`, etc.) as well as a potential
    // `font` rule.
    $oRuleSet->removeRule('font-');
    $oRuleSet->removeRule('cursor');
}

Output

To output the entire CSS document into a variable, just use ->render():

$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
$cssDocument = $parser->parse();
print $cssDocument->render();

If you want to format the output, pass an instance of type \Sabberworm\CSS\OutputFormat:

$format = \Sabberworm\CSS\OutputFormat::create()
    ->indentWithSpaces(4)->setSpaceBetweenRules("\n");
print $cssDocument->render($format);

Or use one of the predefined formats:

print $cssDocument->render(Sabberworm\CSS\OutputFormat::createPretty());
print $cssDocument->render(Sabberworm\CSS\OutputFormat::createCompact());

To see what you can do with output formatting, look at the tests in tests/OutputFormatTest.php.

Examples

Example 1 (At-Rules)

Input

@charset "utf-8";

@font-face {
  font-family: "CrassRoots";
  src: url("../media/cr.ttf");
}

html, body {
    font-size: 1.6em;
}

@keyframes mymove {
    from { top: 0px; }
    to { top: 200px; }
}
Structure (var_dump())
class Sabberworm\CSS\CSSList\Document#4 (2) {
  protected $aContents =>
  array(4) {
    [0] =>
    class Sabberworm\CSS\Property\Charset#6 (2) {
      private $sCharset =>
      class Sabberworm\CSS\Value\CSSString#5 (2) {
        private $sString =>
        string(5) "utf-8"
        protected $iLineNo =>
        int(1)
      }
      protected $iLineNo =>
      int(1)
    }
    [1] =>
    class Sabberworm\CSS\RuleSet\AtRuleSet#7 (4) {
      private $sType =>
      string(9) "font-face"
      private $sArgs =>
      string(0) ""
      private $aRules =>
      array(2) {
        'font-family' =>
        array(1) {
          [0] =>
          class Sabberworm\CSS\Rule\Rule#8 (4) {
            private $sRule =>
            string(11) "font-family"
            private $mValue =>
            class Sabberworm\CSS\Value\CSSString#9 (2) {
              private $sString =>
              string(10) "CrassRoots"
              protected $iLineNo =>
              int(4)
            }
            private $bIsImportant =>
            bool(false)
            protected $iLineNo =>
            int(4)
          }
        }
        'src' =>
        array(1) {
          [0] =>
          class Sabberworm\CSS\Rule\Rule#10 (4) {
            private $sRule =>
            string(3) "src"
            private $mValue =>
            class Sabberworm\CSS\Value\URL#11 (2) {
              private $oURL =>
              class Sabberworm\CSS\Value\CSSString#12 (2) {
                private $sString =>
                string(15) "../media/cr.ttf"
                protected $iLineNo =>
                int(5)
              }
              protected $iLineNo =>
              int(5)
            }
            private $bIsImportant =>
            bool(false)
            protected $iLineNo =>
            int(5)
          }
        }
      }
      protected $iLineNo =>
      int(3)
    }
    [2] =>
    class Sabberworm\CSS\RuleSet\DeclarationBlock#13 (3) {
      private $aSelectors =>
      array(2) {
        [0] =>
        class Sabberworm\CSS\Property\Selector#14 (2) {
          private $sSelector =>
          string(4) "html"
          private $iSpecificity =>
          NULL
        }
        [1] =>
        class Sabberworm\CSS\Property\Selector#15 (2) {
          private $sSelector =>
          string(4) "body"
          private $iSpecificity =>
          NULL
        }
      }
      private $aRules =>
      array(1) {
        'font-size' =>
        array(1) {
          [0] =>
          class Sabberworm\CSS\Rule\Rule#16 (4) {
            private $sRule =>
            string(9) "font-size"
            private $mValue =>
            class Sabberworm\CSS\Value\Size#17 (4) {
              private $fSize =>
              double(1.6)
              private $sUnit =>
              string(2) "em"
              private $bIsColorComponent =>
              bool(false)
              protected $iLineNo =>
              int(9)
            }
            private $bIsImportant =>
            bool(false)
            protected $iLineNo =>
            int(9)
          }
        }
      }
      protected $iLineNo =>
      int(8)
    }
    [3] =>
    class Sabberworm\CSS\CSSList\KeyFrame#18 (4) {
      private $vendorKeyFrame =>
      string(9) "keyframes"
      private $animationName =>
      string(6) "mymove"
      protected $aContents =>
      array(2) {
        [0] =>
        class Sabberworm\CSS\RuleSet\DeclarationBlock#19 (3) {
          private $aSelectors =>
          array(1) {
            [0] =>
            class Sabberworm\CSS\Property\Selector#20 (2) {
              private $sSelector =>
              string(4) "from"
              private $iSpecificity =>
              NULL
            }
          }
          private $aRules =>
          array(1) {
            'top' =>
            array(1) {
              [0] =>
              class Sabberworm\CSS\Rule\Rule#21 (4) {
                private $sRule =>
                string(3) "top"
                private $mValue =>
                class Sabberworm\CSS\Value\Size#22 (4) {
                  private $fSize =>
                  double(0)
                  private $sUnit =>
                  string(2) "px"
                  private $bIsColorComponent =>
                  bool(false)
                  protected $iLineNo =>
                  int(13)
                }
                private $bIsImportant =>
                bool(false)
                protected $iLineNo =>
                int(13)
              }
            }
          }
          protected $iLineNo =>
          int(13)
        }
        [1] =>
        class Sabberworm\CSS\RuleSet\DeclarationBlock#23 (3) {
          private $aSelectors =>
          array(1) {
            [0] =>
            class Sabberworm\CSS\Property\Selector#24 (2) {
              private $sSelector =>
              string(2) "to"
              private $iSpecificity =>
              NULL
            }
          }
          private $aRules =>
          array(1) {
            'top' =>
            array(1) {
              [0] =>
              class Sabberworm\CSS\Rule\Rule#25 (4) {
                private $sRule =>
                string(3) "top"
                private $mValue =>
                class Sabberworm\CSS\Value\Size#26 (4) {
                  private $fSize =>
                  double(200)
                  private $sUnit =>
                  string(2) "px"
                  private $bIsColorComponent =>
                  bool(false)
                  protected $iLineNo =>
                  int(14)
                }
                private $bIsImportant =>
                bool(false)
                protected $iLineNo =>
                int(14)
              }
            }
          }
          protected $iLineNo =>
          int(14)
        }
      }
      protected $iLineNo =>
      int(12)
    }
  }
  protected $iLineNo =>
  int(1)
}

Output (render())

@charset "utf-8";
@font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");}
html, body {font-size: 1.6em;}
@keyframes mymove {from {top: 0px;} to {top: 200px;}}

Example 2 (Values)

Input

#header {
    margin: 10px 2em 1cm 2%;
    font-family: Verdana, Helvetica, "Gill Sans", sans-serif;
    color: red !important;
}
Structure (var_dump())
class Sabberworm\CSS\CSSList\Document#4 (2) {
  protected $aContents =>
  array(1) {
    [0] =>
    class Sabberworm\CSS\RuleSet\DeclarationBlock#5 (3) {
      private $aSelectors =>
      array(1) {
        [0] =>
        class Sabberworm\CSS\Property\Selector#6 (2) {
          private $sSelector =>
          string(7) "#header"
          private $iSpecificity =>
          NULL
        }
      }
      private $aRules =>
      array(3) {
        'margin' =>
        array(1) {
          [0] =>
          class Sabberworm\CSS\Rule\Rule#7 (4) {
            private $sRule =>
            string(6) "margin"
            private $mValue =>
            class Sabberworm\CSS\Value\RuleValueList#12 (3) {
              protected $aComponents =>
              array(4) {
                [0] =>
                class Sabberworm\CSS\Value\Size#8 (4) {
                  private $fSize =>
                  double(10)
                  private $sUnit =>
                  string(2) "px"
                  private $bIsColorComponent =>
                  bool(false)
                  protected $iLineNo =>
                  int(2)
                }
                [1] =>
                class Sabberworm\CSS\Value\Size#9 (4) {
                  private $fSize =>
                  double(2)
                  private $sUnit =>
                  string(2) "em"
                  private $bIsColorComponent =>
                  bool(false)
                  protected $iLineNo =>
                  int(2)
                }
                [2] =>
                class Sabberworm\CSS\Value\Size#10 (4) {
                  private $fSize =>
                  double(1)
                  private $sUnit =>
                  string(2) "cm"
                  private $bIsColorComponent =>
                  bool(false)
                  protected $iLineNo =>
                  int(2)
                }
                [3] =>
                class Sabberworm\CSS\Value\Size#11 (4) {
                  private $fSize =>
                  double(2)
                  private $sUnit =>
                  string(1) "%"
                  private $bIsColorComponent =>
                  bool(false)
                  protected $iLineNo =>
                  int(2)
                }
              }
              protected $sSeparator =>
              string(1) " "
              protected $iLineNo =>
              int(2)
            }
            private $bIsImportant =>
            bool(false)
            protected $iLineNo =>
            int(2)
          }
        }
        'font-family' =>
        array(1) {
          [0] =>
          class Sabberworm\CSS\Rule\Rule#13 (4) {
            private $sRule =>
            string(11) "font-family"
            private $mValue =>
            class Sabberworm\CSS\Value\RuleValueList#15 (3) {
              protected $aComponents =>
              array(4) {
                [0] =>
                string(7) "Verdana"
                [1] =>
                string(9) "Helvetica"
                [2] =>
                class Sabberworm\CSS\Value\CSSString#14 (2) {
                  private $sString =>
                  string(9) "Gill Sans"
                  protected $iLineNo =>
                  int(3)
                }
                [3] =>
                string(10) "sans-serif"
              }
              protected $sSeparator =>
              string(1) ","
              protected $iLineNo =>
              int(3)
            }
            private $bIsImportant =>
            bool(false)
            protected $iLineNo =>
            int(3)
          }
        }
        'color' =>
        array(1) {
          [0] =>
          class Sabberworm\CSS\Rule\Rule#16 (4) {
            private $sRule =>
            string(5) "color"
            private $mValue =>
            string(3) "red"
            private $bIsImportant =>
            bool(true)
            protected $iLineNo =>
            int(4)
          }
        }
      }
      protected $iLineNo =>
      int(1)
    }
  }
  protected $iLineNo =>
  int(1)
}

Output (render())

#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;}

Class diagram

classDiagram
    direction LR

    %% Start of the part generated from the PHP code using tasuku43/mermaid-class-diagram

    class Renderable {
        <<interface>>
    }
    class DeclarationBlock {
    }
    class RuleSet {
        <<abstract>>
    }
    class AtRuleSet {
    }
    class KeyframeSelector {
    }
    class AtRule {
        <<interface>>
    }
    class Charset {
    }
    class Import {
    }
    class Selector {
    }
    class CSSNamespace {
    }
    class Settings {
    }
    class Rule {
    }
    class Parser {
    }
    class OutputFormatter {
    }
    class OutputFormat {
    }
    class OutputException {
    }
    class UnexpectedEOFException {
    }
    class SourceException {
    }
    class UnexpectedTokenException {
    }
    class ParserState {
    }
    class Anchor {
    }
    class CSSBlockList {
        <<abstract>>
    }
    class Document {
    }
    class CSSList {
        <<abstract>>
    }
    class KeyFrame {
    }
    class AtRuleBlockList {
    }
    class Color {
    }
    class URL {
    }
    class CalcRuleValueList {
    }
    class ValueList {
        <<abstract>>
    }
    class CalcFunction {
    }
    class LineName {
    }
    class Value {
        <<abstract>>
    }
    class Size {
    }
    class CSSString {
    }
    class PrimitiveValue {
        <<abstract>>
    }
    class CSSFunction {
    }
    class RuleValueList {
    }
    class Commentable {
        <<interface>>
    }
    class Comment {
    }

    RuleSet <|-- DeclarationBlock: inheritance
    Renderable <|.. RuleSet: realization
    Commentable <|.. RuleSet: realization
    RuleSet <|-- AtRuleSet: inheritance
    AtRule <|.. AtRuleSet: realization
    Selector <|-- KeyframeSelector: inheritance
    Renderable <|-- AtRule: inheritance
    Commentable <|-- AtRule: inheritance
    AtRule <|.. Charset: realization
    AtRule <|.. Import: realization
    AtRule <|.. CSSNamespace: realization
    Renderable <|.. Rule: realization
    Commentable <|.. Rule: realization
    SourceException <|-- OutputException: inheritance
    UnexpectedTokenException <|-- UnexpectedEOFException: inheritance
    Exception <|-- SourceException: inheritance
    SourceException <|-- UnexpectedTokenException: inheritance
    CSSList <|-- CSSBlockList: inheritance
    CSSBlockList <|-- Document: inheritance
    Renderable <|.. CSSList: realization
    Commentable <|.. CSSList: realization
    CSSList <|-- KeyFrame: inheritance
    AtRule <|.. KeyFrame: realization
    CSSBlockList <|-- AtRuleBlockList: inheritance
    AtRule <|.. AtRuleBlockList: realization
    CSSFunction <|-- Color: inheritance
    PrimitiveValue <|-- URL: inheritance
    RuleValueList <|-- CalcRuleValueList: inheritance
    Value <|-- ValueList: inheritance
    CSSFunction <|-- CalcFunction: inheritance
    ValueList <|-- LineName: inheritance
    Renderable <|.. Value: realization
    PrimitiveValue <|-- Size: inheritance
    PrimitiveValue <|-- CSSString: inheritance
    Value <|-- PrimitiveValue: inheritance
    ValueList <|-- CSSFunction: inheritance
    ValueList <|-- RuleValueList: inheritance
    Renderable <|.. Comment: realization

    %% end of the generated part


    Anchor --> "1" ParserState : oParserState
    CSSList --> "*" CSSList : aContents
    CSSList --> "*" Charset : aContents
    CSSList --> "*" Comment : aComments
    CSSList --> "*" Import : aContents
    CSSList --> "*" RuleSet : aContents
    CSSNamespace --> "*" Comment : aComments
    Charset --> "*" Comment : aComments
    Charset --> "1" CSSString : oCharset
    DeclarationBlock --> "*" Selector : aSelectors
    Import --> "*" Comment : aComments
    OutputFormat --> "1" OutputFormat : oNextLevelFormat
    OutputFormat --> "1" OutputFormatter : oFormatter
    OutputFormatter --> "1" OutputFormat : oFormat
    Parser --> "1" ParserState : oParserState
    ParserState --> "1" Settings : oParserSettings
    Rule --> "*" Comment : aComments
    Rule --> "1" RuleValueList : mValue
    RuleSet --> "*" Comment : aComments
    RuleSet --> "*" Rule : aRules
    URL --> "1" CSSString : oURL
    ValueList --> "*" Value : aComponents

Contributors/Thanks to

  • oliverklee for lots of refactorings, code modernizations and CI integrations
  • raxbg for contributions to parse calc, grid lines, and various bugfixes.
  • westonruter for bugfixes and improvements.
  • FMCorz for many patches and suggestions, for being able to parse comments and IE hacks (in lenient mode).
  • Lullabot for a patch that allows to know the line number for each parsed token.
  • ju1ius for the specificity parsing code and the ability to expand/compact shorthand properties.
  • ossinkine for a 150 time performance boost.
  • GaryJones for lots of input and https://css-specificity.info/.
  • docteurklein for output formatting and CSSList->remove() inspiration.
  • nicolopignatelli for PSR-0 compatibility.
  • diegoembarcadero for keyframe at-rule parsing.
  • goetas for @namespace at-rule support.
  • View full list

Misc

Legacy Support

The latest pre-PSR-0 version of this project can be checked with the 0.9.0 tag.

Running Tests

To run all continuous integration (CI) checks for this project (including unit tests),

  • run composer install to install the development dependencies managed with Composer;
  • run phive install to install the development dependencies managed with PHIVE;
  • run composer ci to run all static and dynamic CI checks.

Details of other Composer scripts available (e.g. to run one specific CI check) are provided with composer list.

php-css-parser's People

Contributors

ali-khalili avatar bartbutler avatar dependabot[bot] avatar diegoembarcadero avatar digilist avatar docteurklein avatar eddymens avatar goetas avatar hkarlstrom avatar icereaper avatar idrinth avatar jakeqz avatar kaiongithub avatar lukasz-jakub-adamczuk avatar mathroc avatar nicolopignatelli avatar oliverklee avatar ossinkine avatar paulannekov avatar raxbg avatar ruud68 avatar sabberworm avatar shannmcnicholl avatar sidkshatriya avatar slusarz avatar szepeviktor avatar timwolla avatar viezel avatar westonruter avatar yunosh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-css-parser's Issues

incorrect handling of multiple values

div.main
{
  background-image: url("foo.png") top left no-repeat,
                    url("bar.png") bottom right no-repeat;
}

This rule obviously has two values, themselves consisting of 4 values.

Expected result of CSSDocument::getAllRuleSets():

array(1) {
  [0]=>
  object(CSSDeclarationBlock)#3 (2) {
    ["aSelectors":"CSSDeclarationBlock":private]=>
    array(1) {
      [0]=>
      object(CSSSelector)#4 (2) {
        ["sSelector":"CSSSelector":private]=>
        string(8) "div.main"
        ["iSpecificity":"CSSSelector":private]=>
        NULL
      }
    }
    ["aRules":"CSSRuleSet":private]=>
    array(1) {
      ["background-image"]=>
      object(CSSRule)#5 (3) {
        ["sRule":"CSSRule":private]=>
        string(16) "background-image"
        ["aValues":"CSSRule":private]=>
        array(2) {
          [0]=>
          array(4) {
            [0]=>
            object(CSSURL)#6 (1) {
              ["oURL":"CSSURL":private]=>
              object(CSSString)#7 (1) {
                ["sString":"CSSString":private]=>
                string(7) "foo.png"
              }
            }
            [1]=>
            string(3) "top"
            [2]=>
            string(4) "left"
            [3]=>
            string(9) "no-repeat"
          }
          array(4) {
            [0]=>
            object(CSSURL)#6 (1) {
              ["oURL":"CSSURL":private]=>
              object(CSSString)#7 (1) {
                ["sString":"CSSString":private]=>
                string(7) "bar.png"
              }
            }
            [1]=>
            string(3) "bottom"
            [2]=>
            string(4) "right"
            [3]=>
            string(9) "no-repeat"
          }
        }
        ["bIsImportant":"CSSRule":private]=>
        bool(false)
      }
    }
  }
}
array(1) {
  [0]=>
  object(CSSDeclarationBlock)#3 (2) {
    ["aSelectors":"CSSDeclarationBlock":private]=>
    array(1) {
      [0]=>
      object(CSSSelector)#4 (2) {
        ["sSelector":"CSSSelector":private]=>
        string(8) "div.main"
        ["iSpecificity":"CSSSelector":private]=>
        NULL
      }
    }
    ["aRules":"CSSRuleSet":private]=>
    array(1) {
      ["background-image"]=>
      object(CSSRule)#5 (3) {
        ["sRule":"CSSRule":private]=>
        string(16) "background-image"
        ["aValues":"CSSRule":private]=>
        array(7) {
          [0]=>
          array(1) {
            [0]=>
            object(CSSURL)#6 (1) {
              ["oURL":"CSSURL":private]=>
              object(CSSString)#7 (1) {
                ["sString":"CSSString":private]=>
                string(7) "foo.png"
              }
            }
          }
          [1]=>
          array(1) {
            [0]=>
            string(3) "top"
          }
          [2]=>
          array(1) {
            [0]=>
            string(4) "left"
          }
          [3]=>
          array(2) {
            [0]=>
            string(9) "no-repeat"
            [1]=>
            object(CSSURL)#8 (1) {
              ["oURL":"CSSURL":private]=>
              object(CSSString)#9 (1) {
                ["sString":"CSSString":private]=>
                string(7) "bar.png"
              }
            }
          }
          [4]=>
          array(1) {
            [0]=>
            string(6) "bottom"
          }
          [5]=>
          array(1) {
            [0]=>
            string(5) "right"
          }
          [6]=>
          array(1) {
            [0]=>
            string(9) "no-repeat"
          }
        }
        ["bIsImportant":"CSSRule":private]=>
        bool(false)
      }
    }
  }
}

No support for css functions

div.main{
  background-image: linear-gradient(#000, #fff);
}

Stack trace:

PHP Fatal error:  Uncaught exception 'Exception' with message 'Identifier expected, got (#000' in /home/ju1ius/code/php/third-party/PHP-CSS-Parser/CSSParser.php:98
Stack trace:
#0 CSSParser.php(247): CSSParser->parseIdentifier()
#1 CSSParser.php(216): CSSParser->parseValue()
#2 CSSParser.php(204): CSSParser->parseRule()
#3 CSSParser.php(198): CSSParser->parseRuleSet(Object(CSSDeclarationBlock))
#4 CSSParser.php(50): CSSParser->parseSelector()
#5 CSSParser.php(35): CSSParser->parseList(Object(CSSDocument), true)
#6 CSSParser.php(29): CSSParser->parseDocument(Object(CSSDocument))
#7 test.php(84): CSSParser->parse()
#8 {main}
  thrown in CSSParser.php on line 98

Parse Exception for valid css

This selector wont pass, and I get a Identifier expected. Got ":80em"

This is the selector
@media(min-width:80em){a[href^=fax],a[href^=tel]{cursor:text;pointer-events:none}a[href^=fax]:hover,a[href^=tel]:hover{text-decoration:none}}

Double quoting in CSSURL value

Not sure if bug or feature.

When parsing this:

body.home {
  background-image: url('images/bg_body_home.jpg');
  background-repeat: repeat-x;
}

I get this:


object(CSSURL)[6]
  private 'oURL' => 
    object(CSSString)[7]
      private 'sString' => string ''images/bg_body_home.jpg'' (length=25)

(Note the double quoting in the sString value)

The problem seems to be in __toString() method of CSString:


public function __toString() {
        $sString = addslashes($this->sString);
        $sString = str_replace("\n", '\A', $sString);
        return '"'.$sString.'"';
    }

I fixed it by modifying __toString() method in CSSUrl class:

    public function __toString() {
    $this->oURL = str_replace("'", '', $this->oURL);
    $this->oURL = str_replace('"', '', $this->oURL);
    $this->oURL = str_replace("\\", "'", $this->oURL);
    $sUrl = str_replace("\n", '\A', $this->oURL);
        return "url({$sUrl})";
    }

(A botch, I know)

Require? Include?

To read a file, for example, you’d do the following:

REQUIRE('......................................'); <-- And what file do we call?

$oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
$oCssDocument = $oCssParser->parse();

webkit-linear-gradient bug

This is a perfectly valid CSS5 rule...

-webkit-linear-gradient(top right, white, black)

After parsing this gets mangled to

-webkit-linear-gradient(top)

Parse Comments?

Would it be possible to add the ability to parse comments that are both associated with a specific declaration block (aka are within the rule's opening and closing braces), and just global comments that are at the root document level?

For instance, for a CSS snippet like this:

h1 {
  /* my comment */
  color: #000000;
  font-size: 24px;
}

Would be parsed to (in simplified structure):

    declaration block =>
        selector =>  "h1"
        comments => array
            "my comment"
        ruleset => array
            ....

Error with incorrect CSS

#home .bg-layout { background-image: url(/bundles/main/img/bg1.png?5);};

This css content is valid, but failed parse process (Last ";").
Thank.

documentation? easier functions?

This class seems to be way too complicated. With hardly any documentation.

how do i get a class from a css file and change it? Sounds like a simple thing to do, but not with this class.

Support CSS3 calc()

Following document throws Sabberworm\CSS\Parsing\UnexpectedTokenException with message Identifier expected. Got “+ 20p”. This is expected to be valid CSS document:

body
{
    border-top: solid green 0em;
    border-top-width: calc(1em + 20px);
}

The parser is initialized like this:

$settings = Sabberworm\CSS\Settings::create();
$settings->beStrict();
$parser = new Sabberworm\CSS\Parser($css, $settings);

I think Parser::parseValue() would need to be modified but I have no idea how to fix this.

fatal Error on php 5.3.3 Centos

PHP Fatal error: Can't inherit abstract function Sabberworm\CSS\Renderable::__toString() (previously declared abstract in Sabberworm\CSS\Value\Value) in /www/vendor/sabberworm/php-css-parser/lib/Sabberworm/CSS/Value/Value.php on line 7

my code used:
$oParser = new Sabberworm\CSS\Parser($css.$template['Template']['style']);
$oCss = $oParser->parse();
foreach($oCss->getAllDeclarationBlocks() as $oBlock) {
foreach($oBlock->getSelectors() as $oSelector) {
//Loop over all selector parts (the comma-separated strings in a selector) and prepend the id
$oSelector->setSelector('form table '.$oSelector->getSelector());
}
}
$css = $oCss->render();

My code is exactly the same as the example in the wiki.

My php info
PHP 5.3.3 (cli) (built: Aug 6 2014 05:54:27)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
with Suhosin v0.9.29, Copyright (c) 2007, by SektionEins GmbH

Return rules by id

Hi, I didn't see any way to return all the rules associated with a particular selector. Looks like I would just have to iterate over all delcaration blocks and then get the selector and return the values if a hit was found. Any plans on adding something similar or am I missing anything? Thanks and great library.

Support CSS specificity

The support for specificity would allow CSSParser to merge RuleSets according to CSS cascading rules (http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order).

But I think it would need a bit of redesign.
The statement
#myid .someclass, foo.bar { mydeclaration: myvalue}
should be parsed as two CSSSelector objects having the same rules but a different specificity.

I invite you to have a look to http://github.com/alexdunae/css_parser, and in particular to https://github.com/alexdunae/css_parser/blob/master/lib/css_parser.rb for the css specificity thing.

How to get position of selectors

Examples show how to get all selectors. I need position of selectors. I mean cursor position in file of that selector.

Is that possible or not supported yet?

CSSRule->__toString doesn't work with sized values

If I have a CSS rule as follows (this is in your example css):
div.rating-cancel,div.star-rating{width:17px;height:15px;}

The CSSRule->__toString either throws an error, or it looks like this:
width: Object;

The problem is on line 707 when it tries to implode a comma into the values. When a value is a CSSSize, it produces the above result.

For you to test this, in your CSSParserTests.php file,
change line 19 to:
$oDoc = $oParser->parse();
and add this line after the catch clause
echo $oDoc->__toString();

Incorrect parsing of line-height in font shorthand

div.main{
  font: 14px/1.2 Arial, sans-serif;
}
PHP Fatal error:  Uncaught exception 'Exception' with message 'Identifier expected, got /1.2 ' in /home/ju1ius/code/php/third-party/PHP-CSS-Parser/CSSParser.php:110
Stack trace:
#0 CSSParser.php(259): CSSParser->parseIdentifier()
#1 CSSParser.php(228): CSSParser->parseValue()
#2 CSSParser.php(216): CSSParser->parseRule()
#3 CSSParser.php(210): CSSParser->parseRuleSet(Object(CSSDeclarationBlock))
#4 CSSParser.php(50): CSSParser->parseSelector()
#5 CSSParser.php(35): CSSParser->parseList(Object(CSSDocument), true)
#6 CSSParser.php(29): CSSParser->parseDocument(Object(CSSDocument))
#7 test.php(17): CSSParser->parse()
#8 {main}
  thrown in CSSParser.php on line 110

Multiple backgrounds

Hi,
I have the following section/rule:

.gradient {
    background: linear-gradient(45deg,transparent 0%,transparent 74%,#cce7f5 74%,#cce7f5 80%,transparent 80%,transparent 82%,#cce7f5 82%,#cce7f5 85%,transparent 85%,transparent 89%,#cce7f5 89%,#cce7f5 91%,transparent 91%,transparent 100%) no-repeat border-box,linear-gradient(to bottom,#53bce2 0%,#53bce2 80%,#54abbe 100%) no-repeat border-box;
}

And I need to get the value of the rule "background", but I need get separately, for exemple:

array(
    'background-image' => array(
        'linear-gradient(45deg,transparent 0%,transparent 74%,#cce7f5 74%,#cce7f5 80%,transparent 80%,transparent 82%,#cce7f5 82%,#cce7f5 85%,transparent 85%,transparent 89%,#cce7f5 89%,#cce7f5 91%,transparent 91%,transparent 100%)',
        'linear-gradient(to bottom,#53bce2 0%,#53bce2 80%,#54abbe 100%')
    ),
    'background-repeat' => array('no-repeat','no-repeat'),
    'background-origin' => array('border-box','border-box'),
);

...or something like this, but I just need the get the values of "background" separately (background-image, background-repeat, background-origin).
How can I do this?

Support CSS3 media queries

I try to parse a last Boostrap Css but i got an error to Sabberworm/CSS/Parser.php at line 171.
Maybe it's can parse this line : @media (min-width: 768px) {.lead {font-size: 21px;}}
Any idea?

Thanks

Correcting incorrect values

The parser seems to fix incorrect values - e.g. "margin: 14px0;" becomes "margin: 14px;" - it would be great if there was a way to turn this off - ignore the rule when the value is incorrect instead of parsing it with a correct value - the beStrict() method seems to be doing something else.

Fatal error: Uncaught exception 'Exception' with message 'Identifier expected, got ; ' in /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php:115

I get an error when I try to parse this file:

Fatal error: Uncaught exception 'Exception' with message 'Identifier expected, got ; ' in /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php:115 Stack trace: #0 /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php(222): CSSParser->parseIdentifier() #1 /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php(215): CSSParser->parseRule() #2 /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php(209): CSSParser->parseRuleSet(Object(CSSDeclarationBlock)) #3 /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php(67): CSSParser->parseSelector() #4 /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php(52): CSSParser->parseList(Object(CSSDocument), true) #5 /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php(42): CSSParser->parseDocument(Object(CSSDocument)) #6 /var/www/svn-rudloff/test.php(4): CSSParser->parse() #7 {main} thrown in /var/www/svn-rudloff/proxy/CSS-parser/CSSParser.php on line 115

getAllValues() doesn't recognize color values

the css:
.box_pic{background: url(images/no-image) #D1B6DB center center no-repeat;}

the call:
$CssParser = new CSSParser(file_get_contents($filepath));
$CssDocument = $CssParser->parse();

echo $CssDocument;
// that one outputs:
// .box_pic {background: url("images/no-image") rgb(209,182,219) center center no-repeat;}

//but this one:
foreach ($CssDocument->getAllValues() as $value)
echo $value;
//outputs:
// url("images/no-image")centercenterno-repeaturl

Any reason to make parser methods private?

A bit of background (if you are wondering why I've suddenly shown up and made a bunch of pull requests :) ):

I'm a dev on the Horde project. We finally got fed up with our csstidy parser module and decided to look for code that could actually parse CSS3, and do it in a coherent fashion (i.e. stateful parsing, not preg crap). Found this project and am happy. We are going to package the parser in a PEAR package to be able to be used in our framework (i.e. autoloading is setup, etc.).

As such, one of the things I would like to see is stream support instead of reading all data into memory. We use streams all over the place to reduce memory usage - since we have installations that have millions of users, we need to cut down memory usage as much as possible.

One way to do that is to open CSS files with fopen() and then pass the stream resource to the CSS parser instead of reading the whole file into memory (which can be 200KB+ of data). I would then write a local Horde wrapper which extends Parser to overwrite the strlen, substr (and new strpos method I just added a pull request for) to access the stream instead of making them string operations.

However, with those methods being marked as private, there is no way to do that. As a project, we mark everything as protected for precisely this reason - so end users can extend our classes as they see fit.

Any chance of making this change upstream? Appreciate the requests you've already processed from me - it would be great to keep our copy as pristine as possible in order to be able to update from the main source as needed, thus the reason for the request.

PSR-0 compatibility

Partially as a follow-up on #13, do you have any plans on making this library PSR-0 compatible? This would make it a lot easier to use the library in todays common PHP frameworks, and easily install it from a PEAR channel with the PEAR installer.

Really each single class should be moved to a separate file, and a properly structured hierarchy of either PHP 5.3 namespaces or PEAR-style named classes (with each _ in a class name being replaced by the directory separator when autoloading from the file system) established.

I personally would vote for dropping PHP 5.2 compatibility and use PHP 5.3 namespaces. Like this (supposing you might want to use Sabberworm as vendor name, but can be replaced with something else unique):

Sabberworm\CSS\List
Sabberworm\CSS\Document
Sabberworm\CSS\MediaQuery
...
Sabberworm\CSS\Parser

I am willing to help you making this move or do it entirely myself, as this library rocks and I'm eager to make it easier to use it.

Fail to parse M$ filters

filter:  progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed');

Installing without composer

How do I install the php-css-parser by uploading it? Is there a particular place I need to put the files? Would I need to modify the file contents? I do not have composer.

Parsing the IE invalid standard 'filter'

Most stylesheets I see in production have atleast one DXImageFilter in them, usually for lightboxes and the likes. While they are invalid format (non W3c) I dont think its great to have the parser fail when parsing them, they should be ignored? But hopefully left in the final output so yeah.

An example of what i am talking about is:
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="overlay.png",sizingMethod="scale")}

Any chance you could improve your parser to handle Microsofts proprietary extension?

option to minimally format the output

It would be nice if there was an option to minimally format the output (indenting and line breaks) to make the result readable, which can be useful while debugging etc.

New feature: improve diagnostics for parse errors

In case of parse errors the Parser::parse() will throw Exception or UnexpectedTokenException depending on error type.

However, this is not enough to create meaningful diagnostics about the problem. For example a CSS document such as this

@foo (something=another))
{
  bar { zoo: 1; }
}

will cause parse() to throw \Exception with a message Unopened { where the real problem is an extra closing parenthesis before first '{'.

In best case, there would be much more exception classes for each specific parse error, but I think even adding a simple public method to Parser.php would improve things a lot:

    /**
     * @return int byte offset for current parser position
     *  In case the parse() throws an exception, this method can be
     *  used to approximate the error location.
     */
    public function getPosition()
    {
        return $this->iCurrentPosition;
    }

Add compress() method to Document

For maximum compressibility (i.e. removing all unneeded whitespace and semicolons), might want to add this method to Document:

    public function compress()
    {
        return str_replace(
            array("\n", ' {', ': ', ';}', ', '),
            array('', '{', ':', '}', ','),
            strval($this)
        );
    }

We currently have this in the wrapper object around the Document object, and it seems to work fine on all browsers. At the least, it outputs code that looks very similar to what jquerymobile does to compress their CSS (i.e. taking their CSS -> importing into PHP-CSS-Parser -> strval($document) -> compressing output = output almost identical to the input).

Open source license not specified

This looks like amazing stuff, and I'd like to use it, but I need to know whether it is under an open source license, and which license. Currently your README and source code have no copyright statement or license stated. Although github doesn't host non-open-source projects for free, that still covers an awful lot of ground. For instance, if you plan to use the GPL then I need to know I can't use it in a project that won't also be under the GPL. While a BSD license would make it a safe choice for use in any project.

Please add a copyright statement and let folks know what license they are able to use the code under.

Thank you!

Exceptions or return codes

Is there any example how I can get exception messages?
How can I detect in my code that the parsing did not work because of wrong css?

I use the first Example - neded exactly this - but with wrong css it gives me an empty result but no error.

any hints?

Error parsing line with semicolon

Hi,

When parsing the CSS below, the code is throwing this error:
"Generic Identifier expected, got ;"
caused by a ; (which corresponds to an empty property and empty value ?).

However, when submitting this CSS to W3C Epub Validator - http://jigsaw.w3.org/css-validator/validator -, no errors/warnings were found.

Shouldn't the parser be tolerant to this issue?

p.bbb {
;
line-height : 1.33;
text-align : justify;
color : #000000;
text-indent : 23px;
margin : 0px;
}

Ignore properties starting with *

When parsing arbitrary css file, I often have to deal with ie specific css such as

.class {
    /* Some IE specific code */
    *property: value;
}

These properties should be ignored as comment as they are not standard css and aim to patch the same property designed for compliant browser.

The other option would be to allow * characters as valid starting character for a property name. It could be simpler, and output the same css code.

If you are interested in having such feature, I can implement the latter.

Duplicated semicolons generates exception in CSSParser::parseIdentifier

Trying to parse the DokuWiki CSS file at the link below generates an exception in CSSParser::parseIdentifier() on line 115 with the message "Identifier expected, got ;widt".

CSS file link: http://www.syntilect.com/cgf/lib/exe/css.php

Examining the CSS file shows a duplicated semicolon (after right:237px). I suspect this is affecting the parser - will have a look and see if I can find an easy fix.

div.dokuwiki div.ajax_qsearch{position:absolute;right:237px;;width:200px;opacity:0.9;display:none;font-size:80%;line-height:1.2em;border:1px solid #8cacbb;background-color:#f7f9fa;text-align:left;padding:4px;}

CHANGELOG is outdated

Hi,

For downstream distribution, but also for all users, it will be great to have some information about new versions.

The CHANGELOG.md is outdated (no update since 5.0.4)
Digging in the commit list is not really friendly... :(

Can you please consider providing those information in some way (changelog, github release info, or something else) ?

Any way to preserve whitespace?

I am looking to write a deployment script that changes some CSS rules, but would like to keep the whitespace intact - that is, the file should have the same indentations and whitespace as before I parse it with CSS-Parser.

Is this possible? I have checked the consumeWhiteSpace() method but removing it does not help.

breaks with css3 and some newer semantics

the twitter bootstrap.css and bootstrap-responsive.css for example are not parsable:

 exception: Identifier expected, got *disp

the reason is probably one of those declarations:

video {
  display: inline-block;
  *display: inline;
  *zoom: 1;
}

fixing this using (* added)

preg_match('/[\*a-zA-Z0-9]|-|_/u'

I get others like Identifier expected, got :DXIm

filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);

is there an option to ignore those and just add them afterwards again?

Fails parsing this valid CSS (!important)

Fails to parse this:

div.rating-cancel,div.star-rating{float:left;width:17px;height:15px;text-indent:-999em;cursor:pointer;display:block;background:transparent;overflow:hidden}
div.rating-cancel,div.rating-cancel a{background:url(images/delete.gif) no-repeat 0 -16px}
div.star-rating,div.star-rating a{background:url(images/star.gif) no-repeat 0 0px}
div.rating-cancel a,div.star-rating a{display:block;width:16px;height:100%;background-position:0 0px;border:0}
div.star-rating-on a{background-position:0 -16px!important}
div.star-rating-hover a{background-position:0 -32px}
div.star-rating-readonly a{cursor:default !important}
div.star-rating{background:transparent!important; overflow:hidden!important}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.