From 719e2ef4a4df982b6a05eacfdc6ae78df237d2f5 Mon Sep 17 00:00:00 2001 From: JakeQZ Date: Fri, 17 Jan 2025 20:45:15 +0000 Subject: [PATCH] [BUGFIX] Parse `@font-face` src property as comma-delimited list Fixes #789. Also adds an initial `TestCase` for `Rule/Rule`. This is the 8.x backport of #790. --- CHANGELOG.md | 2 ++ src/Rule/Rule.php | 14 ++++++-- tests/Unit/Rule/RuleTest.php | 62 ++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/Unit/Rule/RuleTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index c90927cc2..969e437d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Fixed +- Parse `@font-face` `src` property as comma-delimited list (#794) + ## 8.7.0: Add support for PHP 8.4 ### Added diff --git a/src/Rule/Rule.php b/src/Rule/Rule.php index c1bad65eb..f10dbe660 100644 --- a/src/Rule/Rule.php +++ b/src/Rule/Rule.php @@ -114,16 +114,26 @@ public static function parse(ParserState $oParserState) } /** + * Returns a list of delimiters (or separators). + * The first item is the innermost separator (or, put another way, the highest-precedence operator). + * The sequence continues to the outermost separator (or lowest-precedence operator). + * * @param string $sRule * - * @return array + * @return list */ private static function listDelimiterForRule($sRule) { if (preg_match('/^font($|-)/', $sRule)) { return [',', '/', ' ']; } - return [',', ' ', '/']; + + switch ($sRule) { + case 'src': + return [' ', ',']; + default: + return [',', ' ', '/']; + } } /** diff --git a/tests/Unit/Rule/RuleTest.php b/tests/Unit/Rule/RuleTest.php new file mode 100644 index 000000000..0364e1eae --- /dev/null +++ b/tests/Unit/Rule/RuleTest.php @@ -0,0 +1,62 @@ +}> + */ + public static function provideRulesAndExpectedParsedValueListTypes() + { + return [ + 'src (e.g. in @font-face)' => [ + " + src: url('../fonts/open-sans-italic-300.woff2') format('woff2'), + url('../fonts/open-sans-italic-300.ttf') format('truetype'); + ", + [RuleValueList::class, RuleValueList::class], + ], + ]; + } + + /** + * @test + * + * @param string $rule + * @param list $expectedTypeClassnames + * + * @dataProvider provideRulesAndExpectedParsedValueListTypes + */ + public function parsesValuesIntoExpectedTypeList($rule, array $expectedTypeClassnames) + { + $subject = Rule::parse(new ParserState($rule, Settings::create())); + + $value = $subject->getValue(); + self::assertInstanceOf(ValueList::class, $value); + + $actualClassnames = \array_map( + /** + * @param Value|string $component + * @return string + */ + static function ($component) { + return \is_string($component) ? 'string' : \get_class($component); + }, + $value->getListComponents() + ); + + self::assertSame($expectedTypeClassnames, $actualClassnames); + } +}