Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 60868aa

Browse filesBrowse files
committed
[ExpressionLanguage] Fixed collisions of character operators with object properties
1 parent e87b599 commit 60868aa
Copy full SHA for 60868aa

File tree

Expand file treeCollapse file tree

5 files changed

+65
-5
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+65
-5
lines changed

‎src/Symfony/Component/ExpressionLanguage/Lexer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ExpressionLanguage/Lexer.php
+1-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php
+8-4Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
$operators = ['not', '!', 'or', '||', '&&', 'and', '|', '^', '&', '==', '===', '!=', '!==', '<', '>', '>=', '<=', 'not in', 'in', '..', '+', '-', '~', '*', '/', '%', 'matches', '**'];
12+
$operators = ['not', '!', 'or', '||', '&&', 'and', '|', '^', '&', '==', '===', '!=', '!==', '<', '>', '>=', '<=', 'not in', 'in', '..', '+', '-', '~', '*', '/', '%', 'matches', '**', 'xor'];
1313
$operators = array_combine($operators, array_map('strlen', $operators));
1414
arsort($operators);
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

Copy file name to clipboardExpand all lines: src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,17 @@ public function testCachingWithDifferentNamesOrder()
195195
$expressionLanguage->compile($expression, ['B' => 'b', 'a']);
196196
}
197197

198+
public function testOperatorCollisions()
199+
{
200+
$expressionLanguage = new ExpressionLanguage();
201+
$expression = 'foo.not in [bar]';
202+
$compiled = $expressionLanguage->compile($expression, ['foo', 'bar']);
203+
$this->assertSame('in_array($foo->not, [0 => $bar])', $compiled);
204+
205+
$result = $expressionLanguage->evaluate($expression, ['foo' => (object) ['not' => 'test'], 'bar' => 'test']);
206+
$this->assertTrue($result);
207+
}
208+
198209
/**
199210
* @dataProvider getRegisterCallbacks
200211
*/

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ public function getTokenizeData()
114114
[new Token('string', '#foo', 1)],
115115
'"#foo"',
116116
],
117+
[
118+
[
119+
new Token('name', 'foo', 1),
120+
new Token('punctuation', '.', 4),
121+
new Token('name', 'not', 5),
122+
new Token('operator', 'in', 9),
123+
new Token('punctuation', '[', 12),
124+
new Token('name', 'bar', 13),
125+
new Token('punctuation', ']', 16),
126+
],
127+
'foo.not in [bar]',
128+
],
117129
];
118130
}
119131
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public function getParseData()
5353
$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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.