Skip to content

Commit e8bd4f1

Browse files
committed
feature symfony#54757 [ExpressionLanguage] Support non-existent names when followed by null coalescing (adamkiss)
This PR was squashed before being merged into the 7.2 branch. Discussion ---------- [ExpressionLanguage] Support non-existent names when followed by null coalescing | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Issues | Fix symfony#54359 | License | MIT This PR allows for null coalescing nonexistent names (`nonexistent ?? 123`) in expressions by introducing new type of node `NullCoalescedNameNode`, which compiles to `$nonexistent ?? null`, so it subsequently evaluates to null and the null coalescing takes over then. Edit: feature/bug is questionable, because I half-consider it a bug, since it's _one of the examples in the docs_, despite my original issue being closed as "expected", and the docs issue marked resolved despite this use case throwing while existing names resolving to null pass. Commits ------- 3c620dc [ExpressionLanguage] Support non-existent names when followed by null coalescing
2 parents f7c87b0 + 3c620dc commit e8bd4f1

File tree

5 files changed

+93
-0
lines changed

5 files changed

+93
-0
lines changed

src/Symfony/Component/ExpressionLanguage/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.2
5+
---
6+
7+
* Add support for null-coalescing unknown variables
8+
49
7.1
510
---
611

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ExpressionLanguage\Node;
13+
14+
use Symfony\Component\ExpressionLanguage\Compiler;
15+
16+
/**
17+
* @author Adam Kiss <[email protected]>
18+
*
19+
* @internal
20+
*/
21+
class NullCoalescedNameNode extends Node
22+
{
23+
public function __construct(string $name)
24+
{
25+
parent::__construct(
26+
[],
27+
['name' => $name]
28+
);
29+
}
30+
31+
public function compile(Compiler $compiler): void
32+
{
33+
$compiler->raw('$'.$this->attributes['name'].' ?? null');
34+
}
35+
36+
public function evaluate(array $functions, array $values): null
37+
{
38+
return null;
39+
}
40+
41+
public function toArray(): array
42+
{
43+
return [$this->attributes['name'].' ?? null'];
44+
}
45+
}

src/Symfony/Component/ExpressionLanguage/Parser.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ public function parsePrimaryExpression(): Node\Node
246246
} else {
247247
if (!($this->flags & self::IGNORE_UNKNOWN_VARIABLES)) {
248248
if (!\in_array($token->value, $this->names, true)) {
249+
if ($this->stream->current->test(Token::PUNCTUATION_TYPE, '??')) {
250+
return new Node\NullCoalescedNameNode($token->value);
251+
}
252+
249253
throw new SyntaxError(sprintf('Variable "%s" is not valid.', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, $this->names);
250254
}
251255

src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ public function bar()
433433
}
434434
};
435435

436+
yield ['bar ?? "default"', null];
436437
yield ['foo.bar ?? "default"', null];
437438
yield ['foo.bar.baz ?? "default"', (object) ['bar' => null]];
438439
yield ['foo.bar ?? foo.baz ?? "default"', null];
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ExpressionLanguage\Tests\Node;
13+
14+
use Symfony\Component\ExpressionLanguage\Node\NullCoalescedNameNode;
15+
16+
class NullCoalescedNameNodeTest extends AbstractNodeTestCase
17+
{
18+
public static function getEvaluateData(): array
19+
{
20+
return [
21+
[null, new NullCoalescedNameNode('foo'), []],
22+
];
23+
}
24+
25+
public static function getCompileData(): array
26+
{
27+
return [
28+
['$foo ?? null', new NullCoalescedNameNode('foo')],
29+
];
30+
}
31+
32+
public static function getDumpData(): array
33+
{
34+
return [
35+
['foo ?? null', new NullCoalescedNameNode('foo')],
36+
];
37+
}
38+
}

0 commit comments

Comments
 (0)