diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e044da..ab489fdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Revision History +## 8.0 + +### 8.0.0 (2016-06-30) + +* Store source CSS line numbers in tokens and parsing exceptions. +* *No deprecations* + +#### Backwards-incompatible changes + +* Unrecoverable parser errors throw an exception of type `Sabberworm\CSS\Parsing\SourceException` instead of `\Exception`. + ## 7.0 ### 7.0.0 (2015-08-24) diff --git a/README.md b/README.md index 25250321..d65840bf 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,28 @@ A Parser for CSS Files written in PHP. Allows extraction of CSS files into a dat Add php-css-parser to your composer.json - { - "require": { - "sabberworm/php-css-parser": "*" - } - } +```json +{ + "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($sText); +```php +new Sabberworm\CSS\Parser($sText); +``` To read a file, for example, you’d do the following: - $oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css')); - $oCssDocument = $oCssParser->parse(); +```php +$oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css')); +$oCssDocument = $oCssParser->parse(); +``` The resulting CSS document structure can be manipulated prior to being output. @@ -36,21 +42,27 @@ The resulting CSS document structure can be manipulated prior to being output. The charset option is used only if no @charset declaration is found in the CSS file. 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. - $oSettings = Sabberworm\CSS\Settings::create()->withDefaultCharset('windows-1252'); - new Sabberworm\CSS\Parser($sText, $oSettings); +```php +$oSettings = Sabberworm\CSS\Settings::create()->withDefaultCharset('windows-1252'); +new Sabberworm\CSS\Parser($sText, $oSettings); +``` #### Strict parsing To have the parser choke on invalid rules, supply a thusly configured Sabberworm\CSS\Settings object: - $oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css'), Sabberworm\CSS\Settings::create()->beStrict()); +```php +$oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css'), Sabberworm\CSS\Settings::create()->beStrict()); +``` #### 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 to use this with input you have no control over as it’s not thoroughly covered by test cases. - $oSettings = Sabberworm\CSS\Settings::create()->withMultibyteSupport(false); - new Sabberworm\CSS\Parser($sText, $oSettings); +```php +$oSettings = Sabberworm\CSS\Settings::create()->withMultibyteSupport(false); +new Sabberworm\CSS\Parser($sText, $oSettings); +``` ### Manipulation @@ -114,52 +126,64 @@ There are a few convenience methods on Document to ease finding, manipulating an ### Use `Parser` to prepend an id to all selectors - $sMyId = "#my_id"; - $oParser = new Sabberworm\CSS\Parser($sText); - $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($sMyId.' '.$oSelector->getSelector()); - } +```php +$sMyId = "#my_id"; +$oParser = new Sabberworm\CSS\Parser($sText); +$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($sMyId.' '.$oSelector->getSelector()); } - +} +``` + ### Shrink all absolute sizes to half - $oParser = new Sabberworm\CSS\Parser($sText); - $oCss = $oParser->parse(); - foreach($oCss->getAllValues() as $mValue) { - if($mValue instanceof CSSSize && !$mValue->isRelative()) { - $mValue->setSize($mValue->getSize()/2); - } +```php +$oParser = new Sabberworm\CSS\Parser($sText); +$oCss = $oParser->parse(); +foreach($oCss->getAllValues() as $mValue) { + if($mValue instanceof CSSSize && !$mValue->isRelative()) { + $mValue->setSize($mValue->getSize()/2); } +} +``` ### Remove unwanted rules - $oParser = new Sabberworm\CSS\Parser($sText); - $oCss = $oParser->parse(); - foreach($oCss->getAllRuleSets() as $oRuleSet) { - $oRuleSet->removeRule('font-'); //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('cursor'); - } +```php +$oParser = new Sabberworm\CSS\Parser($sText); +$oCss = $oParser->parse(); +foreach($oCss->getAllRuleSets() as $oRuleSet) { + $oRuleSet->removeRule('font-'); //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('cursor'); +} +``` ### Output To output the entire CSS document into a variable, just use `->render()`: - $oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css')); - $oCssDocument = $oCssParser->parse(); - print $oCssDocument->render(); +```php +$oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css')); +$oCssDocument = $oCssParser->parse(); +print $oCssDocument->render(); +``` If you want to format the output, pass an instance of type `Sabberworm\CSS\OutputFormat`: - $oFormat = Sabberworm\CSS\OutputFormat::create()->indentWithSpaces(4)->setSpaceBetweenRules("\n"); - print $oCssDocument->render($oFormat); +```php +$oFormat = Sabberworm\CSS\OutputFormat::create()->indentWithSpaces(4)->setSpaceBetweenRules("\n"); +print $oCssDocument->render($oFormat); +``` Or use one of the predefined formats: - print $oCssDocument->render(Sabberworm\CSS\OutputFormat::createPretty()); - print $oCssDocument->render(Sabberworm\CSS\OutputFormat::createCompact()); +```php +print $oCssDocument->render(Sabberworm\CSS\OutputFormat::createPretty()); +print $oCssDocument->render(Sabberworm\CSS\OutputFormat::createCompact()); +``` To see what you can do with output formatting, look at the tests in `tests/Sabberworm/CSS/OutputFormatTest.php`. @@ -169,346 +193,428 @@ To see what you can do with output formatting, look at the tests in `tests/Sabbe #### Input - @charset "utf-8"; +```css +@charset "utf-8"; - @font-face { - font-family: "CrassRoots"; - src: url("../media/cr.ttf") - } +@font-face { + font-family: "CrassRoots"; + src: url("../media/cr.ttf") +} - html, body { - font-size: 1.6em - } +html, body { + font-size: 1.6em +} - @keyframes mymove { - from { top: 0px; } - to { top: 200px; } - } +@keyframes mymove { + from { top: 0px; } + to { top: 200px; } +} + +``` #### Structure (`var_dump()`) - object(Sabberworm\CSS\CSSList\Document)#4 (1) { - ["aContents":protected]=> - array(4) { - [0]=> - object(Sabberworm\CSS\Property\Charset)#6 (1) { - ["sCharset":"Sabberworm\CSS\Property\Charset":private]=> - object(Sabberworm\CSS\Value\CSSString)#5 (1) { - ["sString":"Sabberworm\CSS\Value\CSSString":private]=> - string(5) "utf-8" - } - } - [1]=> - object(Sabberworm\CSS\RuleSet\AtRuleSet)#7 (2) { - ["sType":"Sabberworm\CSS\RuleSet\AtRuleSet":private]=> - string(9) "font-face" - ["aRules":"Sabberworm\CSS\RuleSet\RuleSet":private]=> - array(2) { - ["font-family"]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Rule\Rule)#8 (3) { - ["sRule":"Sabberworm\CSS\Rule\Rule":private]=> - string(11) "font-family" - ["mValue":"Sabberworm\CSS\Rule\Rule":private]=> - object(Sabberworm\CSS\Value\CSSString)#9 (1) { - ["sString":"Sabberworm\CSS\Value\CSSString":private]=> - string(10) "CrassRoots" - } - ["bIsImportant":"Sabberworm\CSS\Rule\Rule":private]=> - bool(false) - } - } - ["src"]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Rule\Rule)#10 (3) { - ["sRule":"Sabberworm\CSS\Rule\Rule":private]=> - string(3) "src" - ["mValue":"Sabberworm\CSS\Rule\Rule":private]=> - object(Sabberworm\CSS\Value\URL)#11 (1) { - ["oURL":"Sabberworm\CSS\Value\URL":private]=> - object(Sabberworm\CSS\Value\CSSString)#12 (1) { - ["sString":"Sabberworm\CSS\Value\CSSString":private]=> - string(15) "../media/cr.ttf" - } - } - ["bIsImportant":"Sabberworm\CSS\Rule\Rule":private]=> - bool(false) - } - } - } - } - [2]=> - object(Sabberworm\CSS\RuleSet\DeclarationBlock)#13 (2) { - ["aSelectors":"Sabberworm\CSS\RuleSet\DeclarationBlock":private]=> - array(2) { - [0]=> - object(Sabberworm\CSS\Property\Selector)#14 (2) { - ["sSelector":"Sabberworm\CSS\Property\Selector":private]=> - string(4) "html" - ["iSpecificity":"Sabberworm\CSS\Property\Selector":private]=> - NULL - } - [1]=> - object(Sabberworm\CSS\Property\Selector)#15 (2) { - ["sSelector":"Sabberworm\CSS\Property\Selector":private]=> - string(4) "body" - ["iSpecificity":"Sabberworm\CSS\Property\Selector":private]=> - NULL - } - } - ["aRules":"Sabberworm\CSS\RuleSet\RuleSet":private]=> - array(1) { - ["font-size"]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Rule\Rule)#16 (3) { - ["sRule":"Sabberworm\CSS\Rule\Rule":private]=> - string(9) "font-size" - ["mValue":"Sabberworm\CSS\Rule\Rule":private]=> - object(Sabberworm\CSS\Value\Size)#17 (3) { - ["fSize":"Sabberworm\CSS\Value\Size":private]=> - float(1.6) - ["sUnit":"Sabberworm\CSS\Value\Size":private]=> - string(2) "em" - ["bIsColorComponent":"Sabberworm\CSS\Value\Size":private]=> - bool(false) - } - ["bIsImportant":"Sabberworm\CSS\Rule\Rule":private]=> - bool(false) - } - } - } - } - [3]=> - object(Sabberworm\CSS\CSSList\KeyFrame)#18 (3) { - ["vendorKeyFrame":"Sabberworm\CSS\CSSList\KeyFrame":private]=> - string(9) "keyframes" - ["animationName":"Sabberworm\CSS\CSSList\KeyFrame":private]=> - string(6) "mymove" - ["aContents":protected]=> - array(2) { - [0]=> - object(Sabberworm\CSS\RuleSet\DeclarationBlock)#19 (2) { - ["aSelectors":"Sabberworm\CSS\RuleSet\DeclarationBlock":private]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Property\Selector)#20 (2) { - ["sSelector":"Sabberworm\CSS\Property\Selector":private]=> - string(4) "from" - ["iSpecificity":"Sabberworm\CSS\Property\Selector":private]=> - NULL - } - } - ["aRules":"Sabberworm\CSS\RuleSet\RuleSet":private]=> - array(1) { - ["top"]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Rule\Rule)#21 (3) { - ["sRule":"Sabberworm\CSS\Rule\Rule":private]=> - string(3) "top" - ["mValue":"Sabberworm\CSS\Rule\Rule":private]=> - object(Sabberworm\CSS\Value\Size)#22 (3) { - ["fSize":"Sabberworm\CSS\Value\Size":private]=> - float(0) - ["sUnit":"Sabberworm\CSS\Value\Size":private]=> - string(2) "px" - ["bIsColorComponent":"Sabberworm\CSS\Value\Size":private]=> - bool(false) - } - ["bIsImportant":"Sabberworm\CSS\Rule\Rule":private]=> - bool(false) - } - } - } - } - [1]=> - object(Sabberworm\CSS\RuleSet\DeclarationBlock)#23 (2) { - ["aSelectors":"Sabberworm\CSS\RuleSet\DeclarationBlock":private]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Property\Selector)#24 (2) { - ["sSelector":"Sabberworm\CSS\Property\Selector":private]=> - string(2) "to" - ["iSpecificity":"Sabberworm\CSS\Property\Selector":private]=> - NULL - } - } - ["aRules":"Sabberworm\CSS\RuleSet\RuleSet":private]=> - array(1) { - ["top"]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Rule\Rule)#25 (3) { - ["sRule":"Sabberworm\CSS\Rule\Rule":private]=> - string(3) "top" - ["mValue":"Sabberworm\CSS\Rule\Rule":private]=> - object(Sabberworm\CSS\Value\Size)#26 (3) { - ["fSize":"Sabberworm\CSS\Value\Size":private]=> - float(200) - ["sUnit":"Sabberworm\CSS\Value\Size":private]=> - string(2) "px" - ["bIsColorComponent":"Sabberworm\CSS\Value\Size":private]=> - bool(false) - } - ["bIsImportant":"Sabberworm\CSS\Rule\Rule":private]=> - bool(false) - } - } - } - } - } - } - } - } +```php +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;} - } +```css +@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; - } - +```css +#header { + margin: 10px 2em 1cm 2%; + font-family: Verdana, Helvetica, "Gill Sans", sans-serif; + color: red !important; +} + +``` + #### Structure (`var_dump()`) - object(Sabberworm\CSS\CSSList\Document)#4 (1) { - ["aContents":protected]=> - array(1) { - [0]=> - object(Sabberworm\CSS\RuleSet\DeclarationBlock)#5 (2) { - ["aSelectors":"Sabberworm\CSS\RuleSet\DeclarationBlock":private]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Property\Selector)#6 (2) { - ["sSelector":"Sabberworm\CSS\Property\Selector":private]=> - string(7) "#header" - ["iSpecificity":"Sabberworm\CSS\Property\Selector":private]=> - NULL - } - } - ["aRules":"Sabberworm\CSS\RuleSet\RuleSet":private]=> - array(3) { - ["margin"]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Rule\Rule)#7 (3) { - ["sRule":"Sabberworm\CSS\Rule\Rule":private]=> - string(6) "margin" - ["mValue":"Sabberworm\CSS\Rule\Rule":private]=> - object(Sabberworm\CSS\Value\RuleValueList)#12 (2) { - ["aComponents":protected]=> - array(4) { - [0]=> - object(Sabberworm\CSS\Value\Size)#8 (3) { - ["fSize":"Sabberworm\CSS\Value\Size":private]=> - float(10) - ["sUnit":"Sabberworm\CSS\Value\Size":private]=> - string(2) "px" - ["bIsColorComponent":"Sabberworm\CSS\Value\Size":private]=> - bool(false) - } - [1]=> - object(Sabberworm\CSS\Value\Size)#9 (3) { - ["fSize":"Sabberworm\CSS\Value\Size":private]=> - float(2) - ["sUnit":"Sabberworm\CSS\Value\Size":private]=> - string(2) "em" - ["bIsColorComponent":"Sabberworm\CSS\Value\Size":private]=> - bool(false) - } - [2]=> - object(Sabberworm\CSS\Value\Size)#10 (3) { - ["fSize":"Sabberworm\CSS\Value\Size":private]=> - float(1) - ["sUnit":"Sabberworm\CSS\Value\Size":private]=> - string(2) "cm" - ["bIsColorComponent":"Sabberworm\CSS\Value\Size":private]=> - bool(false) - } - [3]=> - object(Sabberworm\CSS\Value\Size)#11 (3) { - ["fSize":"Sabberworm\CSS\Value\Size":private]=> - float(2) - ["sUnit":"Sabberworm\CSS\Value\Size":private]=> - string(1) "%" - ["bIsColorComponent":"Sabberworm\CSS\Value\Size":private]=> - bool(false) - } - } - ["sSeparator":protected]=> - string(1) " " - } - ["bIsImportant":"Sabberworm\CSS\Rule\Rule":private]=> - bool(false) - } - } - ["font-family"]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Rule\Rule)#13 (3) { - ["sRule":"Sabberworm\CSS\Rule\Rule":private]=> - string(11) "font-family" - ["mValue":"Sabberworm\CSS\Rule\Rule":private]=> - object(Sabberworm\CSS\Value\RuleValueList)#15 (2) { - ["aComponents":protected]=> - array(4) { - [0]=> - string(7) "Verdana" - [1]=> - string(9) "Helvetica" - [2]=> - object(Sabberworm\CSS\Value\CSSString)#14 (1) { - ["sString":"Sabberworm\CSS\Value\CSSString":private]=> - string(9) "Gill Sans" - } - [3]=> - string(10) "sans-serif" - } - ["sSeparator":protected]=> - string(1) "," - } - ["bIsImportant":"Sabberworm\CSS\Rule\Rule":private]=> - bool(false) - } - } - ["color"]=> - array(1) { - [0]=> - object(Sabberworm\CSS\Rule\Rule)#16 (3) { - ["sRule":"Sabberworm\CSS\Rule\Rule":private]=> - string(5) "color" - ["mValue":"Sabberworm\CSS\Rule\Rule":private]=> - string(3) "red" - ["bIsImportant":"Sabberworm\CSS\Rule\Rule":private]=> - bool(true) - } - } - } - } - } - } +```php +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;} +```css +#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;} +``` ## Contributors/Thanks to * [ju1ius](https://github.com/ju1ius) for the specificity parsing code and the ability to expand/compact shorthand properties. * [GaryJones](https://github.com/GaryJones) for lots of input and [http://css-specificity.info/](http://css-specificity.info/). * [ossinkine](https://github.com/ossinkine) for a 150 time performance boost. +* [Lullabot](https://github.com/Lullabot) for a patch that allows to know the line number for each parsed token. * [docteurklein](https://github.com/docteurklein) for output formatting and `CSSList->remove()` inspiration. * [nicolopignatelli](https://github.com/nicolopignatelli) for PSR-0 compatibility. * [diegoembarcadero](https://github.com/diegoembarcadero) for keyframe at-rule parsing. diff --git a/lib/Sabberworm/CSS/Parsing/OutputException.php b/lib/Sabberworm/CSS/Parsing/OutputException.php index ba847fe7..1c811770 100644 --- a/lib/Sabberworm/CSS/Parsing/OutputException.php +++ b/lib/Sabberworm/CSS/Parsing/OutputException.php @@ -6,7 +6,7 @@ * Thrown if the CSS parsers attempts to print something invalid */ class OutputException extends SourceException { - public function __construct($sMessage, $iLineNo = 0) { - parent::__construct($sMessage, $iLineNo); - } + public function __construct($sMessage, $iLineNo = 0) { + parent::__construct($sMessage, $iLineNo); + } } \ No newline at end of file diff --git a/lib/Sabberworm/CSS/Parsing/SourceException.php b/lib/Sabberworm/CSS/Parsing/SourceException.php index 7bb54aa5..9bb99138 100644 --- a/lib/Sabberworm/CSS/Parsing/SourceException.php +++ b/lib/Sabberworm/CSS/Parsing/SourceException.php @@ -3,16 +3,16 @@ namespace Sabberworm\CSS\Parsing; class SourceException extends \Exception { - private $iLineNo; - public function __construct($sMessage, $iLineNo = 0) { - $this->iLineNo = $iLineNo; - if (!empty($iLineNo)) { - $sMessage .= " [line no: $iLineNo]"; - } - parent::__construct($sMessage); - } + private $iLineNo; + public function __construct($sMessage, $iLineNo = 0) { + $this->iLineNo = $iLineNo; + if (!empty($iLineNo)) { + $sMessage .= " [line no: $iLineNo]"; + } + parent::__construct($sMessage); + } - public function getLineNo() { - return $this->iLineNo; - } + public function getLineNo() { + return $this->iLineNo; + } } \ No newline at end of file diff --git a/tests/quickdump.php b/tests/quickdump.php index d3e7283a..387cab72 100644 --- a/tests/quickdump.php +++ b/tests/quickdump.php @@ -2,14 +2,18 @@ require_once(dirname(__FILE__).'/bootstrap.php'); -$oParser = new Sabberworm\CSS\Parser(file_get_contents('php://stdin')); +$sSource = file_get_contents('php://stdin'); +$oParser = new Sabberworm\CSS\Parser($sSource); $oDoc = $oParser->parse(); +echo "\n".'#### Input'."\n\n```css\n"; +print $sSource; -echo '#### Structure (`var_dump()`)'."\n"; +echo "\n```\n\n".'#### Structure (`var_dump()`)'."\n\n```php\n"; var_dump($oDoc); -echo '#### Output (`render()`)'."\n"; +echo "\n```\n\n".'#### Output (`render()`)'."\n\n```css\n"; print $oDoc->render(); -echo "\n"; + +echo "\n```\n";