8000 [ExpressionLanguage] Fixed collisions of character operators with obj… · symfony/symfony@6ae75b6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6ae75b6

Browse files
committed
[ExpressionLanguage] Fixed collisions of character operators with object properties
1 parent 648d488 commit 6ae75b6

File tree

5 files changed

+64
-4
lines changed

5 files changed

+64
-4
lines changed

src/Symfony/Component/ExpressionLanguage/Lexer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public function tokenize($expression)
7373
// strings
7474
$tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1);
7575
$cursor += \strlen($match[0]);
76-
} elseif (preg_match('/not in(?=[\s(])|\!\=\=|not(?=[\s(])|and(?=[\s(])|\=\=\=|\>\=|or(?=[\s(])|\<\=|\*\*|\.\.|in(?=[\s(])|&&|\|\||matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) {
76+
} elseif (preg_match('/(?<!\.)not in(?=[\s(])|\!\=\=|(?<!\.)not(?=[\s(])|(?<!\.)and(?=[\s(])|\=\=\=|\>\=|(?<!\.)or(?=[\s(])|\<\=|\*\*|\.\.|(?<!\.)in(?=[\s(])|&&|\|\||(?<!\.)matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) {
7777
// operators
7878
$tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1);
7979
$cursor += \strlen($match[0]);

src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@
1515

1616
$regex = [];
1717
foreach ($operators as $operator => $length) {
18-
// an operator that ends with a character must be followed by
19-
// a whitespace or a parenthesis
20-
$regex[] = preg_quote($operator, '/').(ctype_alpha($operator[$length - 1]) ? '(?=[\s(])' : '');
18+
// Collisions of character operators:
19+
// - an operator that begins with a character must not be following after dot
20+
// - an operator that ends with a character must be followed by a whitespace or a parenthesis
21+
$regex[] =
22+
(ctype_alpha($operator[$length - 1]) ? '(?<!\.)' : '')
23+
.preg_quote($operator, '/')
24+
.(ctype_alpha($operator[$length - 1]) ? '(?=[\s(])' : '');
2125
}
2226

2327
echo '/'.implode('|', $regex).'/A';

src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,17 @@ public function testCachingWithDifferentNamesOrder()
233233
$expressionLanguage->compile($expression, ['B' => 'b', 'a']);
234234
}
235235

236+
public function testOperatorCollisions()
237+
{
238+
$expressionLanguage = new ExpressionLanguage();
239+
$expression = 'foo.not in [bar]';
240+
$compiled = $expressionLanguage->compile($expression, ['foo', 'bar']);
241+
$this->assertSame('in_array($foo->not, [0 => $bar])', $compiled);
242+
243+
$result = $expressionLanguage->evaluate($expression, ['foo' => (object) ['not' => 'test'], 'bar' => 'test']);
244+
$this->assertTrue($result);
245+
}
246+
236247
/**
237248
* @dataProvider getRegisterCallbacks
238249
*/

src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,18 @@ public function getTokenizeData()
112112
[new Token('string', '#foo', 1)],
113113
'"#foo"',
114114
],
115+
[
116+
[
117+
new Token('name', 'foo', 1),
118+
new Token('punctuation', '.', 4),
119+
new Token('name', 'not', 5),
120+
new Token('operator', 'in', 9),
121+
new Token('punctuation', '[', 12),
122+
new Token('name', 'bar', 13),
123+
new Token('punctuation', ']', 16),
124+
],
125+
'foo.not in [bar]',
126+
],
115127
];
116128
}
117129
}

src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public function getParseData()
53 6DB6 53
$arguments->addElement(new Node\ConstantNode(2));
5454
$arguments->addElement(new Node\ConstantNode(true));
5555

56+
$arrayNode = new Node\ArrayNode();
57+
$arrayNode->addElement(new Node\NameNode('bar'));
58+
5659
return [
5760
[
5861
new Node\NameNode('a'),
@@ -151,6 +154,36 @@ public function getParseData()
151154
'bar',
152155
['foo' => 'bar'],
153156
],
157+
158+
// Operators collisions
159+
[
160+
new Node\BinaryNode(
161+
'in',
162+
new Node\GetAttrNode(
163+
new Node\NameNode('foo'),
164+
new Node\ConstantNode('not', true),
165+
new Node\ArgumentsNode(),
166+
Node\GetAttrNode::PROPERTY_CALL
167+
),
168+
$arrayNode
169+
),
170+
'foo.not in [bar]',
171+
['foo', 'bar'],
172+
],
173+
[
174+
new Node\BinaryNode(
175+
'or',
176+
new Node\UnaryNode('not', new Node\NameNode('foo')),
177+
new Node\GetAttrNode(
178+
new Node\NameNode('foo'),
179+
new Node\ConstantNode('not', true),
180+
new Node\ArgumentsNode(),
181+
Node\GetAttrNode::PROPERTY_CALL
182+
)
183+
),
184+
'not foo or foo.not',
185+
['foo'],
186+
],
154187
];
155188
}
156189

0 commit comments

Comments
 (0)
0