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 6ae75b6

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

File tree

5 files changed

+64
-4
lines changed
Filter options

5 files changed

+64
-4
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
+7-3Lines 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

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
@@ -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

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
@@ -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

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.