From 441d519ce1687d1e316edc8ce2d1982dd039695b Mon Sep 17 00:00:00 2001 From: Pablo Borowicz Date: Tue, 26 Dec 2017 16:32:22 +0100 Subject: [PATCH 1/4] Extracted OutputFormatter class + code formatting + comments --- lib/Sabberworm/CSS/OutputFormat.php | 444 ++++++++++++------------- lib/Sabberworm/CSS/OutputFormatter.php | 236 +++++++++++++ 2 files changed, 444 insertions(+), 236 deletions(-) create mode 100644 lib/Sabberworm/CSS/OutputFormatter.php diff --git a/lib/Sabberworm/CSS/OutputFormat.php b/lib/Sabberworm/CSS/OutputFormat.php index 1b179840..cc81b786 100644 --- a/lib/Sabberworm/CSS/OutputFormat.php +++ b/lib/Sabberworm/CSS/OutputFormat.php @@ -2,29 +2,39 @@ namespace Sabberworm\CSS; -use Sabberworm\CSS\Parsing\OutputException; - +/** + * Output format configuration + */ class OutputFormat { + /** - * Value format - */ - // " means double-quote, ' means single-quote + * String quoting type. + * " means double-quote, ' means single-quote + * @var string + */ public $sStringQuotingType = '"'; - // Output RGB colors in hash notation if possible - public $bRGBHashNotation = true; - + /** - * Declaration format - */ - // Semicolon after the last rule of a declaration block can be omitted. To do that, set this false. + * Output RGB colors in hash notation if possible + * @var bool + */ + public $bRGBHashNotation = true; + + // Declaration format + + /** + * Semicolon after the last rule of a declaration block can be omitted. To do that, set this false. + * @var bool + */ public $bSemicolonAfterLastRule = true; - + /** - * Spacing - * Note that these strings are not sanity-checked: the value should only consist of whitespace - * Any newline character will be indented according to the current level. - * The triples (After, Before, Between) can be set using a wildcard (e.g. `$oFormat->set('Space*Rules', "\n");`) - */ + * Spacing + * Note that these strings are not sanity-checked: the value should only consist of whitespace + * Any newline character will be indented according to the current level. + * The triples (After, Before, Between) can be set using a wildcard (e.g. `$oFormat->set('Space*Rules', "\n");`) + */ + public $sSpaceAfterRuleName = ' '; public $sSpaceBeforeRules = ''; @@ -35,34 +45,52 @@ class OutputFormat { public $sSpaceAfterBlocks = ''; public $sSpaceBetweenBlocks = "\n"; - // This is what’s printed before and after the comma if a declaration block contains multiple selectors. + /** + * Printed before and after the comma if a declaration block contains multiple selectors. + * @var string + */ public $sSpaceBeforeSelectorSeparator = ''; + /** + * Printed after the comma if a declaration block contains multiple selectors. + * @var string + */ public $sSpaceAfterSelectorSeparator = ' '; - // This is what’s printed after the comma of value lists - public $sSpaceBeforeListArgumentSeparator = ''; + + /** + * Printed before the comma of value lists. + * @var string + */ + public $sSpaceBeforeListArgumentSeparator = ''; + + /** + * Printed after the comma of value lists. + * @var string + */ public $sSpaceAfterListArgumentSeparator = ''; - + public $sSpaceBeforeOpeningBrace = ' '; - + /** - * Indentation - */ - // Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings. + * Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings. + * @var string + */ public $sIndentation = "\t"; - + /** - * Output exceptions. - */ + * Output exceptions. + * @var bool + */ public $bIgnoreExceptions = false; - - + private $oFormatter = null; private $oNextLevelFormat = null; private $iIndentationLevel = 0; - - public function __construct() { - } - + + /** + * @param $sName + * + * @return mixed|null + */ public function get($sName) { $aVarPrefixes = array('a', 's', 'm', 'b', 'f', 'o', 'c', 'i'); foreach($aVarPrefixes as $sPrefix) { @@ -73,217 +101,161 @@ public function get($sName) { } return null; } - - public function set($aNames, $mValue) { - $aVarPrefixes = array('a', 's', 'm', 'b', 'f', 'o', 'c', 'i'); - if(is_string($aNames) && strpos($aNames, '*') !== false) { - $aNames = array(str_replace('*', 'Before', $aNames), str_replace('*', 'Between', $aNames), str_replace('*', 'After', $aNames)); - } else if(!is_array($aNames)) { - $aNames = array($aNames); - } - foreach($aVarPrefixes as $sPrefix) { - $bDidReplace = false; - foreach($aNames as $sName) { - $sFieldName = $sPrefix.ucfirst($sName); - if(isset($this->$sFieldName)) { - $this->$sFieldName = $mValue; - $bDidReplace = true; - } - } - if($bDidReplace) { - return $this; - } - } - // Break the chain so the user knows this option is invalid - return false; - } - - public function __call($sMethodName, $aArguments) { - if(strpos($sMethodName, 'set') === 0) { - return $this->set(substr($sMethodName, 3), $aArguments[0]); - } else if(strpos($sMethodName, 'get') === 0) { - return $this->get(substr($sMethodName, 3)); - } else if(method_exists('\\Sabberworm\\CSS\\OutputFormatter', $sMethodName)) { - return call_user_func_array(array($this->getFormatter(), $sMethodName), $aArguments); - } else { - throw new \Exception('Unknown OutputFormat method called: '.$sMethodName); - } - } - - public function indentWithTabs($iNumber = 1) { - return $this->setIndentation(str_repeat("\t", $iNumber)); - } - - public function indentWithSpaces($iNumber = 2) { - return $this->setIndentation(str_repeat(" ", $iNumber)); - } - - public function nextLevel() { - if($this->oNextLevelFormat === null) { - $this->oNextLevelFormat = clone $this; - $this->oNextLevelFormat->iIndentationLevel++; - $this->oNextLevelFormat->oFormatter = null; - } - return $this->oNextLevelFormat; - } - - public function beLenient() { - $this->bIgnoreExceptions = true; - } - - public function getFormatter() { - if($this->oFormatter === null) { - $this->oFormatter = new OutputFormatter($this); - } - return $this->oFormatter; - } - - public function level() { - return $this->iIndentationLevel; - } - - public static function create() { - return new OutputFormat(); - } - - public static function createCompact() { - return self::create()->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('')->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator(''); - } - - public static function createPretty() { - return self::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' ')); - } -} -class OutputFormatter { - private $oFormat; - - public function __construct(OutputFormat $oFormat) { - $this->oFormat = $oFormat; - } - - public function space($sName, $sType = null) { - $sSpaceString = $this->oFormat->get("Space$sName"); - // If $sSpaceString is an array, we have multple values configured depending on the type of object the space applies to - if(is_array($sSpaceString)) { - if($sType !== null && isset($sSpaceString[$sType])) { - $sSpaceString = $sSpaceString[$sType]; - } else { - $sSpaceString = reset($sSpaceString); - } - } - return $this->prepareSpace($sSpaceString); - } - - public function spaceAfterRuleName() { - return $this->space('AfterRuleName'); - } - - public function spaceBeforeRules() { - return $this->space('BeforeRules'); - } - - public function spaceAfterRules() { - return $this->space('AfterRules'); - } - - public function spaceBetweenRules() { - return $this->space('BetweenRules'); - } - - public function spaceBeforeBlocks() { - return $this->space('BeforeBlocks'); - } - - public function spaceAfterBlocks() { - return $this->space('AfterBlocks'); - } - - public function spaceBetweenBlocks() { - return $this->space('BetweenBlocks'); - } - - public function spaceBeforeSelectorSeparator() { - return $this->space('BeforeSelectorSeparator'); + /** + * @param $aNames + * @param $mValue + * + * @return $this|false + */ + public function set($aNames, $mValue) + { + $aVarPrefixes = array('a', 's', 'm', 'b', 'f', 'o', 'c', 'i'); + if (is_string($aNames) && strpos($aNames, '*') !== false) { + $aNames = array( + str_replace('*', 'Before', $aNames), + str_replace('*', 'Between', $aNames), + str_replace('*', 'After', $aNames), + ); + } elseif (!is_array($aNames)) { + $aNames = array($aNames); + } + foreach ($aVarPrefixes as $sPrefix) { + $bDidReplace = false; + foreach ($aNames as $sName) { + $sFieldName = $sPrefix.ucfirst($sName); + if (isset($this->$sFieldName)) { + $this->$sFieldName = $mValue; + $bDidReplace = true; + } + } + if ($bDidReplace) { + return $this; + } + } + // Break the chain so the user knows this option is invalid + return false; } - public function spaceAfterSelectorSeparator() { - return $this->space('AfterSelectorSeparator'); + /** + * @param string $sMethodName + * @param array $aArguments + * + * @return false|mixed|null|OutputFormat + * @throws \Exception + */ + public function __call($sMethodName, $aArguments) + { + if (strpos($sMethodName, 'set') === 0) { + return $this->set(substr($sMethodName, 3), $aArguments[0]); + } elseif (strpos($sMethodName, 'get') === 0) { + return $this->get(substr($sMethodName, 3)); + } elseif (method_exists('\\Sabberworm\\CSS\\OutputFormatter', $sMethodName)) { + return call_user_func_array(array($this->getFormatter(), $sMethodName), $aArguments); + } else { + throw new \Exception('Unknown OutputFormat method called: '.$sMethodName); + } } - public function spaceBeforeListArgumentSeparator($sSeparator) { - return $this->space('BeforeListArgumentSeparator', $sSeparator); + /** + * Sets indentation to a number of tabs + * + * @param int $iNumber [default=1] Number of tabs to indent with + * + * @return $this|false + */ + public function indentWithTabs($iNumber = 1) + { + return $this->setIndentation(str_repeat("\t", $iNumber)); } - public function spaceAfterListArgumentSeparator($sSeparator) { - return $this->space('AfterListArgumentSeparator', $sSeparator); - } + /** + * Sets indentation to a number of spaces + * @param int $iNumber [default=2] Number of spaces to indent with + * + * @return $this|false + */ + public function indentWithSpaces($iNumber = 2) + { + return $this->setIndentation(str_repeat(" ", $iNumber)); + } - public function spaceBeforeOpeningBrace() { - return $this->space('BeforeOpeningBrace'); - } + /** + * @return null|OutputFormat + */ + public function nextLevel() + { + if ($this->oNextLevelFormat === null) { + $this->oNextLevelFormat = clone $this; + $this->oNextLevelFormat->iIndentationLevel++; + $this->oNextLevelFormat->oFormatter = null; + } + return $this->oNextLevelFormat; + } - /** - * Runs the given code, either swallowing or passing exceptions, depending on the bIgnoreExceptions setting. - */ - public function safely($cCode) { - if($this->oFormat->get('IgnoreExceptions')) { - // If output exceptions are ignored, run the code with exception guards - try { - return $cCode(); - } catch (OutputException $e) { - return null; - } //Do nothing - } else { - // Run the code as-is - return $cCode(); - } - } + /** + * Activates exception ignoring + */ + public function beLenient() + { + $this->bIgnoreExceptions = true; + } - /** - * Clone of the implode function but calls ->render with the current output format instead of __toString() - */ - public function implode($sSeparator, $aValues, $bIncreaseLevel = false) { - $sResult = ''; - $oFormat = $this->oFormat; - if($bIncreaseLevel) { - $oFormat = $oFormat->nextLevel(); - } - $bIsFirst = true; - foreach($aValues as $mValue) { - if($bIsFirst) { - $bIsFirst = false; - } else { - $sResult .= $sSeparator; - } - if($mValue instanceof \Sabberworm\CSS\Renderable) { - $sResult .= $mValue->render($oFormat); - } else { - $sResult .= $mValue; - } - } - return $sResult; - } - - public function removeLastSemicolon($sString) { - if($this->oFormat->get('SemicolonAfterLastRule')) { - return $sString; - } - $sString = explode(';', $sString); - if(count($sString) < 2) { - return $sString[0]; - } - $sLast = array_pop($sString); - $sNextToLast = array_pop($sString); - array_push($sString, $sNextToLast.$sLast); - return implode(';', $sString); - } + /** + * @return null|OutputFormatter + */ + public function getFormatter() + { + if ($this->oFormatter === null) { + $this->oFormatter = new OutputFormatter($this); + } + return $this->oFormatter; + } - private function prepareSpace($sSpaceString) { - return str_replace("\n", "\n".$this->indent(), $sSpaceString); - } + /** + * @return int + */ + public function level() + { + return $this->iIndentationLevel; + } - private function indent() { - return str_repeat($this->oFormat->sIndentation, $this->oFormat->level()); - } -} \ No newline at end of file + /** + * @return OutputFormat + */ + public static function create() + { + return new OutputFormat(); + } + + /** + * @return OutputFormat|false + */ + public static function createCompact() + { + return self::create() + ->set('Space*Rules', "") + ->set('Space*Blocks', "") + ->setSpaceAfterRuleName('') + ->setSpaceBeforeOpeningBrace('') + ->setSpaceAfterSelectorSeparator(''); + } + + /** + * @return OutputFormat|false + */ + public static function createPretty() + { + return self::create() + ->set('Space*Rules', "\n") + ->set('Space*Blocks', "\n") + ->setSpaceBetweenBlocks("\n\n") + ->set( + 'SpaceAfterListArgumentSeparator', + array( + 'default' => '', + ',' => ' ' + ) + ); + } +} diff --git a/lib/Sabberworm/CSS/OutputFormatter.php b/lib/Sabberworm/CSS/OutputFormatter.php new file mode 100644 index 00000000..fde6923e --- /dev/null +++ b/lib/Sabberworm/CSS/OutputFormatter.php @@ -0,0 +1,236 @@ +oFormat = $oFormat; + } + + /** + * @param $sName + * @param string|null $sType + * + * @return string + */ + public function space($sName, $sType = null) + { + $sSpaceString = $this->oFormat->get("Space$sName"); + // If $sSpaceString is an array, we have multiple values configured depending on the type of object the space applies to + if (is_array($sSpaceString)) { + if ($sType !== null && isset($sSpaceString[$sType])) { + $sSpaceString = $sSpaceString[$sType]; + } else { + $sSpaceString = reset($sSpaceString); + } + } + return $this->prepareSpace($sSpaceString); + } + + /** + * @return mixed + */ + public function spaceAfterRuleName() + { + return $this->space('AfterRuleName'); + } + + /** + * @return string + */ + public function spaceBeforeRules() + { + return $this->space('BeforeRules'); + } + + /** + * @return string + */ + public function spaceAfterRules() + { + return $this->space('AfterRules'); + } + + /** + * @return string + */ + public function spaceBetweenRules() + { + return $this->space('BetweenRules'); + } + + /** + * @return string + */ + public function spaceBeforeBlocks() + { + return $this->space('BeforeBlocks'); + } + + /** + * @return string + */ + public function spaceAfterBlocks() + { + return $this->space('AfterBlocks'); + } + + /** + * @return string + */ + public function spaceBetweenBlocks() + { + return $this->space('BetweenBlocks'); + } + + /** + * @return string + */ + public function spaceBeforeSelectorSeparator() + { + return $this->space('BeforeSelectorSeparator'); + } + + /** + * @return string + */ + public function spaceAfterSelectorSeparator() + { + return $this->space('AfterSelectorSeparator'); + } + + /** + * @param string $sSeparator + * + * @return string + */ + public function spaceBeforeListArgumentSeparator($sSeparator) + { + return $this->space('BeforeListArgumentSeparator', $sSeparator); + } + + /** + * @param string $sSeparator + * + * @return string + */ + public function spaceAfterListArgumentSeparator($sSeparator) + { + return $this->space('AfterListArgumentSeparator', $sSeparator); + } + + /** + * @return string + */ + public function spaceBeforeOpeningBrace() + { + return $this->space('BeforeOpeningBrace'); + } + + /** + * Calls the given function, either swallowing or passing exceptions, depending on the bIgnoreExceptions setting. + * + * @param string $cCode Function name + * + * @return mixed + * + * @throws OutputException + */ + public function safely($cCode) + { + if ($this->oFormat->get('IgnoreExceptions')) { + // If output exceptions are ignored, run the code with exception guards + try { + return $cCode(); + } catch (OutputException $e) { + return null; + } // Do nothing + } else { + // Run the code as-is + return $cCode(); + } + } + + /** + * Clone of the implode function but calls ->render with the current output format instead of __toString() + * + * @param string $sSeparator + * @param string[] $aValues + * @param bool $bIncreaseLevel + * + * @return string + */ + public function implode($sSeparator, $aValues, $bIncreaseLevel = false) + { + $sResult = ''; + $oFormat = $this->oFormat; + if ($bIncreaseLevel) { + $oFormat = $oFormat->nextLevel(); + } + $bIsFirst = true; + foreach ($aValues as $mValue) { + if ($bIsFirst) { + $bIsFirst = false; + } else { + $sResult .= $sSeparator; + } + if ($mValue instanceof \Sabberworm\CSS\Renderable) { + $sResult .= $mValue->render($oFormat); + } else { + $sResult .= $mValue; + } + } + return $sResult; + } + + /** + * @param $sString + * + * @return array|string + */ + public function removeLastSemicolon($sString) + { + if ($this->oFormat->get('SemicolonAfterLastRule')) { + return $sString; + } + $sString = explode(';', $sString); + if (count($sString) < 2) { + return $sString[0]; + } + $sLast = array_pop($sString); + $sNextToLast = array_pop($sString); + array_push($sString, $sNextToLast.$sLast); + return implode(';', $sString); + } + + /** + * @param $sSpaceString + * + * @return string + */ + private function prepareSpace($sSpaceString) + { + return str_replace("\n", "\n".$this->indent(), $sSpaceString); + } + + /** + * Returns the indentation character, repeated as many times as the current level + * @return string + */ + private function indent() + { + return str_repeat($this->oFormat->sIndentation, $this->oFormat->level()); + } +} From 0f4a139451b7fe2e94652c56d3382ab065c1f1a4 Mon Sep 17 00:00:00 2001 From: Pablo Borowicz Date: Tue, 26 Dec 2017 18:01:18 +0100 Subject: [PATCH 2/4] Comments and code formatting --- lib/Sabberworm/CSS/CSSList/CSSList.php | 68 ++-- lib/Sabberworm/CSS/CSSList/Document.php | 16 +- lib/Sabberworm/CSS/Comment/Commentable.php | 30 +- lib/Sabberworm/CSS/Renderable.php | 22 +- lib/Sabberworm/CSS/Rule/Rule.php | 18 +- .../CSS/RuleSet/DeclarationBlock.php | 19 +- tests/Sabberworm/CSS/OutputFormatTest.php | 305 +++++++++++------- 7 files changed, 300 insertions(+), 178 deletions(-) diff --git a/lib/Sabberworm/CSS/CSSList/CSSList.php b/lib/Sabberworm/CSS/CSSList/CSSList.php index bc90460b..ae550fda 100644 --- a/lib/Sabberworm/CSS/CSSList/CSSList.php +++ b/lib/Sabberworm/CSS/CSSList/CSSList.php @@ -2,6 +2,7 @@ namespace Sabberworm\CSS\CSSList; +use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Renderable; use Sabberworm\CSS\RuleSet\DeclarationBlock; use Sabberworm\CSS\RuleSet\RuleSet; @@ -90,40 +91,45 @@ public function removeDeclarationBlockBySelector($mSelector, $bRemoveAll = false } public function __toString() { - return $this->render(new \Sabberworm\CSS\OutputFormat()); + return $this->render(new OutputFormat()); } - public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { - $sResult = ''; - $bIsFirst = true; - $oNextLevel = $oOutputFormat; - if(!$this->isRootList()) { - $oNextLevel = $oOutputFormat->nextLevel(); - } - foreach ($this->aContents as $oContent) { - $sRendered = $oOutputFormat->safely(function() use ($oNextLevel, $oContent) { - return $oContent->render($oNextLevel); - }); - if($sRendered === null) { - continue; - } - if($bIsFirst) { - $bIsFirst = false; - $sResult .= $oNextLevel->spaceBeforeBlocks(); - } else { - $sResult .= $oNextLevel->spaceBetweenBlocks(); - } - $sResult .= $sRendered; - } - - if(!$bIsFirst) { - // Had some output - $sResult .= $oOutputFormat->spaceAfterBlocks(); - } - - return $sResult; + /** + * {@inheritdoc} + */ + public function render(OutputFormat $oOutputFormat) { + $sResult = ''; + $bIsFirst = true; + $oNextLevel = $oOutputFormat; + if (!$this->isRootList()) { + $oNextLevel = $oOutputFormat->nextLevel(); + } + foreach ($this->aContents as $oContent) { + $sRendered = $oOutputFormat->safely( + function () use ($oNextLevel, $oContent) { + return $oContent->render($oNextLevel); + } + ); + if ($sRendered === null) { + continue; + } + if ($bIsFirst) { + $bIsFirst = false; + $sResult .= $oNextLevel->spaceBeforeBlocks(); + } else { + $sResult .= $oNextLevel->spaceBetweenBlocks(); + } + $sResult .= $sRendered; + } + + if (!$bIsFirst) { + // Had some output + $sResult .= $oOutputFormat->spaceAfterBlocks(); + } + + return $sResult; } - + /** * Return true if the list can not be further outdented. Only important when rendering. */ diff --git a/lib/Sabberworm/CSS/CSSList/Document.php b/lib/Sabberworm/CSS/CSSList/Document.php index bd4a23ee..2d045869 100644 --- a/lib/Sabberworm/CSS/CSSList/Document.php +++ b/lib/Sabberworm/CSS/CSSList/Document.php @@ -2,6 +2,8 @@ namespace Sabberworm\CSS\CSSList; +use Sabberworm\CSS\OutputFormat; + /** * The root CSSList of a parsed file. Contains all top-level css contents, mostly declaration blocks, but also any @-rules encountered. */ @@ -90,10 +92,16 @@ public function createShorthands() { } } - // Override render() to make format argument optional - public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat = null) { + /** + * {@inheritdoc} + * + * @param OutputFormat|null $oOutputFormat + * + * @return string + */ + public function render(OutputFormat $oOutputFormat = null) { if($oOutputFormat === null) { - $oOutputFormat = new \Sabberworm\CSS\OutputFormat(); + $oOutputFormat = new OutputFormat(); } return parent::render($oOutputFormat); } @@ -102,4 +110,4 @@ public function isRootList() { return true; } -} \ No newline at end of file +} diff --git a/lib/Sabberworm/CSS/Comment/Commentable.php b/lib/Sabberworm/CSS/Comment/Commentable.php index 3100f17a..484d6d52 100644 --- a/lib/Sabberworm/CSS/Comment/Commentable.php +++ b/lib/Sabberworm/CSS/Comment/Commentable.php @@ -2,22 +2,20 @@ namespace Sabberworm\CSS\Comment; -interface Commentable { - - /** - * @param array $aComments Array of comments. - */ - public function addComments(array $aComments); - - /** - * @return array - */ - public function getComments(); - - /** - * @param array $aComments Array containing Comment objects. - */ - public function setComments(array $aComments); +interface Commentable +{ + /** + * @param Comment[] $aComments Array of comments. + */ + public function addComments(array $aComments); + /** + * @return Comment[] + */ + public function getComments(); + /** + * @param Comment[] $aComments Array containing Comment objects. + */ + public function setComments(array $aComments); } diff --git a/lib/Sabberworm/CSS/Renderable.php b/lib/Sabberworm/CSS/Renderable.php index 3ac06652..ea4f2fa9 100644 --- a/lib/Sabberworm/CSS/Renderable.php +++ b/lib/Sabberworm/CSS/Renderable.php @@ -2,8 +2,26 @@ namespace Sabberworm\CSS; +use Sabberworm\CSS\OutputFormat; + interface Renderable { + + /** + * @return string + */ public function __toString(); - public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat); + + /** + * Renders this component + * + * @param OutputFormat $oOutputFormat Formatting options + * + * @return string Rendered CSS + */ + public function render(OutputFormat $oOutputFormat); + + /** + * @return int Line number + */ public function getLineNo(); -} \ No newline at end of file +} diff --git a/lib/Sabberworm/CSS/Rule/Rule.php b/lib/Sabberworm/CSS/Rule/Rule.php index 3e485375..afe60c8a 100644 --- a/lib/Sabberworm/CSS/Rule/Rule.php +++ b/lib/Sabberworm/CSS/Rule/Rule.php @@ -2,6 +2,8 @@ namespace Sabberworm\CSS\Rule; +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Renderable; use Sabberworm\CSS\Value\RuleValueList; use Sabberworm\CSS\Value\Value; @@ -152,11 +154,17 @@ public function getIsImportant() { return $this->bIsImportant; } + /** + * {@inheritdoc} + */ public function __toString() { - return $this->render(new \Sabberworm\CSS\OutputFormat()); + return $this->render(new OutputFormat()); } - public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { + /** + * {@inheritdoc} + */ + public function render(OutputFormat $oOutputFormat) { $sResult = "{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}"; if ($this->mValue instanceof Value) { //Can also be a ValueList $sResult .= $this->mValue->render($oOutputFormat); @@ -174,21 +182,21 @@ public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { } /** - * @param array $aComments Array of comments. + * {@inheritdoc} */ public function addComments(array $aComments) { $this->aComments = array_merge($this->aComments, $aComments); } /** - * @return array + * {@inheritdoc} */ public function getComments() { return $this->aComments; } /** - * @param array $aComments Array containing Comment objects. + * {@inheritdoc} */ public function setComments(array $aComments) { $this->aComments = $aComments; diff --git a/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php b/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php index e18f5d82..20543f79 100644 --- a/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php +++ b/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php @@ -2,6 +2,8 @@ namespace Sabberworm\CSS\RuleSet; +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Property\Selector; use Sabberworm\CSS\Rule\Rule; use Sabberworm\CSS\Value\RuleValueList; @@ -87,7 +89,7 @@ public function expandShorthands() { public function createShorthands() { $this->createBackgroundShorthand(); $this->createDimensionsShorthand(); - // border must be shortened after dimensions + // border must be shortened after dimensions $this->createBorderShorthand(); $this->createFontShorthand(); $this->createListStyleShorthand(); @@ -446,7 +448,7 @@ public function createBorderShorthand() { /* * Looks for long format CSS dimensional properties - * (margin, padding, border-color, border-style and border-width) + * (margin, padding, border-color, border-style and border-width) * and converts them into shorthand CSS properties. * */ @@ -501,7 +503,7 @@ public function createDimensionsShorthand() { $oNewRule->addValue($aValues['bottom']); } } else { - // No sides are equal + // No sides are equal $oNewRule->addValue($aValues['top']); $oNewRule->addValue($aValues['left']); $oNewRule->addValue($aValues['bottom']); @@ -516,8 +518,8 @@ public function createDimensionsShorthand() { } /** - * Looks for long format CSS font properties (e.g. font-weight) and - * tries to convert them into a shorthand CSS font property. + * Looks for long format CSS font properties (e.g. font-weight) and + * tries to convert them into a shorthand CSS font property. * At least font-size AND font-family must be present in order to create a shorthand declaration. * */ public function createFontShorthand() { @@ -591,10 +593,13 @@ public function createFontShorthand() { } public function __toString() { - return $this->render(new \Sabberworm\CSS\OutputFormat()); + return $this->render(new OutputFormat()); } - public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { + /** + * {@inheritdoc} + */ + public function render(OutputFormat $oOutputFormat) { if(count($this->aSelectors) === 0) { // If all the selectors have been removed, this declaration block becomes invalid throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo); diff --git a/tests/Sabberworm/CSS/OutputFormatTest.php b/tests/Sabberworm/CSS/OutputFormatTest.php index 238b5ba5..a534dfc1 100644 --- a/tests/Sabberworm/CSS/OutputFormatTest.php +++ b/tests/Sabberworm/CSS/OutputFormatTest.php @@ -2,12 +2,18 @@ namespace Sabberworm\CSS; -use Sabberworm\CSS\Parser; -use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\CSSList\Document; -global $TEST_CSS; +class OutputFormatTest extends \PHPUnit_Framework_TestCase +{ + private $oParser; -$TEST_CSS = <<oParser = new Parser($TEST_CSS); - $this->oDocument = $this->oParser->parse(); - } - - public function testPlain() { - $this->assertSame('.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render()); - } - - public function testCompact() { - $this->assertSame('.main,.test{font:italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background:white;}@media screen{.main{background-size:100% 100%;font-size:1.3em;background-color:#fff;}}', $this->oDocument->render(OutputFormat::createCompact())); - } - - public function testPretty() { - global $TEST_CSS; - $this->assertSame($TEST_CSS, $this->oDocument->render(OutputFormat::createPretty())); - } - - public function testSpaceAfterListArgumentSeparator() { - $this->assertSame('.main, .test {font: italic normal bold 16px/ 1.2 "Helvetica", Verdana, sans-serif;background: white;} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setSpaceAfterListArgumentSeparator(" "))); - } - - public function testSpaceAfterListArgumentSeparatorComplex() { - $this->assertSame('.main, .test {font: italic normal bold 16px/1.2 "Helvetica", Verdana, sans-serif;background: white;} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setSpaceAfterListArgumentSeparator(array('default' => ' ', ',' => "\t", '/' => '', ' ' => '')))); - } - - public function testSpaceAfterSelectorSeparator() { - $this->assertSame('.main, + protected function setUp() + { + $this->oParser = new Parser(self::$testCSS); + $this->oDocument = $this->oParser->parse(); + } + + public function testPlain() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render() + ); + } + + public function testCompact() + { + $this->assertSame( + '.main,.test{font:italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background:white;}@media screen{.main{background-size:100% 100%;font-size:1.3em;background-color:#fff;}}', + $this->oDocument->render(OutputFormat::createCompact()) + ); + } + + public function testPretty() + { + $this->assertSame( + self::$testCSS, + $this->oDocument->render(OutputFormat::createPretty()) + ); + } + + public function testSpaceAfterListArgumentSeparator() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/ 1.2 "Helvetica", Verdana, sans-serif;background: white;} +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setSpaceAfterListArgumentSeparator(" ")) + ); + } + + public function testSpaceAfterListArgumentSeparatorComplex() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica", Verdana, sans-serif;background: white;} +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render( + OutputFormat::create()->setSpaceAfterListArgumentSeparator( + array('default' => ' ', ',' => "\t", '/' => '', ' ' => '') + ) + ) + ); + } + + public function testSpaceAfterSelectorSeparator() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setSpaceAfterSelectorSeparator("\n"))); - } - - public function testStringQuotingType() { - $this->assertSame('.main, .test {font: italic normal bold 16px/1.2 \'Helvetica\',Verdana,sans-serif;background: white;} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setStringQuotingType("'"))); - } - - public function testRGBHashNotation() { - $this->assertSame('.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: rgb(255,255,255);}}', $this->oDocument->render(OutputFormat::create()->setRGBHashNotation(false))); - } - - public function testSemicolonAfterLastRule() { - $this->assertSame('.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff}}', $this->oDocument->render(OutputFormat::create()->setSemicolonAfterLastRule(false))); - } - - public function testSpaceAfterRuleName() { - $this->assertSame('.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setSpaceAfterRuleName("\t"))); - } - - public function testSpaceRules() { - $this->assertSame('.main, .test { +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setSpaceAfterSelectorSeparator("\n")) + ); + } + + public function testStringQuotingType() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 \'Helvetica\',Verdana,sans-serif;background: white;} +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setStringQuotingType("'")) + ); + } + + public function testRGBHashNotation() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: rgb(255,255,255);}}', + $this->oDocument->render(OutputFormat::create()->setRGBHashNotation(false)) + ); + } + + public function testSemicolonAfterLastRule() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white} +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff}}', + $this->oDocument->render(OutputFormat::create()->setSemicolonAfterLastRule(false)) + ); + } + + public function testSpaceAfterRuleName() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setSpaceAfterRuleName("\t")) + ); + } + + public function testSpaceRules() + { + $this->assertSame( + '.main, .test { font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif; background: white; } @@ -93,20 +140,28 @@ public function testSpaceRules() { background-size: 100% 100%; font-size: 1.3em; background-color: #fff; - }}', $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n"))); - } - - public function testSpaceBlocks() { - $this->assertSame(' + }}', + $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n")) + ); + } + + public function testSpaceBlocks() + { + $this->assertSame( + ' .main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} @media screen { .main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;} } -', $this->oDocument->render(OutputFormat::create()->set('Space*Blocks', "\n"))); - } - - public function testSpaceBoth() { - $this->assertSame(' +', + $this->oDocument->render(OutputFormat::create()->set('Space*Blocks', "\n")) + ); + } + + public function testSpaceBoth() + { + $this->assertSame( + ' .main, .test { font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif; background: white; @@ -118,15 +173,23 @@ public function testSpaceBoth() { background-color: #fff; } } -', $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n"))); - } - - public function testSpaceBetweenBlocks() { - $this->assertSame('.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;}@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setSpaceBetweenBlocks(''))); - } - - public function testIndentation() { - $this->assertSame(' +', + $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")) + ); + } + + public function testSpaceBetweenBlocks() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;}@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setSpaceBetweenBlocks('')) + ); + } + + public function testIndentation() + { + $this->assertSame( + ' .main, .test { font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif; background: white; @@ -138,33 +201,49 @@ public function testIndentation() { background-color: #fff; } } -', $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setIndentation(''))); - } - - public function testSpaceBeforeBraces() { - $this->assertSame('.main, .test{font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} -@media screen{.main{background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setSpaceBeforeOpeningBrace(''))); - } - - /** - * @expectedException Sabberworm\CSS\Parsing\OutputException - */ - public function testIgnoreExceptionsOff() { - $aBlocks = $this->oDocument->getAllDeclarationBlocks(); - $oFirstBlock = $aBlocks[0]; - $oFirstBlock->removeSelector('.main'); - $this->assertSame('.test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} -@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(false))); - $oFirstBlock->removeSelector('.test'); - $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(false)); - } - - public function testIgnoreExceptionsOn() { - $aBlocks = $this->oDocument->getAllDeclarationBlocks(); - $oFirstBlock = $aBlocks[0]; - $oFirstBlock->removeSelector('.main'); - $oFirstBlock->removeSelector('.test'); - $this->assertSame('@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(true))); - } +', + $this->oDocument->render( + OutputFormat::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setIndentation('') + ) + ); + } + + public function testSpaceBeforeBraces() + { + $this->assertSame( + '.main, .test{font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} +@media screen{.main{background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setSpaceBeforeOpeningBrace('')) + ); + } + + /** + * @expectedException Sabberworm\CSS\Parsing\OutputException + */ + public function testIgnoreExceptionsOff() + { + $aBlocks = $this->oDocument->getAllDeclarationBlocks(); + $oFirstBlock = $aBlocks[0]; + $oFirstBlock->removeSelector('.main'); + $this->assertSame( + '.test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} +@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(false)) + ); + $oFirstBlock->removeSelector('.test'); + $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(false)); + } + + public function testIgnoreExceptionsOn() + { + $aBlocks = $this->oDocument->getAllDeclarationBlocks(); + $oFirstBlock = $aBlocks[0]; + $oFirstBlock->removeSelector('.main'); + $oFirstBlock->removeSelector('.test'); + $this->assertSame( + '@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(true)) + ); + } -} \ No newline at end of file +} From 9e23f77d2131fd2f11897ca10c806cf6287eb121 Mon Sep 17 00:00:00 2001 From: Pablo Borowicz Date: Tue, 26 Dec 2017 18:12:22 +0100 Subject: [PATCH 3/4] Add option in OutputFormat to allow keeping comments --- lib/Sabberworm/CSS/OutputFormat.php | 27 +++++++ .../CSS/RuleSet/DeclarationBlock.php | 24 +++++- lib/Sabberworm/CSS/RuleSet/RuleSet.php | 76 +++++++++++++++---- tests/Sabberworm/CSS/OutputFormatTest.php | 20 +++++ 4 files changed, 131 insertions(+), 16 deletions(-) diff --git a/lib/Sabberworm/CSS/OutputFormat.php b/lib/Sabberworm/CSS/OutputFormat.php index cc81b786..48773f4a 100644 --- a/lib/Sabberworm/CSS/OutputFormat.php +++ b/lib/Sabberworm/CSS/OutputFormat.php @@ -76,6 +76,12 @@ class OutputFormat { */ public $sIndentation = "\t"; + /** + * Indicates if comments should be kept or thrown away + * @var bool + */ + private $bKeepComments = false; + /** * Output exceptions. * @var bool @@ -220,6 +226,26 @@ public function level() return $this->iIndentationLevel; } + /** + * Indicates if comments should be kept or thrown away + * @param bool $toggle + * @return $this + */ + public function setKeepComments($toggle) + { + $this->bKeepComments = $toggle; + return $this; + } + + /** + * Indicates if comments should be kept or thrown away + * @return bool + */ + public function getKeepComments() + { + return $this->bKeepComments; + } + /** * @return OutputFormat */ @@ -247,6 +273,7 @@ public static function createCompact() public static function createPretty() { return self::create() + ->setKeepComments(true) ->set('Space*Rules', "\n") ->set('Space*Blocks', "\n") ->setSpaceBetweenBlocks("\n\n") diff --git a/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php b/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php index 20543f79..5a19ac7c 100644 --- a/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php +++ b/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php @@ -604,7 +604,29 @@ public function render(OutputFormat $oOutputFormat) { // If all the selectors have been removed, this declaration block becomes invalid throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo); } - $sResult = $oOutputFormat->implode($oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(), $this->aSelectors) . $oOutputFormat->spaceBeforeOpeningBrace() . '{'; + + $sResult = ''; + + // render comments + if ($oOutputFormat->getKeepComments()) { + $comments = $this->getCommentsBefore(); + if (!empty($comments)) { + $sResult .= implode( + '', + array_map( + function (Comment $comment) use ($oOutputFormat) { + return '/*' . $comment->getComment() . "*/\n"; + }, + $comments + ) + ); + } + } + + $sResult .= $oOutputFormat->implode( + $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(), + $this->aSelectors + ) . $oOutputFormat->spaceBeforeOpeningBrace() . '{'; $sResult .= parent::render($oOutputFormat); $sResult .= '}'; return $sResult; diff --git a/lib/Sabberworm/CSS/RuleSet/RuleSet.php b/lib/Sabberworm/CSS/RuleSet/RuleSet.php index 124be88d..88052720 100644 --- a/lib/Sabberworm/CSS/RuleSet/RuleSet.php +++ b/lib/Sabberworm/CSS/RuleSet/RuleSet.php @@ -2,6 +2,7 @@ namespace Sabberworm\CSS\RuleSet; +use Sabberworm\CSS\Comment\Comment; use Sabberworm\CSS\Rule\Rule; use Sabberworm\CSS\Renderable; use Sabberworm\CSS\Comment\Commentable; @@ -47,12 +48,20 @@ public function addRule(Rule $oRule, Rule $oSibling = null) { array_splice($this->aRules[$sRule], $iPosition, 0, array($oRule)); } - /** - * Returns all rules matching the given rule name - * @param (null|string|Rule) $mRule pattern to search for. If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are returned as well as one matching the pattern with the dash excluded. passing a Rule behaves like calling getRules($mRule->getRule()). - * @example $oRuleSet->getRules('font-') //returns an array of all rules either beginning with font- or matching font. - * @example $oRuleSet->getRules('font') //returns array(0 => $oRule, …) or array(). - */ + /** + * Returns all rules matching the given rule name + * + * @param (null|string|Rule) $mRule pattern to search for. + * If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are + * returned as well as one matching the pattern with the dash excluded. + * Passing a Rule behaves like calling getRules($mRule->getRule()). + * + * @example $oRuleSet->getRules('font-') //returns an array of all rules either beginning with font- or matching + * font. + * @example $oRuleSet->getRules('font') //returns array(0 => $oRule, …) or array(). + * + * @return array + */ public function getRules($mRule = null) { if ($mRule instanceof Rule) { $mRule = $mRule->getRule(); @@ -78,11 +87,21 @@ public function setRules(array $aRules) { } } - /** - * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name as keys. This method exists mainly for backwards-compatibility and is really only partially useful. - * @param (string) $mRule pattern to search for. If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are returned as well as one matching the pattern with the dash excluded. passing a Rule behaves like calling getRules($mRule->getRule()). - * Note: This method loses some information: Calling this (with an argument of 'background-') on a declaration block like { background-color: green; background-color; rgba(0, 127, 0, 0.7); } will only yield an associative array containing the rgba-valued rule while @link{getRules()} would yield an indexed array containing both. - */ + /** + * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name as + * keys. This method exists mainly for backwards-compatibility and is really only partially useful. + * + * @param (string) $mRule pattern to search for. + * If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are + * returned as well as one matching the pattern with the dash excluded. + * Passing a Rule behaves like calling getRules($mRule->getRule()). + * Note: This method loses some information: Calling this (with an argument of 'background-') + * on a declaration block like { background-color: green; background-color; rgba(0, 127, 0, 0.7); } + * will only yield an associative array containing the rgba-valued rule while @link{getRules()} + * would yield an indexed array containing both. + * + * @return array + */ public function getRulesAssoc($mRule = null) { $aResult = array(); foreach($this->getRules($mRule) as $oRule) { @@ -92,9 +111,19 @@ public function getRulesAssoc($mRule = null) { } /** - * Remove a rule from this RuleSet. This accepts all the possible values that @link{getRules()} accepts. If given a Rule, it will only remove this particular rule (by identity). If given a name, it will remove all rules by that name. Note: this is different from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would remove all rules with the same name. To get the old behvaiour, use removeRule($oRule->getRule()). - * @param (null|string|Rule) $mRule pattern to remove. If $mRule is null, all rules are removed. If the pattern ends in a dash, all rules starting with the pattern are removed as well as one matching the pattern with the dash excluded. Passing a Rule behaves matches by identity. - */ + * Remove a rule from this RuleSet. + * + * This accepts all the possible values that @link{getRules()} accepts. + * If given a Rule, it will only remove this particular rule (by identity). + * If given a name, it will remove all rules by that name. + * Note: this is different from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would + * remove all rules with the same name. To get the old behvaiour, use removeRule($oRule->getRule()). + * + * @param (null|string|Rule) $mRule pattern to remove. + * If $mRule is null, all rules are removed. If the pattern ends in a dash, all rules starting with the pattern + * are removed as well as one matching the pattern with the dash excluded. + * Passing a Rule behaves matches by identity. + */ public function removeRule($mRule) { if($mRule instanceof Rule) { $sRule = $mRule->getRule(); @@ -140,7 +169,7 @@ public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { $sResult .= $sRendered; } } - + if(!$bIsFirst) { // Had some output $sResult .= $oOutputFormat->spaceAfterRules(); @@ -170,4 +199,21 @@ public function setComments(array $aComments) { $this->aComments = $aComments; } + /** + * Returns all comments that were declared before this Rule + * @return Comment[] + */ + public function getCommentsBefore() + { + $lineNo = $this->getLineNo(); + $return = array(); + /** @var Comment $comment */ + foreach ($this->aComments as $comment) { + if ($comment->getLineNo() <= $lineNo) { + $return[] = $comment; + } + } + + return $return; + } } diff --git a/tests/Sabberworm/CSS/OutputFormatTest.php b/tests/Sabberworm/CSS/OutputFormatTest.php index a534dfc1..ea87b3dc 100644 --- a/tests/Sabberworm/CSS/OutputFormatTest.php +++ b/tests/Sabberworm/CSS/OutputFormatTest.php @@ -15,6 +15,9 @@ class OutputFormatTest extends \PHPUnit_Framework_TestCase private static $testCSS = <<assertSame( + $expected, + $this->oDocument->render( + OutputFormat::createCompact() + ->setKeepComments(true) + ) + ); + } + public function testPretty() { $this->assertSame( From 85ead7a089d5751ceaa7b364e9af5a61449f36ac Mon Sep 17 00:00:00 2001 From: Pablo Borowicz Date: Thu, 28 Dec 2017 13:50:11 +0100 Subject: [PATCH 4/4] Indent using tabs --- lib/Sabberworm/CSS/CSSList/CSSList.php | 66 +-- lib/Sabberworm/CSS/CSSList/Document.php | 14 +- lib/Sabberworm/CSS/Comment/Commentable.php | 24 +- lib/Sabberworm/CSS/OutputFormat.php | 412 +++++++++--------- lib/Sabberworm/CSS/Renderable.php | 26 +- lib/Sabberworm/CSS/Rule/Rule.php | 12 +- .../CSS/RuleSet/DeclarationBlock.php | 40 +- lib/Sabberworm/CSS/RuleSet/RuleSet.php | 110 ++--- tests/Sabberworm/CSS/OutputFormatTest.php | 332 +++++++------- 9 files changed, 518 insertions(+), 518 deletions(-) diff --git a/lib/Sabberworm/CSS/CSSList/CSSList.php b/lib/Sabberworm/CSS/CSSList/CSSList.php index ae550fda..97cd599b 100644 --- a/lib/Sabberworm/CSS/CSSList/CSSList.php +++ b/lib/Sabberworm/CSS/CSSList/CSSList.php @@ -94,40 +94,40 @@ public function __toString() { return $this->render(new OutputFormat()); } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ public function render(OutputFormat $oOutputFormat) { - $sResult = ''; - $bIsFirst = true; - $oNextLevel = $oOutputFormat; - if (!$this->isRootList()) { - $oNextLevel = $oOutputFormat->nextLevel(); - } - foreach ($this->aContents as $oContent) { - $sRendered = $oOutputFormat->safely( - function () use ($oNextLevel, $oContent) { - return $oContent->render($oNextLevel); - } - ); - if ($sRendered === null) { - continue; - } - if ($bIsFirst) { - $bIsFirst = false; - $sResult .= $oNextLevel->spaceBeforeBlocks(); - } else { - $sResult .= $oNextLevel->spaceBetweenBlocks(); - } - $sResult .= $sRendered; - } - - if (!$bIsFirst) { - // Had some output - $sResult .= $oOutputFormat->spaceAfterBlocks(); - } - - return $sResult; + $sResult = ''; + $bIsFirst = true; + $oNextLevel = $oOutputFormat; + if (!$this->isRootList()) { + $oNextLevel = $oOutputFormat->nextLevel(); + } + foreach ($this->aContents as $oContent) { + $sRendered = $oOutputFormat->safely( + function () use ($oNextLevel, $oContent) { + return $oContent->render($oNextLevel); + } + ); + if ($sRendered === null) { + continue; + } + if ($bIsFirst) { + $bIsFirst = false; + $sResult .= $oNextLevel->spaceBeforeBlocks(); + } else { + $sResult .= $oNextLevel->spaceBetweenBlocks(); + } + $sResult .= $sRendered; + } + + if (!$bIsFirst) { + // Had some output + $sResult .= $oOutputFormat->spaceAfterBlocks(); + } + + return $sResult; } /** diff --git a/lib/Sabberworm/CSS/CSSList/Document.php b/lib/Sabberworm/CSS/CSSList/Document.php index 2d045869..30b29467 100644 --- a/lib/Sabberworm/CSS/CSSList/Document.php +++ b/lib/Sabberworm/CSS/CSSList/Document.php @@ -92,13 +92,13 @@ public function createShorthands() { } } - /** - * {@inheritdoc} - * - * @param OutputFormat|null $oOutputFormat - * - * @return string - */ + /** + * {@inheritdoc} + * + * @param OutputFormat|null $oOutputFormat + * + * @return string + */ public function render(OutputFormat $oOutputFormat = null) { if($oOutputFormat === null) { $oOutputFormat = new OutputFormat(); diff --git a/lib/Sabberworm/CSS/Comment/Commentable.php b/lib/Sabberworm/CSS/Comment/Commentable.php index 484d6d52..68540f02 100644 --- a/lib/Sabberworm/CSS/Comment/Commentable.php +++ b/lib/Sabberworm/CSS/Comment/Commentable.php @@ -4,18 +4,18 @@ interface Commentable { - /** - * @param Comment[] $aComments Array of comments. - */ - public function addComments(array $aComments); + /** + * @param Comment[] $aComments Array of comments. + */ + public function addComments(array $aComments); - /** - * @return Comment[] - */ - public function getComments(); + /** + * @return Comment[] + */ + public function getComments(); - /** - * @param Comment[] $aComments Array containing Comment objects. - */ - public function setComments(array $aComments); + /** + * @param Comment[] $aComments Array containing Comment objects. + */ + public function setComments(array $aComments); } diff --git a/lib/Sabberworm/CSS/OutputFormat.php b/lib/Sabberworm/CSS/OutputFormat.php index 48773f4a..ab4753ca 100644 --- a/lib/Sabberworm/CSS/OutputFormat.php +++ b/lib/Sabberworm/CSS/OutputFormat.php @@ -8,24 +8,24 @@ class OutputFormat { /** - * String quoting type. - * " means double-quote, ' means single-quote - * @var string - */ + * String quoting type. + * " means double-quote, ' means single-quote + * @var string + */ public $sStringQuotingType = '"'; /** - * Output RGB colors in hash notation if possible - * @var bool - */ + * Output RGB colors in hash notation if possible + * @var bool + */ public $bRGBHashNotation = true; // Declaration format - /** - * Semicolon after the last rule of a declaration block can be omitted. To do that, set this false. - * @var bool - */ + /** + * Semicolon after the last rule of a declaration block can be omitted. To do that, set this false. + * @var bool + */ public $bSemicolonAfterLastRule = true; /** @@ -46,45 +46,45 @@ class OutputFormat { public $sSpaceBetweenBlocks = "\n"; /** - * Printed before and after the comma if a declaration block contains multiple selectors. - * @var string - */ + * Printed before and after the comma if a declaration block contains multiple selectors. + * @var string + */ public $sSpaceBeforeSelectorSeparator = ''; - /** - * Printed after the comma if a declaration block contains multiple selectors. - * @var string - */ + /** + * Printed after the comma if a declaration block contains multiple selectors. + * @var string + */ public $sSpaceAfterSelectorSeparator = ' '; - /** - * Printed before the comma of value lists. - * @var string - */ - public $sSpaceBeforeListArgumentSeparator = ''; + /** + * Printed before the comma of value lists. + * @var string + */ + public $sSpaceBeforeListArgumentSeparator = ''; - /** - * Printed after the comma of value lists. - * @var string - */ + /** + * Printed after the comma of value lists. + * @var string + */ public $sSpaceAfterListArgumentSeparator = ''; public $sSpaceBeforeOpeningBrace = ' '; /** * Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings. - * @var string + * @var string */ public $sIndentation = "\t"; - /** - * Indicates if comments should be kept or thrown away - * @var bool - */ + /** + * Indicates if comments should be kept or thrown away + * @var bool + */ private $bKeepComments = false; /** * Output exceptions. - * @var bool + * @var bool */ public $bIgnoreExceptions = false; @@ -92,11 +92,11 @@ class OutputFormat { private $oNextLevelFormat = null; private $iIndentationLevel = 0; - /** - * @param $sName - * - * @return mixed|null - */ + /** + * @param $sName + * + * @return mixed|null + */ public function get($sName) { $aVarPrefixes = array('a', 's', 'm', 'b', 'f', 'o', 'c', 'i'); foreach($aVarPrefixes as $sPrefix) { @@ -108,181 +108,181 @@ public function get($sName) { return null; } - /** - * @param $aNames - * @param $mValue - * - * @return $this|false - */ + /** + * @param $aNames + * @param $mValue + * + * @return $this|false + */ public function set($aNames, $mValue) - { - $aVarPrefixes = array('a', 's', 'm', 'b', 'f', 'o', 'c', 'i'); - if (is_string($aNames) && strpos($aNames, '*') !== false) { - $aNames = array( - str_replace('*', 'Before', $aNames), - str_replace('*', 'Between', $aNames), - str_replace('*', 'After', $aNames), - ); - } elseif (!is_array($aNames)) { - $aNames = array($aNames); - } - foreach ($aVarPrefixes as $sPrefix) { - $bDidReplace = false; - foreach ($aNames as $sName) { - $sFieldName = $sPrefix.ucfirst($sName); - if (isset($this->$sFieldName)) { - $this->$sFieldName = $mValue; - $bDidReplace = true; - } - } - if ($bDidReplace) { - return $this; - } - } - // Break the chain so the user knows this option is invalid - return false; + { + $aVarPrefixes = array('a', 's', 'm', 'b', 'f', 'o', 'c', 'i'); + if (is_string($aNames) && strpos($aNames, '*') !== false) { + $aNames = array( + str_replace('*', 'Before', $aNames), + str_replace('*', 'Between', $aNames), + str_replace('*', 'After', $aNames), + ); + } elseif (!is_array($aNames)) { + $aNames = array($aNames); + } + foreach ($aVarPrefixes as $sPrefix) { + $bDidReplace = false; + foreach ($aNames as $sName) { + $sFieldName = $sPrefix.ucfirst($sName); + if (isset($this->$sFieldName)) { + $this->$sFieldName = $mValue; + $bDidReplace = true; + } + } + if ($bDidReplace) { + return $this; + } + } + // Break the chain so the user knows this option is invalid + return false; } - /** - * @param string $sMethodName - * @param array $aArguments - * - * @return false|mixed|null|OutputFormat - * @throws \Exception - */ + /** + * @param string $sMethodName + * @param array $aArguments + * + * @return false|mixed|null|OutputFormat + * @throws \Exception + */ public function __call($sMethodName, $aArguments) - { - if (strpos($sMethodName, 'set') === 0) { - return $this->set(substr($sMethodName, 3), $aArguments[0]); - } elseif (strpos($sMethodName, 'get') === 0) { - return $this->get(substr($sMethodName, 3)); - } elseif (method_exists('\\Sabberworm\\CSS\\OutputFormatter', $sMethodName)) { - return call_user_func_array(array($this->getFormatter(), $sMethodName), $aArguments); - } else { - throw new \Exception('Unknown OutputFormat method called: '.$sMethodName); - } + { + if (strpos($sMethodName, 'set') === 0) { + return $this->set(substr($sMethodName, 3), $aArguments[0]); + } elseif (strpos($sMethodName, 'get') === 0) { + return $this->get(substr($sMethodName, 3)); + } elseif (method_exists('\\Sabberworm\\CSS\\OutputFormatter', $sMethodName)) { + return call_user_func_array(array($this->getFormatter(), $sMethodName), $aArguments); + } else { + throw new \Exception('Unknown OutputFormat method called: '.$sMethodName); + } } - /** - * Sets indentation to a number of tabs - * - * @param int $iNumber [default=1] Number of tabs to indent with - * - * @return $this|false - */ - public function indentWithTabs($iNumber = 1) - { + /** + * Sets indentation to a number of tabs + * + * @param int $iNumber [default=1] Number of tabs to indent with + * + * @return $this|false + */ + public function indentWithTabs($iNumber = 1) + { return $this->setIndentation(str_repeat("\t", $iNumber)); } - /** - * Sets indentation to a number of spaces - * @param int $iNumber [default=2] Number of spaces to indent with - * - * @return $this|false - */ - public function indentWithSpaces($iNumber = 2) - { - return $this->setIndentation(str_repeat(" ", $iNumber)); - } - - /** - * @return null|OutputFormat - */ - public function nextLevel() - { - if ($this->oNextLevelFormat === null) { - $this->oNextLevelFormat = clone $this; - $this->oNextLevelFormat->iIndentationLevel++; - $this->oNextLevelFormat->oFormatter = null; - } - return $this->oNextLevelFormat; - } - - /** - * Activates exception ignoring - */ - public function beLenient() - { - $this->bIgnoreExceptions = true; - } - - /** - * @return null|OutputFormatter - */ - public function getFormatter() - { - if ($this->oFormatter === null) { - $this->oFormatter = new OutputFormatter($this); - } - return $this->oFormatter; - } - - /** - * @return int - */ - public function level() - { - return $this->iIndentationLevel; - } - - /** - * Indicates if comments should be kept or thrown away - * @param bool $toggle - * @return $this - */ - public function setKeepComments($toggle) - { - $this->bKeepComments = $toggle; - return $this; - } - - /** - * Indicates if comments should be kept or thrown away - * @return bool - */ - public function getKeepComments() - { - return $this->bKeepComments; - } - - /** - * @return OutputFormat - */ - public static function create() - { - return new OutputFormat(); - } - - /** - * @return OutputFormat|false - */ - public static function createCompact() - { - return self::create() - ->set('Space*Rules', "") - ->set('Space*Blocks', "") - ->setSpaceAfterRuleName('') - ->setSpaceBeforeOpeningBrace('') - ->setSpaceAfterSelectorSeparator(''); - } - - /** - * @return OutputFormat|false - */ - public static function createPretty() - { - return self::create() - ->setKeepComments(true) - ->set('Space*Rules', "\n") - ->set('Space*Blocks', "\n") - ->setSpaceBetweenBlocks("\n\n") - ->set( - 'SpaceAfterListArgumentSeparator', - array( - 'default' => '', - ',' => ' ' - ) - ); - } + /** + * Sets indentation to a number of spaces + * @param int $iNumber [default=2] Number of spaces to indent with + * + * @return $this|false + */ + public function indentWithSpaces($iNumber = 2) + { + return $this->setIndentation(str_repeat(" ", $iNumber)); + } + + /** + * @return null|OutputFormat + */ + public function nextLevel() + { + if ($this->oNextLevelFormat === null) { + $this->oNextLevelFormat = clone $this; + $this->oNextLevelFormat->iIndentationLevel++; + $this->oNextLevelFormat->oFormatter = null; + } + return $this->oNextLevelFormat; + } + + /** + * Activates exception ignoring + */ + public function beLenient() + { + $this->bIgnoreExceptions = true; + } + + /** + * @return null|OutputFormatter + */ + public function getFormatter() + { + if ($this->oFormatter === null) { + $this->oFormatter = new OutputFormatter($this); + } + return $this->oFormatter; + } + + /** + * @return int + */ + public function level() + { + return $this->iIndentationLevel; + } + + /** + * Indicates if comments should be kept or thrown away + * @param bool $toggle + * @return $this + */ + public function setKeepComments($toggle) + { + $this->bKeepComments = $toggle; + return $this; + } + + /** + * Indicates if comments should be kept or thrown away + * @return bool + */ + public function getKeepComments() + { + return $this->bKeepComments; + } + + /** + * @return OutputFormat + */ + public static function create() + { + return new OutputFormat(); + } + + /** + * @return OutputFormat|false + */ + public static function createCompact() + { + return self::create() + ->set('Space*Rules', "") + ->set('Space*Blocks', "") + ->setSpaceAfterRuleName('') + ->setSpaceBeforeOpeningBrace('') + ->setSpaceAfterSelectorSeparator(''); + } + + /** + * @return OutputFormat|false + */ + public static function createPretty() + { + return self::create() + ->setKeepComments(true) + ->set('Space*Rules', "\n") + ->set('Space*Blocks', "\n") + ->setSpaceBetweenBlocks("\n\n") + ->set( + 'SpaceAfterListArgumentSeparator', + array( + 'default' => '', + ',' => ' ' + ) + ); + } } diff --git a/lib/Sabberworm/CSS/Renderable.php b/lib/Sabberworm/CSS/Renderable.php index ea4f2fa9..99c829bb 100644 --- a/lib/Sabberworm/CSS/Renderable.php +++ b/lib/Sabberworm/CSS/Renderable.php @@ -6,22 +6,22 @@ interface Renderable { - /** - * @return string - */ + /** + * @return string + */ public function __toString(); - /** - * Renders this component - * - * @param OutputFormat $oOutputFormat Formatting options - * - * @return string Rendered CSS - */ + /** + * Renders this component + * + * @param OutputFormat $oOutputFormat Formatting options + * + * @return string Rendered CSS + */ public function render(OutputFormat $oOutputFormat); - /** - * @return int Line number - */ + /** + * @return int Line number + */ public function getLineNo(); } diff --git a/lib/Sabberworm/CSS/Rule/Rule.php b/lib/Sabberworm/CSS/Rule/Rule.php index afe60c8a..f0b2e82e 100644 --- a/lib/Sabberworm/CSS/Rule/Rule.php +++ b/lib/Sabberworm/CSS/Rule/Rule.php @@ -154,16 +154,16 @@ public function getIsImportant() { return $this->bIsImportant; } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ public function __toString() { return $this->render(new OutputFormat()); } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ public function render(OutputFormat $oOutputFormat) { $sResult = "{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}"; if ($this->mValue instanceof Value) { //Can also be a ValueList diff --git a/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php b/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php index 5a19ac7c..21e54a88 100644 --- a/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php +++ b/lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php @@ -596,37 +596,37 @@ public function __toString() { return $this->render(new OutputFormat()); } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ public function render(OutputFormat $oOutputFormat) { if(count($this->aSelectors) === 0) { // If all the selectors have been removed, this declaration block becomes invalid throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo); } - $sResult = ''; + $sResult = ''; // render comments if ($oOutputFormat->getKeepComments()) { - $comments = $this->getCommentsBefore(); - if (!empty($comments)) { - $sResult .= implode( - '', - array_map( - function (Comment $comment) use ($oOutputFormat) { - return '/*' . $comment->getComment() . "*/\n"; - }, - $comments - ) - ); - } - } + $comments = $this->getCommentsBefore(); + if (!empty($comments)) { + $sResult .= implode( + '', + array_map( + function (Comment $comment) use ($oOutputFormat) { + return '/*' . $comment->getComment() . "*/\n"; + }, + $comments + ) + ); + } + } $sResult .= $oOutputFormat->implode( - $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(), - $this->aSelectors - ) . $oOutputFormat->spaceBeforeOpeningBrace() . '{'; + $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(), + $this->aSelectors + ) . $oOutputFormat->spaceBeforeOpeningBrace() . '{'; $sResult .= parent::render($oOutputFormat); $sResult .= '}'; return $sResult; diff --git a/lib/Sabberworm/CSS/RuleSet/RuleSet.php b/lib/Sabberworm/CSS/RuleSet/RuleSet.php index 88052720..b141c3b2 100644 --- a/lib/Sabberworm/CSS/RuleSet/RuleSet.php +++ b/lib/Sabberworm/CSS/RuleSet/RuleSet.php @@ -48,20 +48,20 @@ public function addRule(Rule $oRule, Rule $oSibling = null) { array_splice($this->aRules[$sRule], $iPosition, 0, array($oRule)); } - /** - * Returns all rules matching the given rule name - * - * @param (null|string|Rule) $mRule pattern to search for. - * If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are - * returned as well as one matching the pattern with the dash excluded. - * Passing a Rule behaves like calling getRules($mRule->getRule()). - * - * @example $oRuleSet->getRules('font-') //returns an array of all rules either beginning with font- or matching - * font. - * @example $oRuleSet->getRules('font') //returns array(0 => $oRule, …) or array(). - * - * @return array - */ + /** + * Returns all rules matching the given rule name + * + * @param (null|string|Rule) $mRule pattern to search for. + * If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are + * returned as well as one matching the pattern with the dash excluded. + * Passing a Rule behaves like calling getRules($mRule->getRule()). + * + * @example $oRuleSet->getRules('font-') //returns an array of all rules either beginning with font- or matching + * font. + * @example $oRuleSet->getRules('font') //returns array(0 => $oRule, …) or array(). + * + * @return array + */ public function getRules($mRule = null) { if ($mRule instanceof Rule) { $mRule = $mRule->getRule(); @@ -87,21 +87,21 @@ public function setRules(array $aRules) { } } - /** - * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name as - * keys. This method exists mainly for backwards-compatibility and is really only partially useful. - * - * @param (string) $mRule pattern to search for. - * If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are - * returned as well as one matching the pattern with the dash excluded. - * Passing a Rule behaves like calling getRules($mRule->getRule()). - * Note: This method loses some information: Calling this (with an argument of 'background-') - * on a declaration block like { background-color: green; background-color; rgba(0, 127, 0, 0.7); } - * will only yield an associative array containing the rgba-valued rule while @link{getRules()} - * would yield an indexed array containing both. - * - * @return array - */ + /** + * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name as + * keys. This method exists mainly for backwards-compatibility and is really only partially useful. + * + * @param (string) $mRule pattern to search for. + * If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are + * returned as well as one matching the pattern with the dash excluded. + * Passing a Rule behaves like calling getRules($mRule->getRule()). + * Note: This method loses some information: Calling this (with an argument of 'background-') + * on a declaration block like { background-color: green; background-color; rgba(0, 127, 0, 0.7); } + * will only yield an associative array containing the rgba-valued rule while @link{getRules()} + * would yield an indexed array containing both. + * + * @return array + */ public function getRulesAssoc($mRule = null) { $aResult = array(); foreach($this->getRules($mRule) as $oRule) { @@ -112,17 +112,17 @@ public function getRulesAssoc($mRule = null) { /** * Remove a rule from this RuleSet. - * - * This accepts all the possible values that @link{getRules()} accepts. - * If given a Rule, it will only remove this particular rule (by identity). - * If given a name, it will remove all rules by that name. - * Note: this is different from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would + * + * This accepts all the possible values that @link{getRules()} accepts. + * If given a Rule, it will only remove this particular rule (by identity). + * If given a name, it will remove all rules by that name. + * Note: this is different from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would * remove all rules with the same name. To get the old behvaiour, use removeRule($oRule->getRule()). - * - * @param (null|string|Rule) $mRule pattern to remove. - * If $mRule is null, all rules are removed. If the pattern ends in a dash, all rules starting with the pattern - * are removed as well as one matching the pattern with the dash excluded. - * Passing a Rule behaves matches by identity. + * + * @param (null|string|Rule) $mRule pattern to remove. + * If $mRule is null, all rules are removed. If the pattern ends in a dash, all rules starting with the pattern + * are removed as well as one matching the pattern with the dash excluded. + * Passing a Rule behaves matches by identity. */ public function removeRule($mRule) { if($mRule instanceof Rule) { @@ -199,21 +199,21 @@ public function setComments(array $aComments) { $this->aComments = $aComments; } - /** - * Returns all comments that were declared before this Rule - * @return Comment[] - */ - public function getCommentsBefore() - { - $lineNo = $this->getLineNo(); - $return = array(); - /** @var Comment $comment */ - foreach ($this->aComments as $comment) { - if ($comment->getLineNo() <= $lineNo) { - $return[] = $comment; - } - } - - return $return; + /** + * Returns all comments that were declared before this Rule + * @return Comment[] + */ + public function getCommentsBefore() + { + $lineNo = $this->getLineNo(); + $return = array(); + /** @var Comment $comment */ + foreach ($this->aComments as $comment) { + if ($comment->getLineNo() <= $lineNo) { + $return[] = $comment; + } + } + + return $return; } } diff --git a/tests/Sabberworm/CSS/OutputFormatTest.php b/tests/Sabberworm/CSS/OutputFormatTest.php index ea87b3dc..267124e9 100644 --- a/tests/Sabberworm/CSS/OutputFormatTest.php +++ b/tests/Sabberworm/CSS/OutputFormatTest.php @@ -6,14 +6,14 @@ class OutputFormatTest extends \PHPUnit_Framework_TestCase { - private $oParser; + private $oParser; - /** - * @var Document - */ - private $oDocument; + /** + * @var Document + */ + private $oDocument; - private static $testCSS = <<oParser = new Parser(self::$testCSS); - $this->oDocument = $this->oParser->parse(); - } + protected function setUp() + { + $this->oParser = new Parser(self::$testCSS); + $this->oDocument = $this->oParser->parse(); + } - public function testPlain() - { - $this->assertSame( - '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} + public function testPlain() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render() - ); - } + $this->oDocument->render() + ); + } - public function testCompact() - { - $this->assertSame( - '.main,.test{font:italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background:white;}@media screen{.main{background-size:100% 100%;font-size:1.3em;background-color:#fff;}}', - $this->oDocument->render(OutputFormat::createCompact()) - ); - } + public function testCompact() + { + $this->assertSame( + '.main,.test{font:italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background:white;}@media screen{.main{background-size:100% 100%;font-size:1.3em;background-color:#fff;}}', + $this->oDocument->render(OutputFormat::createCompact()) + ); + } - public function testCompactWithComments() - { - $expected = <<assertSame( - $expected, - $this->oDocument->render( - OutputFormat::createCompact() - ->setKeepComments(true) - ) - ); - } + $this->assertSame( + $expected, + $this->oDocument->render( + OutputFormat::createCompact() + ->setKeepComments(true) + ) + ); + } - public function testPretty() - { - $this->assertSame( - self::$testCSS, - $this->oDocument->render(OutputFormat::createPretty()) - ); - } + public function testPretty() + { + $this->assertSame( + self::$testCSS, + $this->oDocument->render(OutputFormat::createPretty()) + ); + } - public function testSpaceAfterListArgumentSeparator() - { - $this->assertSame( - '.main, .test {font: italic normal bold 16px/ 1.2 "Helvetica", Verdana, sans-serif;background: white;} + public function testSpaceAfterListArgumentSeparator() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/ 1.2 "Helvetica", Verdana, sans-serif;background: white;} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render(OutputFormat::create()->setSpaceAfterListArgumentSeparator(" ")) - ); - } + $this->oDocument->render(OutputFormat::create()->setSpaceAfterListArgumentSeparator(" ")) + ); + } - public function testSpaceAfterListArgumentSeparatorComplex() - { - $this->assertSame( - '.main, .test {font: italic normal bold 16px/1.2 "Helvetica", Verdana, sans-serif;background: white;} + public function testSpaceAfterListArgumentSeparatorComplex() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica", Verdana, sans-serif;background: white;} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render( - OutputFormat::create()->setSpaceAfterListArgumentSeparator( - array('default' => ' ', ',' => "\t", '/' => '', ' ' => '') - ) - ) - ); - } + $this->oDocument->render( + OutputFormat::create()->setSpaceAfterListArgumentSeparator( + array('default' => ' ', ',' => "\t", '/' => '', ' ' => '') + ) + ) + ); + } - public function testSpaceAfterSelectorSeparator() - { - $this->assertSame( - '.main, + public function testSpaceAfterSelectorSeparator() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render(OutputFormat::create()->setSpaceAfterSelectorSeparator("\n")) - ); - } + $this->oDocument->render(OutputFormat::create()->setSpaceAfterSelectorSeparator("\n")) + ); + } - public function testStringQuotingType() - { - $this->assertSame( - '.main, .test {font: italic normal bold 16px/1.2 \'Helvetica\',Verdana,sans-serif;background: white;} + public function testStringQuotingType() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 \'Helvetica\',Verdana,sans-serif;background: white;} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render(OutputFormat::create()->setStringQuotingType("'")) - ); - } + $this->oDocument->render(OutputFormat::create()->setStringQuotingType("'")) + ); + } - public function testRGBHashNotation() - { - $this->assertSame( - '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} + public function testRGBHashNotation() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: rgb(255,255,255);}}', - $this->oDocument->render(OutputFormat::create()->setRGBHashNotation(false)) - ); - } + $this->oDocument->render(OutputFormat::create()->setRGBHashNotation(false)) + ); + } - public function testSemicolonAfterLastRule() - { - $this->assertSame( - '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white} + public function testSemicolonAfterLastRule() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff}}', - $this->oDocument->render(OutputFormat::create()->setSemicolonAfterLastRule(false)) - ); - } + $this->oDocument->render(OutputFormat::create()->setSemicolonAfterLastRule(false)) + ); + } - public function testSpaceAfterRuleName() - { - $this->assertSame( - '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} + public function testSpaceAfterRuleName() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render(OutputFormat::create()->setSpaceAfterRuleName("\t")) - ); - } + $this->oDocument->render(OutputFormat::create()->setSpaceAfterRuleName("\t")) + ); + } - public function testSpaceRules() - { - $this->assertSame( - '.main, .test { + public function testSpaceRules() + { + $this->assertSame( + '.main, .test { font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif; background: white; } @@ -161,27 +161,27 @@ public function testSpaceRules() font-size: 1.3em; background-color: #fff; }}', - $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n")) - ); - } + $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n")) + ); + } - public function testSpaceBlocks() - { - $this->assertSame( - ' + public function testSpaceBlocks() + { + $this->assertSame( + ' .main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} @media screen { .main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;} } ', - $this->oDocument->render(OutputFormat::create()->set('Space*Blocks', "\n")) - ); - } + $this->oDocument->render(OutputFormat::create()->set('Space*Blocks', "\n")) + ); + } - public function testSpaceBoth() - { - $this->assertSame( - ' + public function testSpaceBoth() + { + $this->assertSame( + ' .main, .test { font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif; background: white; @@ -194,22 +194,22 @@ public function testSpaceBoth() } } ', - $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")) - ); - } + $this->oDocument->render(OutputFormat::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")) + ); + } - public function testSpaceBetweenBlocks() - { - $this->assertSame( - '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;}@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render(OutputFormat::create()->setSpaceBetweenBlocks('')) - ); - } + public function testSpaceBetweenBlocks() + { + $this->assertSame( + '.main, .test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;}@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setSpaceBetweenBlocks('')) + ); + } - public function testIndentation() - { - $this->assertSame( - ' + public function testIndentation() + { + $this->assertSame( + ' .main, .test { font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif; background: white; @@ -222,48 +222,48 @@ public function testIndentation() } } ', - $this->oDocument->render( - OutputFormat::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setIndentation('') - ) - ); - } + $this->oDocument->render( + OutputFormat::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setIndentation('') + ) + ); + } - public function testSpaceBeforeBraces() - { - $this->assertSame( - '.main, .test{font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} + public function testSpaceBeforeBraces() + { + $this->assertSame( + '.main, .test{font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} @media screen{.main{background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render(OutputFormat::create()->setSpaceBeforeOpeningBrace('')) - ); - } + $this->oDocument->render(OutputFormat::create()->setSpaceBeforeOpeningBrace('')) + ); + } - /** - * @expectedException Sabberworm\CSS\Parsing\OutputException - */ - public function testIgnoreExceptionsOff() - { - $aBlocks = $this->oDocument->getAllDeclarationBlocks(); - $oFirstBlock = $aBlocks[0]; - $oFirstBlock->removeSelector('.main'); - $this->assertSame( - '.test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} + /** + * @expectedException Sabberworm\CSS\Parsing\OutputException + */ + public function testIgnoreExceptionsOff() + { + $aBlocks = $this->oDocument->getAllDeclarationBlocks(); + $oFirstBlock = $aBlocks[0]; + $oFirstBlock->removeSelector('.main'); + $this->assertSame( + '.test {font: italic normal bold 16px/1.2 "Helvetica",Verdana,sans-serif;background: white;} @media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(false)) - ); - $oFirstBlock->removeSelector('.test'); - $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(false)); - } + $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(false)) + ); + $oFirstBlock->removeSelector('.test'); + $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(false)); + } - public function testIgnoreExceptionsOn() - { - $aBlocks = $this->oDocument->getAllDeclarationBlocks(); - $oFirstBlock = $aBlocks[0]; - $oFirstBlock->removeSelector('.main'); - $oFirstBlock->removeSelector('.test'); - $this->assertSame( - '@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', - $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(true)) - ); - } + public function testIgnoreExceptionsOn() + { + $aBlocks = $this->oDocument->getAllDeclarationBlocks(); + $oFirstBlock = $aBlocks[0]; + $oFirstBlock->removeSelector('.main'); + $oFirstBlock->removeSelector('.test'); + $this->assertSame( + '@media screen {.main {background-size: 100% 100%;font-size: 1.3em;background-color: #fff;}}', + $this->oDocument->render(OutputFormat::create()->setIgnoreExceptions(true)) + ); + } }