diff --git a/CHANGELOG.md b/CHANGELOG.md index 891adc8af..b08c38a28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Added +- Add Interface `CSSElement` (#1231) - Methods `getLineNumber` and `getColumnNumber` which return a nullable `int` for the following classes: `Comment`, `CSSList`, `SourceException`, `Charset`, `CSSNamespace`, `Import`, @@ -16,12 +17,20 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Changed +- Parameters for `getAllValues()` are deconflated, so it now takes three (all + optional), allowing `$element` and `$ruleSearchPattern` to be specified + separately (#1241) - Implement `Positionable` in the following CSS item classes: `Comment`, `CSSList`, `SourceException`, `Charset`, `CSSNamespace`, `Import`, `Rule`, `DeclarationBlock`, `RuleSet`, `CSSFunction`, `Value` (#1225) ### Deprecated +- Passing a string as the first argument to `getAllValues()` is deprecated; + the search pattern should now be passed as the second argument (#1241) +- Passing a Boolean as the second argument to `getAllValues()` is deprecated; + the flag for searching in function arguments should now be passed as the third + argument (#1241) - `getLineNo()` is deprecated in these classes (use `getLineNumber()` instead): `Comment`, `CSSList`, `SourceException`, `Charset`, `CSSNamespace`, `Import`, `Rule`, `DeclarationBlock`, `RuleSet`, `CSSFunction`, `Value` (#1225, #1233) diff --git a/src/CSSElement.php b/src/CSSElement.php new file mode 100644 index 000000000..944aabe2c --- /dev/null +++ b/src/CSSElement.php @@ -0,0 +1,17 @@ + + * + * @see RuleSet->getRules() + */ + public function getAllValues( + $element = null, + $ruleSearchPatternOrSearchInFunctionArguments = null, + $searchInFunctionArguments = false + ) { + if (\is_bool($ruleSearchPatternOrSearchInFunctionArguments)) { + $searchInFunctionArguments = $ruleSearchPatternOrSearchInFunctionArguments; + $searchString = null; + } else { + $searchString = $ruleSearchPatternOrSearchInFunctionArguments; + } + + if ($element === null) { + $element = $this; + } elseif (\is_string($element)) { + $searchString = $element; + $element = $this; + } + + $result = []; + $this->allValues($element, $result, $searchString, $searchInFunctionArguments); + return $result; + } + + /** + * @param CSSElement|string $oElement * @param array $aResult * @param string|null $sSearchString * @param bool $bSearchInFunctionArguments diff --git a/src/CSSList/CSSList.php b/src/CSSList/CSSList.php index 2156aa111..18d926e15 100644 --- a/src/CSSList/CSSList.php +++ b/src/CSSList/CSSList.php @@ -4,6 +4,7 @@ use Sabberworm\CSS\Comment\Comment; use Sabberworm\CSS\Comment\Commentable; +use Sabberworm\CSS\CSSElement; use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Parsing\ParserState; use Sabberworm\CSS\Parsing\SourceException; @@ -31,7 +32,7 @@ * * It can also contain `Import` and `Charset` objects stemming from at-rules. */ -abstract class CSSList implements Commentable, Positionable, Renderable +abstract class CSSList implements Commentable, CSSElement, Positionable { use Position; diff --git a/src/CSSList/Document.php b/src/CSSList/Document.php index f74c89645..ed4b09b38 100644 --- a/src/CSSList/Document.php +++ b/src/CSSList/Document.php @@ -8,7 +8,6 @@ use Sabberworm\CSS\Property\Selector; use Sabberworm\CSS\RuleSet\DeclarationBlock; use Sabberworm\CSS\RuleSet\RuleSet; -use Sabberworm\CSS\Value\Value; /** * This class represents the root of a parsed CSS file. It contains all top-level CSS contents: mostly declaration @@ -77,33 +76,6 @@ public function getAllRuleSets() return $aResult; } - /** - * Returns all `Value` objects found recursively in `Rule`s in the tree. - * - * @param CSSList|RuleSet|string $mElement - * the `CSSList` or `RuleSet` to start the search from (defaults to the whole document). - * If a string is given, it is used as rule name filter. - * @param bool $bSearchInFunctionArguments whether to also return Value objects used as Function arguments. - * - * @return array - * - * @see RuleSet->getRules() - */ - public function getAllValues($mElement = null, $bSearchInFunctionArguments = false) - { - $sSearchString = null; - if ($mElement === null) { - $mElement = $this; - } elseif (is_string($mElement)) { - $sSearchString = $mElement; - $mElement = $this; - } - /** @var array $aResult */ - $aResult = []; - $this->allValues($mElement, $aResult, $sSearchString, $bSearchInFunctionArguments); - return $aResult; - } - /** * Returns all `Selector` objects with the requested specificity found recursively in the tree. * diff --git a/src/Rule/Rule.php b/src/Rule/Rule.php index e358d93f4..a34018a84 100644 --- a/src/Rule/Rule.php +++ b/src/Rule/Rule.php @@ -4,13 +4,13 @@ use Sabberworm\CSS\Comment\Comment; use Sabberworm\CSS\Comment\Commentable; +use Sabberworm\CSS\CSSElement; use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Parsing\ParserState; use Sabberworm\CSS\Parsing\UnexpectedEOFException; use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\Position\Position; use Sabberworm\CSS\Position\Positionable; -use Sabberworm\CSS\Renderable; use Sabberworm\CSS\Value\RuleValueList; use Sabberworm\CSS\Value\Value; @@ -19,7 +19,7 @@ * * In CSS, `Rule`s are expressed as follows: “key: value[0][0] value[0][1], value[1][0] value[1][1];” */ -class Rule implements Commentable, Positionable, Renderable +class Rule implements Commentable, CSSElement, Positionable { use Position; diff --git a/src/RuleSet/RuleSet.php b/src/RuleSet/RuleSet.php index 85b94f263..12b8157ac 100644 --- a/src/RuleSet/RuleSet.php +++ b/src/RuleSet/RuleSet.php @@ -4,6 +4,7 @@ use Sabberworm\CSS\Comment\Comment; use Sabberworm\CSS\Comment\Commentable; +use Sabberworm\CSS\CSSElement; use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Parsing\ParserState; use Sabberworm\CSS\Parsing\UnexpectedEOFException; @@ -22,7 +23,7 @@ * If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` * (which accepts either a `Rule` or a rule name; optionally suffixed by a dash to remove all related rules). */ -abstract class RuleSet implements Commentable, Positionable, Renderable +abstract class RuleSet implements CSSElement, Commentable, Positionable { use Position; diff --git a/src/Value/Value.php b/src/Value/Value.php index beb74464a..3025566f0 100644 --- a/src/Value/Value.php +++ b/src/Value/Value.php @@ -2,19 +2,19 @@ namespace Sabberworm\CSS\Value; +use Sabberworm\CSS\CSSElement; use Sabberworm\CSS\Parsing\ParserState; use Sabberworm\CSS\Parsing\SourceException; use Sabberworm\CSS\Parsing\UnexpectedEOFException; use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\Position\Position; use Sabberworm\CSS\Position\Positionable; -use Sabberworm\CSS\Renderable; /** * Abstract base class for specific classes of CSS values: `Size`, `Color`, `CSSString` and `URL`, and another * abstract subclass `ValueList`. */ -abstract class Value implements Positionable, Renderable +abstract class Value implements CSSElement, Positionable { use Position; diff --git a/tests/Unit/CSSList/CSSBlockListTest.php b/tests/Unit/CSSList/CSSBlockListTest.php new file mode 100644 index 000000000..85a2e9e75 --- /dev/null +++ b/tests/Unit/CSSList/CSSBlockListTest.php @@ -0,0 +1,279 @@ +getAllValues()); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesReturnsOneValueDirectlySetAsContent() + { + $subject = new ConcreteCSSBlockList(); + + $value = new CSSString('Superfont'); + + $declarationBlock = new DeclarationBlock(); + $rule = new Rule('font-family'); + $rule->setValue($value); + $declarationBlock->addRule($rule); + $subject->setContents([$declarationBlock]); + + $result = $subject->getAllValues(); + + self::assertSame([$value], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesReturnsMultipleValuesDirectlySetAsContentInOneDeclarationBlock() + { + $subject = new ConcreteCSSBlockList(); + + $value1 = new CSSString('Superfont'); + $value2 = new CSSString('aquamarine'); + + $declarationBlock = new DeclarationBlock(); + $rule1 = new Rule('font-family'); + $rule1->setValue($value1); + $declarationBlock->addRule($rule1); + $rule2 = new Rule('color'); + $rule2->setValue($value2); + $declarationBlock->addRule($rule2); + $subject->setContents([$declarationBlock]); + + $result = $subject->getAllValues(); + + self::assertSame([$value1, $value2], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesReturnsMultipleValuesDirectlySetAsContentInMultipleDeclarationBlocks() + { + $subject = new ConcreteCSSBlockList(); + + $value1 = new CSSString('Superfont'); + $value2 = new CSSString('aquamarine'); + + $declarationBlock1 = new DeclarationBlock(); + $rule1 = new Rule('font-family'); + $rule1->setValue($value1); + $declarationBlock1->addRule($rule1); + $declarationBlock2 = new DeclarationBlock(); + $rule2 = new Rule('color'); + $rule2->setValue($value2); + $declarationBlock2->addRule($rule2); + $subject->setContents([$declarationBlock1, $declarationBlock2]); + + $result = $subject->getAllValues(); + + self::assertSame([$value1, $value2], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesReturnsValuesWithinAtRuleBlockList() + { + $subject = new ConcreteCSSBlockList(); + + $value = new CSSString('Superfont'); + + $declarationBlock = new DeclarationBlock(); + $rule = new Rule('font-family'); + $rule->setValue($value); + $declarationBlock->addRule($rule); + $atRuleBlockList = new AtRuleBlockList('media'); + $atRuleBlockList->setContents([$declarationBlock]); + $subject->setContents([$atRuleBlockList]); + + $result = $subject->getAllValues(); + + self::assertSame([$value], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesWithElementProvidedReturnsOnlyValuesWithinThatElement() + { + $subject = new ConcreteCSSBlockList(); + + $value1 = new CSSString('Superfont'); + $value2 = new CSSString('aquamarine'); + + $declarationBlock1 = new DeclarationBlock(); + $rule1 = new Rule('font-family'); + $rule1->setValue($value1); + $declarationBlock1->addRule($rule1); + $declarationBlock2 = new DeclarationBlock(); + $rule2 = new Rule('color'); + $rule2->setValue($value2); + $declarationBlock2->addRule($rule2); + $subject->setContents([$declarationBlock1, $declarationBlock2]); + + $result = $subject->getAllValues($declarationBlock1); + + self::assertSame([$value1], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesWithSearchStringProvidedReturnsOnlyValuesFromMatchingRules() + { + $subject = new ConcreteCSSBlockList(); + + $value1 = new CSSString('Superfont'); + $value2 = new CSSString('aquamarine'); + + $declarationBlock = new DeclarationBlock(); + $rule1 = new Rule('font-family'); + $rule1->setValue($value1); + $declarationBlock->addRule($rule1); + $rule2 = new Rule('color'); + $rule2->setValue($value2); + $declarationBlock->addRule($rule2); + $subject->setContents([$declarationBlock]); + + $result = $subject->getAllValues('font-'); + + self::assertSame([$value1], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesWithSearchStringProvidedInNewMethodSignatureReturnsOnlyValuesFromMatchingRules() + { + $subject = new ConcreteCSSBlockList(); + + $value1 = new CSSString('Superfont'); + $value2 = new CSSString('aquamarine'); + + $declarationBlock = new DeclarationBlock(); + $rule1 = new Rule('font-family'); + $rule1->setValue($value1); + $declarationBlock->addRule($rule1); + $rule2 = new Rule('color'); + $rule2->setValue($value2); + $declarationBlock->addRule($rule2); + $subject->setContents([$declarationBlock]); + + $result = $subject->getAllValues(null, 'font-'); + + self::assertSame([$value1], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesByDefaultDoesNotReturnValuesInFunctionArguments() + { + $subject = new ConcreteCSSBlockList(); + + $value1 = new Size(10, 'px'); + $value2 = new Size(2, '%'); + + $declarationBlock = new DeclarationBlock(); + $rule = new Rule('margin'); + $rule->setValue(new CSSFunction('max', [$value1, $value2])); + $declarationBlock->addRule($rule); + $subject->setContents([$declarationBlock]); + + $result = $subject->getAllValues(); + + self::assertSame([], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesWithSearchInFunctionArgumentsReturnsValuesInFunctionArguments() + { + $subject = new ConcreteCSSBlockList(); + + $value1 = new Size(10, 'px'); + $value2 = new Size(2, '%'); + + $declarationBlock = new DeclarationBlock(); + $rule = new Rule('margin'); + $rule->setValue(new CSSFunction('max', [$value1, $value2])); + $declarationBlock->addRule($rule); + $subject->setContents([$declarationBlock]); + + $result = $subject->getAllValues(null, true); + + self::assertSame([$value1, $value2], $result); + } + + /** + * @test + * + * @return void + */ + public function getAllValuesWithSearchInFunctionArgumentsInNewMethodSignatureReturnsValuesInFunctionArguments() + { + $subject = new ConcreteCSSBlockList(); + + $value1 = new Size(10, 'px'); + $value2 = new Size(2, '%'); + + $declarationBlock = new DeclarationBlock(); + $rule = new Rule('margin'); + $rule->setValue(new CSSFunction('max', [$value1, $value2])); + $declarationBlock->addRule($rule); + $subject->setContents([$declarationBlock]); + + $result = $subject->getAllValues(null, null, true); + + self::assertSame([$value1, $value2], $result); + } +} diff --git a/tests/Unit/CSSList/CSSListTest.php b/tests/Unit/CSSList/CSSListTest.php new file mode 100644 index 000000000..d7fa79cba --- /dev/null +++ b/tests/Unit/CSSList/CSSListTest.php @@ -0,0 +1,27 @@ +}> */ diff --git a/tests/Unit/RuleSet/Fixtures/ConcreteRuleSet.php b/tests/Unit/RuleSet/Fixtures/ConcreteRuleSet.php new file mode 100644 index 000000000..0aa966699 --- /dev/null +++ b/tests/Unit/RuleSet/Fixtures/ConcreteRuleSet.php @@ -0,0 +1,21 @@ +render(new OutputFormat()); + } +} diff --git a/tests/Unit/Value/ValueTest.php b/tests/Unit/Value/ValueTest.php new file mode 100644 index 000000000..d9ce5a4f3 --- /dev/null +++ b/tests/Unit/Value/ValueTest.php @@ -0,0 +1,28 @@ +