[ExpressionLanguage] Fixed collisions of character operators with object properties
This commit is contained in:
parent
648d488bb2
commit
4b83ae7547
@ -73,7 +73,7 @@ class Lexer
|
|||||||
// strings
|
// strings
|
||||||
$tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1);
|
$tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1);
|
||||||
$cursor += \strlen($match[0]);
|
$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('/(?<=^|[\s(])not in(?=[\s(])|\!\=\=|(?<=^|[\s(])not(?=[\s(])|(?<=^|[\s(])and(?=[\s(])|\=\=\=|\>\=|(?<=^|[\s(])or(?=[\s(])|\<\=|\*\*|\.\.|(?<=^|[\s(])in(?=[\s(])|&&|\|\||(?<=^|[\s(])matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) {
|
||||||
// operators
|
// operators
|
||||||
$tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1);
|
$tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1);
|
||||||
$cursor += \strlen($match[0]);
|
$cursor += \strlen($match[0]);
|
||||||
|
@ -15,9 +15,13 @@ arsort($operators);
|
|||||||
|
|
||||||
$regex = [];
|
$regex = [];
|
||||||
foreach ($operators as $operator => $length) {
|
foreach ($operators as $operator => $length) {
|
||||||
// an operator that ends with a character must be followed by
|
// Collisions of character operators:
|
||||||
// a whitespace or a parenthesis
|
// - an operator that begins with a character must have a space or a parenthesis before or starting at the beginning of a string
|
||||||
$regex[] = preg_quote($operator, '/').(ctype_alpha($operator[$length - 1]) ? '(?=[\s(])' : '');
|
// - an operator that ends with a character must be followed by a whitespace or a parenthesis
|
||||||
|
$regex[] =
|
||||||
|
(ctype_alpha($operator[0]) ? '(?<=^|[\s(])' : '')
|
||||||
|
.preg_quote($operator, '/')
|
||||||
|
.(ctype_alpha($operator[$length - 1]) ? '(?=[\s(])' : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
echo '/'.implode('|', $regex).'/A';
|
echo '/'.implode('|', $regex).'/A';
|
||||||
|
@ -233,6 +233,17 @@ class ExpressionLanguageTest extends TestCase
|
|||||||
$expressionLanguage->compile($expression, ['B' => 'b', 'a']);
|
$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
|
* @dataProvider getRegisterCallbacks
|
||||||
*/
|
*/
|
||||||
|
@ -112,6 +112,18 @@ class LexerTest extends TestCase
|
|||||||
[new Token('string', '#foo', 1)],
|
[new Token('string', '#foo', 1)],
|
||||||
'"#foo"',
|
'"#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]',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,9 @@ class ParserTest extends TestCase
|
|||||||
$arguments->addElement(new Node\ConstantNode(2));
|
$arguments->addElement(new Node\ConstantNode(2));
|
||||||
$arguments->addElement(new Node\ConstantNode(true));
|
$arguments->addElement(new Node\ConstantNode(true));
|
||||||
|
|
||||||
|
$arrayNode = new Node\ArrayNode();
|
||||||
|
$arrayNode->addElement(new Node\NameNode('bar'));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
new Node\NameNode('a'),
|
new Node\NameNode('a'),
|
||||||
@ -151,6 +154,36 @@ class ParserTest extends TestCase
|
|||||||
'bar',
|
'bar',
|
||||||
['foo' => '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'],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user