Skip to content

Commit

Permalink
[ExpressionLanguage] Fixed collisions of character operators with obj…
Browse files Browse the repository at this point in the history
…ect properties
  • Loading branch information
Andrej-in-ua committed Feb 17, 2020
1 parent 648d488 commit 6ae75b6
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/Symfony/Component/ExpressionLanguage/Lexer.php
Expand Up @@ -73,7 +73,7 @@ public function tokenize($expression)
// strings
$tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1);
$cursor += \strlen($match[0]);
} elseif (preg_match('/not in(?=[\s(])|\!\=\=|not(?=[\s(])|and(?=[\s(])|\=\=\=|\>\=|or(?=[\s(])|\<\=|\*\*|\.\.|in(?=[\s(])|&&|\|\||matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) {
} elseif (preg_match('/(?<!\.)not in(?=[\s(])|\!\=\=|(?<!\.)not(?=[\s(])|(?<!\.)and(?=[\s(])|\=\=\=|\>\=|(?<!\.)or(?=[\s(])|\<\=|\*\*|\.\.|(?<!\.)in(?=[\s(])|&&|\|\||(?<!\.)matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) {
// operators
$tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1);
$cursor += \strlen($match[0]);
Expand Down
Expand Up @@ -15,9 +15,13 @@

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

echo '/'.implode('|', $regex).'/A';
Expand Up @@ -233,6 +233,17 @@ public function testCachingWithDifferentNamesOrder()
$expressionLanguage->compile($expression, ['B' => 'b', 'a']);
}

public function testOperatorCollisions()
{
$expressionLanguage = new ExpressionLanguage();
$expression = 'foo.not in [bar]';
$compiled = $expressionLanguage->compile($expression, ['foo', 'bar']);
$this->assertSame('in_array($foo->not, [0 => $bar])', $compiled);

$result = $expressionLanguage->evaluate($expression, ['foo' => (object) ['not' => 'test'], 'bar' => 'test']);
$this->assertTrue($result);
}

/**
* @dataProvider getRegisterCallbacks
*/
Expand Down
12 changes: 12 additions & 0 deletions src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php
Expand Up @@ -112,6 +112,18 @@ public function getTokenizeData()
[new Token('string', '#foo', 1)],
'"#foo"',
],
[
[
new Token('name', 'foo', 1),
new Token('punctuation', '.', 4),
new Token('name', 'not', 5),
new Token('operator', 'in', 9),
new Token('punctuation', '[', 12),
new Token('name', 'bar', 13),
new Token('punctuation', ']', 16),
],
'foo.not in [bar]',
],
];
}
}
33 changes: 33 additions & 0 deletions src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php
Expand Up @@ -53,6 +53,9 @@ public function getParseData()
$arguments->addElement(new Node\ConstantNode(2));
$arguments->addElement(new Node\ConstantNode(true));

$arrayNode = new Node\ArrayNode();
$arrayNode->addElement(new Node\NameNode('bar'));

return [
[
new Node\NameNode('a'),
Expand Down Expand Up @@ -151,6 +154,36 @@ public function getParseData()
'bar',
['foo' => 'bar'],
],

// Operators collisions
[
new Node\BinaryNode(
'in',
new Node\GetAttrNode(
new Node\NameNode('foo'),
new Node\ConstantNode('not', true),
new Node\ArgumentsNode(),
Node\GetAttrNode::PROPERTY_CALL
),
$arrayNode
),
'foo.not in [bar]',
['foo', 'bar'],
],
[
new Node\BinaryNode(
'or',
new Node\UnaryNode('not', new Node\NameNode('foo')),
new Node\GetAttrNode(
new Node\NameNode('foo'),
new Node\ConstantNode('not', true),
new Node\ArgumentsNode(),
Node\GetAttrNode::PROPERTY_CALL
)
),
'not foo or foo.not',
['foo'],
],
];
}

Expand Down

0 comments on commit 6ae75b6

Please sign in to comment.