Skip to content

Commit dddbba0

Browse files
authored
[FEATURE] Support arithmetic operators in CSS functions (#607)
This is the enhancement from #390. `calc` was already supported. Closes #390
1 parent 046ab81 commit dddbba0

File tree

3 files changed

+120
-1
lines changed

3 files changed

+120
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
66
## x.y.z
77

88
### Added
9+
- Support arithmetic operators in CSS function arguments (#607)
910
- Add support for inserting an item in a CSS list (#545)
1011
- Add a class diagram to the README (#482)
1112
- Add support for the `dvh`, `lvh` and `svh` length units (#415)

src/Value/Value.php

+10-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,16 @@ public static function parsePrimitiveValue(ParserState $oParserState)
164164
} elseif ($oParserState->comes("U+")) {
165165
$oValue = self::parseUnicodeRangeValue($oParserState);
166166
} else {
167-
$oValue = self::parseIdentifierOrFunction($oParserState);
167+
$sNextChar = $oParserState->peek(1);
168+
try {
169+
$oValue = self::parseIdentifierOrFunction($oParserState);
170+
} catch (UnexpectedTokenException $e) {
171+
if (\in_array($sNextChar, ['+', '-', '*', '/'], true)) {
172+
$oValue = $oParserState->consume(1);
173+
} else {
174+
throw $e;
175+
}
176+
}
168177
}
169178
$oParserState->consumeWhiteSpace();
170179
return $oValue;

tests/Value/ValueTest.php

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Tests\Value;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Sabberworm\CSS\Parsing\ParserState;
9+
use Sabberworm\CSS\Settings;
10+
use Sabberworm\CSS\Value\Value;
11+
12+
/**
13+
* @covers \Sabberworm\CSS\Value\Value
14+
*/
15+
final class ValueTest extends TestCase
16+
{
17+
/**
18+
* @return array<string, array{0: string}>
19+
*/
20+
public static function provideArithmeticOperator(): array
21+
{
22+
$units = ['+', '-', '*', '/'];
23+
24+
return \array_combine(
25+
$units,
26+
\array_map(
27+
function (string $unit): array {
28+
return [$unit];
29+
},
30+
$units
31+
)
32+
);
33+
}
34+
35+
/**
36+
* @test
37+
*
38+
* @dataProvider provideArithmeticOperator
39+
*/
40+
public function parsesArithmeticInFunctions(string $operator): void
41+
{
42+
$subject = Value::parseValue(new ParserState('max(300px, 50vh ' . $operator . ' 10px);', Settings::create()));
43+
44+
self::assertSame('max(300px,50vh ' . $operator . ' 10px)', (string) $subject);
45+
}
46+
47+
/**
48+
* @return array<string, array{0: string, 1: string}>
49+
* The first datum is a template for the parser (using `sprintf` insertion marker `%s` for some expression).
50+
* The second is for the expected result, which may have whitespace and trailing semicolon removed.
51+
*/
52+
public static function provideCssFunctionTemplates(): array
53+
{
54+
return [
55+
'calc' => [
56+
'to be parsed' => 'calc(%s);',
57+
'expected' => 'calc(%s)',
58+
],
59+
'max' => [
60+
'to be parsed' => 'max(300px, %s);',
61+
'expected' => 'max(300px,%s)',
62+
],
63+
];
64+
}
65+
66+
/**
67+
* @test
68+
*
69+
* @dataProvider provideCssFunctionTemplates
70+
*/
71+
public function parsesArithmeticWithMultipleOperatorsInFunctions(
72+
string $parserTemplate,
73+
string $expectedResultTemplate
74+
): void {
75+
static $expression = '300px + 10% + 10vw';
76+
77+
$subject = Value::parseValue(new ParserState(\sprintf($parserTemplate, $expression), Settings::create()));
78+
79+
self::assertSame(\sprintf($expectedResultTemplate, $expression), (string) $subject);
80+
}
81+
82+
/**
83+
* @return array<string, array{0: string, 1: string}>
84+
*/
85+
public static function provideMalformedLengthOperands(): array
86+
{
87+
return [
88+
'LHS missing number' => ['vh', '10px'],
89+
'RHS missing number' => ['50vh', 'px'],
90+
'LHS missing unit' => ['50', '10px'],
91+
'RHS missing unit' => ['50vh', '10'],
92+
];
93+
}
94+
95+
/**
96+
* @test
97+
*
98+
* @dataProvider provideMalformedLengthOperands
99+
*/
100+
public function parsesArithmeticWithMalformedOperandsInFunctions(string $leftOperand, string $rightOperand): void
101+
{
102+
$subject = Value::parseValue(new ParserState(
103+
'max(300px, ' . $leftOperand . ' + ' . $rightOperand . ');',
104+
Settings::create()
105+
));
106+
107+
self::assertSame('max(300px,' . $leftOperand . ' + ' . $rightOperand . ')', (string) $subject);
108+
}
109+
}

0 commit comments

Comments
 (0)