Файловый менеджер - Редактировать - /home/harasnat/www/mf/RuleSet.tar
Назад
AtRuleSet.php 0000604 00000002620 15062134261 0007121 0 ustar 00 <?php namespace Sabberworm\CSS\RuleSet; use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Property\AtRule; /** * A RuleSet constructed by an unknown at-rule. `@font-face` rules are rendered into AtRuleSet objects. */ class AtRuleSet extends RuleSet implements AtRule { /** * @var string */ private $sType; /** * @var string */ private $sArgs; /** * @param string $sType * @param string $sArgs * @param int $iLineNo */ public function __construct($sType, $sArgs = '', $iLineNo = 0) { parent::__construct($iLineNo); $this->sType = $sType; $this->sArgs = $sArgs; } /** * @return string */ public function atRuleName() { return $this->sType; } /** * @return string */ public function atRuleArgs() { return $this->sArgs; } /** * @return string */ public function __toString() { return $this->render(new OutputFormat()); } /** * @return string */ public function render(OutputFormat $oOutputFormat) { $sArgs = $this->sArgs; if ($sArgs) { $sArgs = ' ' . $sArgs; } $sResult = "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{"; $sResult .= parent::render($oOutputFormat); $sResult .= '}'; return $sResult; } } DeclarationBlock.php 0000604 00000071606 15062134261 0010463 0 ustar 00 <?php namespace Sabberworm\CSS\RuleSet; use Sabberworm\CSS\CSSList\CSSList; use Sabberworm\CSS\CSSList\KeyFrame; use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Parsing\OutputException; use Sabberworm\CSS\Parsing\ParserState; use Sabberworm\CSS\Parsing\UnexpectedEOFException; use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\Property\KeyframeSelector; use Sabberworm\CSS\Property\Selector; use Sabberworm\CSS\Rule\Rule; use Sabberworm\CSS\Value\Color; use Sabberworm\CSS\Value\RuleValueList; use Sabberworm\CSS\Value\Size; use Sabberworm\CSS\Value\URL; use Sabberworm\CSS\Value\Value; /** * Declaration blocks are the parts of a CSS file which denote the rules belonging to a selector. * * Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`). */ class DeclarationBlock extends RuleSet { /** * @var array<int, Selector|string> */ private $aSelectors; /** * @param int $iLineNo */ public function __construct($iLineNo = 0) { parent::__construct($iLineNo); $this->aSelectors = []; } /** * @param CSSList|null $oList * * @return DeclarationBlock|false * * @throws UnexpectedTokenException * @throws UnexpectedEOFException */ public static function parse(ParserState $oParserState, $oList = null) { $aComments = []; $oResult = new DeclarationBlock($oParserState->currentLine()); try { $aSelectorParts = []; $sStringWrapperChar = false; do { $aSelectorParts[] = $oParserState->consume(1) . $oParserState->consumeUntil(['{', '}', '\'', '"'], false, false, $aComments); if (in_array($oParserState->peek(), ['\'', '"']) && substr(end($aSelectorParts), -1) != "\\") { if ($sStringWrapperChar === false) { $sStringWrapperChar = $oParserState->peek(); } elseif ($sStringWrapperChar == $oParserState->peek()) { $sStringWrapperChar = false; } } } while (!in_array($oParserState->peek(), ['{', '}']) || $sStringWrapperChar !== false); $oResult->setSelectors(implode('', $aSelectorParts), $oList); if ($oParserState->comes('{')) { $oParserState->consume(1); } } catch (UnexpectedTokenException $e) { if ($oParserState->getSettings()->bLenientParsing) { if (!$oParserState->comes('}')) { $oParserState->consumeUntil('}', false, true); } return false; } else { throw $e; } } $oResult->setComments($aComments); RuleSet::parseRuleSet($oParserState, $oResult); return $oResult; } /** * @param array<int, Selector|string>|string $mSelector * @param CSSList|null $oList * * @throws UnexpectedTokenException */ public function setSelectors($mSelector, $oList = null) { if (is_array($mSelector)) { $this->aSelectors = $mSelector; } else { $this->aSelectors = explode(',', $mSelector); } foreach ($this->aSelectors as $iKey => $mSelector) { if (!($mSelector instanceof Selector)) { if ($oList === null || !($oList instanceof KeyFrame)) { if (!Selector::isValid($mSelector)) { throw new UnexpectedTokenException( "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.", $mSelector, "custom" ); } $this->aSelectors[$iKey] = new Selector($mSelector); } else { if (!KeyframeSelector::isValid($mSelector)) { throw new UnexpectedTokenException( "Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.", $mSelector, "custom" ); } $this->aSelectors[$iKey] = new KeyframeSelector($mSelector); } } } } /** * Remove one of the selectors of the block. * * @param Selector|string $mSelector * * @return bool */ public function removeSelector($mSelector) { if ($mSelector instanceof Selector) { $mSelector = $mSelector->getSelector(); } foreach ($this->aSelectors as $iKey => $oSelector) { if ($oSelector->getSelector() === $mSelector) { unset($this->aSelectors[$iKey]); return true; } } return false; } /** * @return array<int, Selector|string> * * @deprecated will be removed in version 9.0; use `getSelectors()` instead */ public function getSelector() { return $this->getSelectors(); } /** * @param Selector|string $mSelector * @param CSSList|null $oList * * @return void * * @deprecated will be removed in version 9.0; use `setSelectors()` instead */ public function setSelector($mSelector, $oList = null) { $this->setSelectors($mSelector, $oList); } /** * @return array<int, Selector|string> */ public function getSelectors() { return $this->aSelectors; } /** * Splits shorthand declarations (e.g. `margin` or `font`) into their constituent parts. * * @return void */ public function expandShorthands() { // border must be expanded before dimensions $this->expandBorderShorthand(); $this->expandDimensionsShorthand(); $this->expandFontShorthand(); $this->expandBackgroundShorthand(); $this->expandListStyleShorthand(); } /** * Creates shorthand declarations (e.g. `margin` or `font`) whenever possible. * * @return void */ public function createShorthands() { $this->createBackgroundShorthand(); $this->createDimensionsShorthand(); // border must be shortened after dimensions $this->createBorderShorthand(); $this->createFontShorthand(); $this->createListStyleShorthand(); } /** * Splits shorthand border declarations (e.g. `border: 1px red;`). * * Additional splitting happens in expandDimensionsShorthand. * * Multiple borders are not yet supported as of 3. * * @return void */ public function expandBorderShorthand() { $aBorderRules = [ 'border', 'border-left', 'border-right', 'border-top', 'border-bottom', ]; $aBorderSizes = [ 'thin', 'medium', 'thick', ]; $aRules = $this->getRulesAssoc(); foreach ($aBorderRules as $sBorderRule) { if (!isset($aRules[$sBorderRule])) { continue; } $oRule = $aRules[$sBorderRule]; $mRuleValue = $oRule->getValue(); $aValues = []; if (!$mRuleValue instanceof RuleValueList) { $aValues[] = $mRuleValue; } else { $aValues = $mRuleValue->getListComponents(); } foreach ($aValues as $mValue) { if ($mValue instanceof Value) { $mNewValue = clone $mValue; } else { $mNewValue = $mValue; } if ($mValue instanceof Size) { $sNewRuleName = $sBorderRule . "-width"; } elseif ($mValue instanceof Color) { $sNewRuleName = $sBorderRule . "-color"; } else { if (in_array($mValue, $aBorderSizes)) { $sNewRuleName = $sBorderRule . "-width"; } else { $sNewRuleName = $sBorderRule . "-style"; } } $oNewRule = new Rule($sNewRuleName, $oRule->getLineNo(), $oRule->getColNo()); $oNewRule->setIsImportant($oRule->getIsImportant()); $oNewRule->addValue([$mNewValue]); $this->addRule($oNewRule); } $this->removeRule($sBorderRule); } } /** * Splits shorthand dimensional declarations (e.g. `margin: 0px auto;`) * into their constituent parts. * * Handles `margin`, `padding`, `border-color`, `border-style` and `border-width`. * * @return void */ public function expandDimensionsShorthand() { $aExpansions = [ 'margin' => 'margin-%s', 'padding' => 'padding-%s', 'border-color' => 'border-%s-color', 'border-style' => 'border-%s-style', 'border-width' => 'border-%s-width', ]; $aRules = $this->getRulesAssoc(); foreach ($aExpansions as $sProperty => $sExpanded) { if (!isset($aRules[$sProperty])) { continue; } $oRule = $aRules[$sProperty]; $mRuleValue = $oRule->getValue(); $aValues = []; if (!$mRuleValue instanceof RuleValueList) { $aValues[] = $mRuleValue; } else { $aValues = $mRuleValue->getListComponents(); } $top = $right = $bottom = $left = null; switch (count($aValues)) { case 1: $top = $right = $bottom = $left = $aValues[0]; break; case 2: $top = $bottom = $aValues[0]; $left = $right = $aValues[1]; break; case 3: $top = $aValues[0]; $left = $right = $aValues[1]; $bottom = $aValues[2]; break; case 4: $top = $aValues[0]; $right = $aValues[1]; $bottom = $aValues[2]; $left = $aValues[3]; break; } foreach (['top', 'right', 'bottom', 'left'] as $sPosition) { $oNewRule = new Rule(sprintf($sExpanded, $sPosition), $oRule->getLineNo(), $oRule->getColNo()); $oNewRule->setIsImportant($oRule->getIsImportant()); $oNewRule->addValue(${$sPosition}); $this->addRule($oNewRule); } $this->removeRule($sProperty); } } /** * Converts shorthand font declarations * (e.g. `font: 300 italic 11px/14px verdana, helvetica, sans-serif;`) * into their constituent parts. * * @return void */ public function expandFontShorthand() { $aRules = $this->getRulesAssoc(); if (!isset($aRules['font'])) { return; } $oRule = $aRules['font']; // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand $aFontProperties = [ 'font-style' => 'normal', 'font-variant' => 'normal', 'font-weight' => 'normal', 'font-size' => 'normal', 'line-height' => 'normal', ]; $mRuleValue = $oRule->getValue(); $aValues = []; if (!$mRuleValue instanceof RuleValueList) { $aValues[] = $mRuleValue; } else { $aValues = $mRuleValue->getListComponents(); } foreach ($aValues as $mValue) { if (!$mValue instanceof Value) { $mValue = mb_strtolower($mValue); } if (in_array($mValue, ['normal', 'inherit'])) { foreach (['font-style', 'font-weight', 'font-variant'] as $sProperty) { if (!isset($aFontProperties[$sProperty])) { $aFontProperties[$sProperty] = $mValue; } } } elseif (in_array($mValue, ['italic', 'oblique'])) { $aFontProperties['font-style'] = $mValue; } elseif ($mValue == 'small-caps') { $aFontProperties['font-variant'] = $mValue; } elseif ( in_array($mValue, ['bold', 'bolder', 'lighter']) || ($mValue instanceof Size && in_array($mValue->getSize(), range(100, 900, 100))) ) { $aFontProperties['font-weight'] = $mValue; } elseif ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') { list($oSize, $oHeight) = $mValue->getListComponents(); $aFontProperties['font-size'] = $oSize; $aFontProperties['line-height'] = $oHeight; } elseif ($mValue instanceof Size && $mValue->getUnit() !== null) { $aFontProperties['font-size'] = $mValue; } else { $aFontProperties['font-family'] = $mValue; } } foreach ($aFontProperties as $sProperty => $mValue) { $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); $oNewRule->addValue($mValue); $oNewRule->setIsImportant($oRule->getIsImportant()); $this->addRule($oNewRule); } $this->removeRule('font'); } /** * Converts shorthand background declarations * (e.g. `background: url("chess.png") gray 50% repeat fixed;`) * into their constituent parts. * * @see http://www.w3.org/TR/21/colors.html#propdef-background * * @return void */ public function expandBackgroundShorthand() { $aRules = $this->getRulesAssoc(); if (!isset($aRules['background'])) { return; } $oRule = $aRules['background']; $aBgProperties = [ 'background-color' => ['transparent'], 'background-image' => ['none'], 'background-repeat' => ['repeat'], 'background-attachment' => ['scroll'], 'background-position' => [ new Size(0, '%', null, false, $this->iLineNo), new Size(0, '%', null, false, $this->iLineNo), ], ]; $mRuleValue = $oRule->getValue(); $aValues = []; if (!$mRuleValue instanceof RuleValueList) { $aValues[] = $mRuleValue; } else { $aValues = $mRuleValue->getListComponents(); } if (count($aValues) == 1 && $aValues[0] == 'inherit') { foreach ($aBgProperties as $sProperty => $mValue) { $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); $oNewRule->addValue('inherit'); $oNewRule->setIsImportant($oRule->getIsImportant()); $this->addRule($oNewRule); } $this->removeRule('background'); return; } $iNumBgPos = 0; foreach ($aValues as $mValue) { if (!$mValue instanceof Value) { $mValue = mb_strtolower($mValue); } if ($mValue instanceof URL) { $aBgProperties['background-image'] = $mValue; } elseif ($mValue instanceof Color) { $aBgProperties['background-color'] = $mValue; } elseif (in_array($mValue, ['scroll', 'fixed'])) { $aBgProperties['background-attachment'] = $mValue; } elseif (in_array($mValue, ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'])) { $aBgProperties['background-repeat'] = $mValue; } elseif ( in_array($mValue, ['left', 'center', 'right', 'top', 'bottom']) || $mValue instanceof Size ) { if ($iNumBgPos == 0) { $aBgProperties['background-position'][0] = $mValue; $aBgProperties['background-position'][1] = 'center'; } else { $aBgProperties['background-position'][$iNumBgPos] = $mValue; } $iNumBgPos++; } } foreach ($aBgProperties as $sProperty => $mValue) { $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); $oNewRule->setIsImportant($oRule->getIsImportant()); $oNewRule->addValue($mValue); $this->addRule($oNewRule); } $this->removeRule('background'); } /** * @return void */ public function expandListStyleShorthand() { $aListProperties = [ 'list-style-type' => 'disc', 'list-style-position' => 'outside', 'list-style-image' => 'none', ]; $aListStyleTypes = [ 'none', 'disc', 'circle', 'square', 'decimal-leading-zero', 'decimal', 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha', 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'armenian', 'georgian', 'cjk-ideographic', 'hiragana', 'hira-gana-iroha', 'katakana-iroha', 'katakana', ]; $aListStylePositions = [ 'inside', 'outside', ]; $aRules = $this->getRulesAssoc(); if (!isset($aRules['list-style'])) { return; } $oRule = $aRules['list-style']; $mRuleValue = $oRule->getValue(); $aValues = []; if (!$mRuleValue instanceof RuleValueList) { $aValues[] = $mRuleValue; } else { $aValues = $mRuleValue->getListComponents(); } if (count($aValues) == 1 && $aValues[0] == 'inherit') { foreach ($aListProperties as $sProperty => $mValue) { $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); $oNewRule->addValue('inherit'); $oNewRule->setIsImportant($oRule->getIsImportant()); $this->addRule($oNewRule); } $this->removeRule('list-style'); return; } foreach ($aValues as $mValue) { if (!$mValue instanceof Value) { $mValue = mb_strtolower($mValue); } if ($mValue instanceof Url) { $aListProperties['list-style-image'] = $mValue; } elseif (in_array($mValue, $aListStyleTypes)) { $aListProperties['list-style-types'] = $mValue; } elseif (in_array($mValue, $aListStylePositions)) { $aListProperties['list-style-position'] = $mValue; } } foreach ($aListProperties as $sProperty => $mValue) { $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); $oNewRule->setIsImportant($oRule->getIsImportant()); $oNewRule->addValue($mValue); $this->addRule($oNewRule); } $this->removeRule('list-style'); } /** * @param array<array-key, string> $aProperties * @param string $sShorthand * * @return void */ public function createShorthandProperties(array $aProperties, $sShorthand) { $aRules = $this->getRulesAssoc(); $aNewValues = []; foreach ($aProperties as $sProperty) { if (!isset($aRules[$sProperty])) { continue; } $oRule = $aRules[$sProperty]; if (!$oRule->getIsImportant()) { $mRuleValue = $oRule->getValue(); $aValues = []; if (!$mRuleValue instanceof RuleValueList) { $aValues[] = $mRuleValue; } else { $aValues = $mRuleValue->getListComponents(); } foreach ($aValues as $mValue) { $aNewValues[] = $mValue; } $this->removeRule($sProperty); } } if (count($aNewValues)) { $oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo()); foreach ($aNewValues as $mValue) { $oNewRule->addValue($mValue); } $this->addRule($oNewRule); } } /** * @return void */ public function createBackgroundShorthand() { $aProperties = [ 'background-color', 'background-image', 'background-repeat', 'background-position', 'background-attachment', ]; $this->createShorthandProperties($aProperties, 'background'); } /** * @return void */ public function createListStyleShorthand() { $aProperties = [ 'list-style-type', 'list-style-position', 'list-style-image', ]; $this->createShorthandProperties($aProperties, 'list-style'); } /** * Combines `border-color`, `border-style` and `border-width` into `border`. * * Should be run after `create_dimensions_shorthand`! * * @return void */ public function createBorderShorthand() { $aProperties = [ 'border-width', 'border-style', 'border-color', ]; $this->createShorthandProperties($aProperties, 'border'); } /** * Looks for long format CSS dimensional properties * (margin, padding, border-color, border-style and border-width) * and converts them into shorthand CSS properties. * * @return void */ public function createDimensionsShorthand() { $aPositions = ['top', 'right', 'bottom', 'left']; $aExpansions = [ 'margin' => 'margin-%s', 'padding' => 'padding-%s', 'border-color' => 'border-%s-color', 'border-style' => 'border-%s-style', 'border-width' => 'border-%s-width', ]; $aRules = $this->getRulesAssoc(); foreach ($aExpansions as $sProperty => $sExpanded) { $aFoldable = []; foreach ($aRules as $sRuleName => $oRule) { foreach ($aPositions as $sPosition) { if ($sRuleName == sprintf($sExpanded, $sPosition)) { $aFoldable[$sRuleName] = $oRule; } } } // All four dimensions must be present if (count($aFoldable) == 4) { $aValues = []; foreach ($aPositions as $sPosition) { $oRule = $aRules[sprintf($sExpanded, $sPosition)]; $mRuleValue = $oRule->getValue(); $aRuleValues = []; if (!$mRuleValue instanceof RuleValueList) { $aRuleValues[] = $mRuleValue; } else { $aRuleValues = $mRuleValue->getListComponents(); } $aValues[$sPosition] = $aRuleValues; } $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); if ((string)$aValues['left'][0] == (string)$aValues['right'][0]) { if ((string)$aValues['top'][0] == (string)$aValues['bottom'][0]) { if ((string)$aValues['top'][0] == (string)$aValues['left'][0]) { // All 4 sides are equal $oNewRule->addValue($aValues['top']); } else { // Top and bottom are equal, left and right are equal $oNewRule->addValue($aValues['top']); $oNewRule->addValue($aValues['left']); } } else { // Only left and right are equal $oNewRule->addValue($aValues['top']); $oNewRule->addValue($aValues['left']); $oNewRule->addValue($aValues['bottom']); } } else { // No sides are equal $oNewRule->addValue($aValues['top']); $oNewRule->addValue($aValues['left']); $oNewRule->addValue($aValues['bottom']); $oNewRule->addValue($aValues['right']); } $this->addRule($oNewRule); foreach ($aPositions as $sPosition) { $this->removeRule(sprintf($sExpanded, $sPosition)); } } } } /** * 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. * * @return void */ public function createFontShorthand() { $aFontProperties = [ 'font-style', 'font-variant', 'font-weight', 'font-size', 'line-height', 'font-family', ]; $aRules = $this->getRulesAssoc(); if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) { return; } $oOldRule = isset($aRules['font-size']) ? $aRules['font-size'] : $aRules['font-family']; $oNewRule = new Rule('font', $oOldRule->getLineNo(), $oOldRule->getColNo()); unset($oOldRule); foreach (['font-style', 'font-variant', 'font-weight'] as $sProperty) { if (isset($aRules[$sProperty])) { $oRule = $aRules[$sProperty]; $mRuleValue = $oRule->getValue(); $aValues = []; if (!$mRuleValue instanceof RuleValueList) { $aValues[] = $mRuleValue; } else { $aValues = $mRuleValue->getListComponents(); } if ($aValues[0] !== 'normal') { $oNewRule->addValue($aValues[0]); } } } // Get the font-size value $oRule = $aRules['font-size']; $mRuleValue = $oRule->getValue(); $aFSValues = []; if (!$mRuleValue instanceof RuleValueList) { $aFSValues[] = $mRuleValue; } else { $aFSValues = $mRuleValue->getListComponents(); } // But wait to know if we have line-height to add it if (isset($aRules['line-height'])) { $oRule = $aRules['line-height']; $mRuleValue = $oRule->getValue(); $aLHValues = []; if (!$mRuleValue instanceof RuleValueList) { $aLHValues[] = $mRuleValue; } else { $aLHValues = $mRuleValue->getListComponents(); } if ($aLHValues[0] !== 'normal') { $val = new RuleValueList('/', $this->iLineNo); $val->addListComponent($aFSValues[0]); $val->addListComponent($aLHValues[0]); $oNewRule->addValue($val); } } else { $oNewRule->addValue($aFSValues[0]); } $oRule = $aRules['font-family']; $mRuleValue = $oRule->getValue(); $aFFValues = []; if (!$mRuleValue instanceof RuleValueList) { $aFFValues[] = $mRuleValue; } else { $aFFValues = $mRuleValue->getListComponents(); } $oFFValue = new RuleValueList(',', $this->iLineNo); $oFFValue->setListComponents($aFFValues); $oNewRule->addValue($oFFValue); $this->addRule($oNewRule); foreach ($aFontProperties as $sProperty) { $this->removeRule($sProperty); } } /** * @return string * * @throws OutputException */ public function __toString() { return $this->render(new OutputFormat()); } /** * @return string * * @throws OutputException */ 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 = $oOutputFormat->sBeforeDeclarationBlock; $sResult .= $oOutputFormat->implode( $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(), $this->aSelectors ); $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors; $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{'; $sResult .= parent::render($oOutputFormat); $sResult .= '}'; $sResult .= $oOutputFormat->sAfterDeclarationBlock; return $sResult; } } RuleSet.php 0000604 00000025324 15062134261 0006642 0 ustar 00 <?php namespace Sabberworm\CSS\RuleSet; use Sabberworm\CSS\Comment\Comment; use Sabberworm\CSS\Comment\Commentable; use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Parsing\ParserState; use Sabberworm\CSS\Parsing\UnexpectedEOFException; use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\Renderable; use Sabberworm\CSS\Rule\Rule; /** * RuleSet is a generic superclass denoting rules. The typical example for rule sets are declaration block. * However, unknown At-Rules (like `@font-face`) are also rule sets. */ abstract class RuleSet implements Renderable, Commentable { /** * @var array<string, Rule> */ private $aRules; /** * @var int */ protected $iLineNo; /** * @var array<array-key, Comment> */ protected $aComments; /** * @param int $iLineNo */ public function __construct($iLineNo = 0) { $this->aRules = []; $this->iLineNo = $iLineNo; $this->aComments = []; } /** * @return void * * @throws UnexpectedTokenException * @throws UnexpectedEOFException */ public static function parseRuleSet(ParserState $oParserState, RuleSet $oRuleSet) { while ($oParserState->comes(';')) { $oParserState->consume(';'); } while (!$oParserState->comes('}')) { $oRule = null; if ($oParserState->getSettings()->bLenientParsing) { try { $oRule = Rule::parse($oParserState); } catch (UnexpectedTokenException $e) { try { $sConsume = $oParserState->consumeUntil(["\n", ";", '}'], true); // We need to “unfind” the matches to the end of the ruleSet as this will be matched later if ($oParserState->streql(substr($sConsume, -1), '}')) { $oParserState->backtrack(1); } else { while ($oParserState->comes(';')) { $oParserState->consume(';'); } } } catch (UnexpectedTokenException $e) { // We’ve reached the end of the document. Just close the RuleSet. return; } } } else { $oRule = Rule::parse($oParserState); } if ($oRule) { $oRuleSet->addRule($oRule); } } $oParserState->consume('}'); } /** * @return int */ public function getLineNo() { return $this->iLineNo; } /** * @param Rule|null $oSibling * * @return void */ public function addRule(Rule $oRule, Rule $oSibling = null) { $sRule = $oRule->getRule(); if (!isset($this->aRules[$sRule])) { $this->aRules[$sRule] = []; } $iPosition = count($this->aRules[$sRule]); if ($oSibling !== null) { $iSiblingPos = array_search($oSibling, $this->aRules[$sRule], true); if ($iSiblingPos !== false) { $iPosition = $iSiblingPos; $oRule->setPosition($oSibling->getLineNo(), $oSibling->getColNo() - 1); } } if ($oRule->getLineNo() === 0 && $oRule->getColNo() === 0) { //this node is added manually, give it the next best line $rules = $this->getRules(); $pos = count($rules); if ($pos > 0) { $last = $rules[$pos - 1]; $oRule->setPosition($last->getLineNo() + 1, 0); } } array_splice($this->aRules[$sRule], $iPosition, 0, [$oRule]); } /** * Returns all rules matching the given rule name * * @example $oRuleSet->getRules('font') // returns array(0 => $oRule, …) or array(). * * @example $oRuleSet->getRules('font-') * //returns an array of all rules either beginning with font- or matching font. * * @param Rule|string|null $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())`. * * @return array<int, Rule> */ public function getRules($mRule = null) { if ($mRule instanceof Rule) { $mRule = $mRule->getRule(); } /** @var array<int, Rule> $aResult */ $aResult = []; foreach ($this->aRules as $sName => $aRules) { // Either no search rule is given or the search rule matches the found rule exactly // or the search rule ends in “-” and the found rule starts with the search rule. if ( !$mRule || $sName === $mRule || ( strrpos($mRule, '-') === strlen($mRule) - strlen('-') && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1)) ) ) { $aResult = array_merge($aResult, $aRules); } } usort($aResult, function (Rule $first, Rule $second) { if ($first->getLineNo() === $second->getLineNo()) { return $first->getColNo() - $second->getColNo(); } return $first->getLineNo() - $second->getLineNo(); }); return $aResult; } /** * Overrides all the rules of this set. * * @param array<array-key, Rule> $aRules The rules to override with. * * @return void */ public function setRules(array $aRules) { $this->aRules = []; foreach ($aRules as $rule) { $this->addRule($rule); } } /** * 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. * * 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 `getRules()` would yield an indexed array containing both. * * @param Rule|string|null $mRule $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())`. * * @return array<string, Rule> */ public function getRulesAssoc($mRule = null) { /** @var array<string, Rule> $aResult */ $aResult = []; foreach ($this->getRules($mRule) as $oRule) { $aResult[$oRule->getRule()] = $oRule; } return $aResult; } /** * Removes a rule from this RuleSet. This accepts all the possible values that `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 behaviour, use `removeRule($oRule->getRule())`. * * @param Rule|string|null $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. * * @return void */ public function removeRule($mRule) { if ($mRule instanceof Rule) { $sRule = $mRule->getRule(); if (!isset($this->aRules[$sRule])) { return; } foreach ($this->aRules[$sRule] as $iKey => $oRule) { if ($oRule === $mRule) { unset($this->aRules[$sRule][$iKey]); } } } else { foreach ($this->aRules as $sName => $aRules) { // Either no search rule is given or the search rule matches the found rule exactly // or the search rule ends in “-” and the found rule starts with the search rule or equals it // (without the trailing dash). if ( !$mRule || $sName === $mRule || (strrpos($mRule, '-') === strlen($mRule) - strlen('-') && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1))) ) { unset($this->aRules[$sName]); } } } } /** * @return string */ public function __toString() { return $this->render(new OutputFormat()); } /** * @return string */ public function render(OutputFormat $oOutputFormat) { $sResult = ''; $bIsFirst = true; foreach ($this->aRules as $aRules) { foreach ($aRules as $oRule) { $sRendered = $oOutputFormat->safely(function () use ($oRule, $oOutputFormat) { return $oRule->render($oOutputFormat->nextLevel()); }); if ($sRendered === null) { continue; } if ($bIsFirst) { $bIsFirst = false; $sResult .= $oOutputFormat->nextLevel()->spaceBeforeRules(); } else { $sResult .= $oOutputFormat->nextLevel()->spaceBetweenRules(); } $sResult .= $sRendered; } } if (!$bIsFirst) { // Had some output $sResult .= $oOutputFormat->spaceAfterRules(); } return $oOutputFormat->removeLastSemicolon($sResult); } /** * @param array<string, Comment> $aComments * * @return void */ public function addComments(array $aComments) { $this->aComments = array_merge($this->aComments, $aComments); } /** * @return array<string, Comment> */ public function getComments() { return $this->aComments; } /** * @param array<string, Comment> $aComments * * @return void */ public function setComments(array $aComments) { $this->aComments = $aComments; } }
| ver. 1.4 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка