minor #24007 [ExpressionLanguage] SyntaxError : make a proposal when no function or variable found (fmata)
This PR was merged into the 3.4 branch.
Discussion
----------
[ExpressionLanguage] SyntaxError : make a proposal when no function or variable found
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | n/a
| License | MIT
| Doc PR | needed ?
Now when an Expression fails to be parsed, a SyntaxError is raised with an explicit message but it's impossible to extract the function, variable or the pair token type/token value to parse.
When a functional user encounters a SyntaxError, by default there is no mechanism to understand why what he typed is incorrect.
This PR exposes when it's possible the subject of the violation and in case of function or variable undefined in the Expression, a proposal is made.
```php
<?php
require 'vendor/autoload.php';
use Symfony\Component\ExpressionLanguage\Lexer;
use Symfony\Component\ExpressionLanguage\Parser;
use Symfony\Component\ExpressionLanguage\SyntaxError;
$lexer = new Lexer();
$parser = new Parser(array());
try {
$parser->parse($lexer->tokenize('user.city.departement in [departement, departement2]'), array('user', 'departement1', 'departement2'));
} catch (SyntaxError $e) {
echo $e->getMessage();
}
```
Outputs :
> Variable "departement" is not valid around position 27 for expression `user.city.departement in [departement, departement2]`.
>
> Did you mean "departement1"?
Commits
-------
f19cf8a916
[ExpressionLanguage] make a proposal in SyntaxError message
This commit is contained in:
commit
565a1c4b48
@ -195,13 +195,13 @@ class Parser
|
|||||||
default:
|
default:
|
||||||
if ('(' === $this->stream->current->value) {
|
if ('(' === $this->stream->current->value) {
|
||||||
if (false === isset($this->functions[$token->value])) {
|
if (false === isset($this->functions[$token->value])) {
|
||||||
throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor, $this->stream->getExpression());
|
throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, array_keys($this->functions));
|
||||||
}
|
}
|
||||||
|
|
||||||
$node = new Node\FunctionNode($token->value, $this->parseArguments());
|
$node = new Node\FunctionNode($token->value, $this->parseArguments());
|
||||||
} else {
|
} else {
|
||||||
if (!in_array($token->value, $this->names, true)) {
|
if (!in_array($token->value, $this->names, true)) {
|
||||||
throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor, $this->stream->getExpression());
|
throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, $this->names);
|
||||||
}
|
}
|
||||||
|
|
||||||
// is the name used in the compiled code different
|
// is the name used in the compiled code different
|
||||||
|
@ -13,7 +13,7 @@ namespace Symfony\Component\ExpressionLanguage;
|
|||||||
|
|
||||||
class SyntaxError extends \LogicException
|
class SyntaxError extends \LogicException
|
||||||
{
|
{
|
||||||
public function __construct($message, $cursor = 0, $expression = '')
|
public function __construct($message, $cursor = 0, $expression = '', $subject = null, array $proposals = null)
|
||||||
{
|
{
|
||||||
$message = sprintf('%s around position %d', $message, $cursor);
|
$message = sprintf('%s around position %d', $message, $cursor);
|
||||||
if ($expression) {
|
if ($expression) {
|
||||||
@ -21,6 +21,21 @@ class SyntaxError extends \LogicException
|
|||||||
}
|
}
|
||||||
$message .= '.';
|
$message .= '.';
|
||||||
|
|
||||||
|
if (null !== $subject && null !== $proposals) {
|
||||||
|
$minScore = INF;
|
||||||
|
foreach ($proposals as $proposal) {
|
||||||
|
$distance = levenshtein($subject, $proposal);
|
||||||
|
if ($distance < $minScore) {
|
||||||
|
$guess = $proposal;
|
||||||
|
$minScore = $distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($guess) && $minScore < 3) {
|
||||||
|
$message .= sprintf(' Did you mean "%s"?', $guess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parent::__construct($message);
|
parent::__construct($message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,4 +195,16 @@ class ParserTest extends TestCase
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
|
||||||
|
* @expectedExceptionMessage Did you mean "baz"?
|
||||||
|
*/
|
||||||
|
public function testNameProposal()
|
||||||
|
{
|
||||||
|
$lexer = new Lexer();
|
||||||
|
$parser = new Parser(array());
|
||||||
|
|
||||||
|
$parser->parse($lexer->tokenize('foo > bar'), array('foo', 'baz'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user