Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 60868aa

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

File tree

5 files changed

+65
-5
lines changed

5 files changed

+65
-5
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: 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

Lines 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

Lines 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

Lines 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)