Skip to content

Commit b36a6ac

Browse files
authored
[BUGFIX] Do not escape characters that do not need escaping in CSS string (#1444)
1 parent 6f07de2 commit b36a6ac

5 files changed

Lines changed: 55 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Please also have a look at our
2626

2727
### Fixed
2828

29+
- Do not escape characters that do not need escaping in CSS string (#1444)
2930
- Reject selector comprising only whitespace (#1433)
3031
- Improve recovery parsing when a rogue `}` is encountered (#1425, #1426)
3132
- Parse comment(s) immediately preceding a selector (#1421, #1424)

src/Value/CSSString.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public function getString(): string
9393
*/
9494
public function render(OutputFormat $outputFormat): string
9595
{
96-
$string = \addslashes($this->string);
96+
$string = $this->escape($this->string, $outputFormat->getStringQuotingType());
9797
$string = \str_replace("\n", '\\A', $string);
9898
return $outputFormat->getStringQuotingType() . $string . $outputFormat->getStringQuotingType();
9999
}
@@ -111,4 +111,15 @@ public function getArrayRepresentation(): array
111111
'contents' => $this->string,
112112
];
113113
}
114+
115+
/**
116+
* @param "'"|'"' $quote
117+
*/
118+
private function escape(string $string, string $quote): string
119+
{
120+
$charactersToEscape = '\\';
121+
$charactersToEscape .= ($quote === '"' ? '"' : "'");
122+
123+
return \addcslashes($string, $charactersToEscape);
124+
}
114125
}

tests/ParserTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,10 @@ public function unicodeParsing(): void
206206
self::assertSame('"\\"\\""', $firstContentRuleAsString);
207207
}
208208
if ($selector === '.test-9') {
209-
self::assertSame('"\\"\\\'"', $firstContentRuleAsString);
209+
self::assertSame('"\\"\'"', $firstContentRuleAsString);
210210
}
211211
if ($selector === '.test-10') {
212-
self::assertSame('"\\\'\\\\"', $firstContentRuleAsString);
212+
self::assertSame('"\'\\\\"', $firstContentRuleAsString);
213213
}
214214
if ($selector === '.test-11') {
215215
self::assertSame('"test"', $firstContentRuleAsString);

tests/Unit/Value/CSSStringTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Sabberworm\CSS\Tests\Unit\Value;
66

77
use PHPUnit\Framework\TestCase;
8+
use Sabberworm\CSS\OutputFormat;
89
use Sabberworm\CSS\Value\CSSString;
910
use Sabberworm\CSS\Value\PrimitiveValue;
1011
use Sabberworm\CSS\Value\Value;
@@ -107,4 +108,42 @@ public function getArrayRepresentationIncludesContents(): void
107108
self::assertArrayHasKey('contents', $result);
108109
self::assertSame($contents, $result['contents']);
109110
}
111+
112+
/**
113+
* @test
114+
*/
115+
public function doesNotEscapeSingleQuotesThatDoNotNeedToBeEscaped(): void
116+
{
117+
$input = "data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none'" .
118+
" xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd'" .
119+
" d='M14.3145 2 3V11.9987H17.5687L18 8.20761H14.3145L14.32 6.31012C14.32 5.32134 14.4207" .
120+
' 4.79153 15.9426 4.79153H17.977V1H14.7223C10.8129 1 9.43687 2.83909 9.43687 ' .
121+
" 5.93187V8.20804H7V11.9991H9.43687V23H14.3145Z' fill='black'/%3E%3C/svg%3E%0A";
122+
123+
$outputFormat = OutputFormat::createPretty();
124+
125+
self::assertSame("\"{$input}\"", (new CSSString($input))->render($outputFormat));
126+
127+
$outputFormat->setStringQuotingType("'");
128+
129+
$expected = str_replace("'", "\\'", $input);
130+
131+
self::assertSame("'{$expected}'", (new CSSString($input))->render($outputFormat));
132+
}
133+
134+
/**
135+
* @test
136+
*/
137+
public function doesNotEscapeDoubleQuotesThatDoNotNeedToBeEscaped(): void
138+
{
139+
$input = '"Hello World"';
140+
141+
$outputFormat = OutputFormat::createPretty();
142+
143+
self::assertSame('"\\"Hello World\\""', (new CSSString($input))->render($outputFormat));
144+
145+
$outputFormat->setStringQuotingType("'");
146+
147+
self::assertSame("'{$input}'", (new CSSString($input))->render($outputFormat));
148+
}
110149
}

tests/fixtures/unicode.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
.test-6 { content: "\00A5" } /* Same as "¥" */
66
.test-7 { content: '\a' } /* Same as "\A" (Newline) */
77
.test-8 { content: "\"\22" } /* Same as "\"\"" */
8-
.test-9 { content: "\"\27" } /* Same as ""\"\'"" */
8+
.test-9 { content: "\"\27" } /* Same as "\"'" */
99
.test-10 { content: "\'\\" } /* Same as "'\" */
1010
.test-11 { content: "\test" } /* Same as "test" */
1111

0 commit comments

Comments
 (0)