From 753a2fc5ae4235cf9750cf7d289d25fd032c0360 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 21 Dec 2019 00:47:30 +0100 Subject: [PATCH] Added optional chaining for $var->elem?[1] --- src/Latte/Compiler/PhpWriter.php | 4 ++-- tests/Latte/PhpWriter.formatArgs().phpt | 1 + tests/Latte/PhpWriter.formatModifiers().phpt | 1 + tests/Latte/PhpWriter.optionalChainingPass().phpt | 14 ++++++++++++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Latte/Compiler/PhpWriter.php b/src/Latte/Compiler/PhpWriter.php index fb9aa3b8a..fcfe7dea2 100644 --- a/src/Latte/Compiler/PhpWriter.php +++ b/src/Latte/Compiler/PhpWriter.php @@ -295,14 +295,14 @@ public function optionalChainingPass(MacroTokens $tokens): MacroTokens do { if ($tokens->nextToken('?')) { - if ($tokens->isNext() && (!$tokens->isNext($tokens::T_CHAR) || $tokens->isNext('(', '[', '{', ':', '!', '@'))) { // is it ternary operator? + if ($tokens->isNext() && (!$tokens->isNext($tokens::T_CHAR) || $tokens->isNext('(', '{', ':', '!', '@'))) { // is it ternary operator? $expr->append($addBraces . ' ?'); break; } $rescue = [$res->tokens, $expr->tokens, $tokens->position, $addBraces]; - if (!$tokens->isNext('->')) { + if (!$tokens->isNext('->', '[')) { $expr->prepend('('); $expr->append(' ?? null)' . $addBraces); break; diff --git a/tests/Latte/PhpWriter.formatArgs().phpt b/tests/Latte/PhpWriter.formatArgs().phpt index 403df6b63..78dea2662 100644 --- a/tests/Latte/PhpWriter.formatArgs().phpt +++ b/tests/Latte/PhpWriter.formatArgs().phpt @@ -137,6 +137,7 @@ test(function () { // optionalChainingPass Assert::same('$a', formatArgs('$a')); Assert::same('($a ?? null)', formatArgs('$a?')); Assert::same('(($a ?? null))', formatArgs('($a?)')); + Assert::same('(($_tmp = $foo ?? null) === null ? null : $_tmp[1])', formatArgs('$foo?[1]')); Assert::same('$var->prop->elem[1]->call(2)->item', formatArgs('$var->prop->elem[1]->call(2)->item')); Assert::same('(($_tmp = $var ?? null) === null ? null : (($_tmp = $_tmp->prop ?? null) === null ? null : (($_tmp = $_tmp->elem[1] ?? null) === null ? null : (($_tmp = $_tmp->call(2) ?? null) === null ? null : ($_tmp->item ?? null)))))', formatArgs('$var?->prop?->elem[1]?->call(2)?->item?')); }); diff --git a/tests/Latte/PhpWriter.formatModifiers().phpt b/tests/Latte/PhpWriter.formatModifiers().phpt index 6253b0a4b..5cbcc23ce 100644 --- a/tests/Latte/PhpWriter.formatModifiers().phpt +++ b/tests/Latte/PhpWriter.formatModifiers().phpt @@ -70,6 +70,7 @@ test(function () { // depth test(function () { // optionalChainingPass Assert::same('($this->filters->mod)(@, ($a ?? null))', formatModifiers('@', 'mod:$a?')); Assert::same('($this->filters->mod)(@, (($a ?? null)))', formatModifiers('@', 'mod:($a?)')); + Assert::same('($this->filters->mod)(@, (($_tmp = $foo ?? null) === null ? null : $_tmp[1]))', formatModifiers('@', 'mod:$foo?[1]')); Assert::same('($this->filters->mod)(@, (($_tmp = $var ?? null) === null ? null : (($_tmp = $_tmp->prop ?? null) === null ? null : (($_tmp = $_tmp->elem[1] ?? null) === null ? null : (($_tmp = $_tmp->call(2) ?? null) === null ? null : ($_tmp->item ?? null))))))', formatModifiers('@', 'mod:$var?->prop?->elem[1]?->call(2)?->item?')); }); diff --git a/tests/Latte/PhpWriter.optionalChainingPass().phpt b/tests/Latte/PhpWriter.optionalChainingPass().phpt index 23eae5984..dc8248b6c 100644 --- a/tests/Latte/PhpWriter.optionalChainingPass().phpt +++ b/tests/Latte/PhpWriter.optionalChainingPass().phpt @@ -22,8 +22,15 @@ test(function () { Assert::same('($a ?? null)', optionalChaining('$a?')); Assert::same('(($a ?? null))', optionalChaining('($a?)')); Assert::same('a?', optionalChaining('a?')); + Assert::same('(($_tmp = $foo ?? null) === null ? null : $_tmp[1])', optionalChaining('$foo?[1]')); Assert::same('($foo[1] ?? null)', optionalChaining('$foo[1]?')); + Assert::same('(($_tmp = $foo ?? null) === null ? null : ($_tmp[1] ?? null))', optionalChaining('$foo?[1]?')); + Assert::same('(($_tmp = $foo ?? null) === null ? null : ($_tmp[1] ?? null)) + 10', optionalChaining('$foo?[1]? + 10')); Assert::same('(($foo[1] ?? null))', optionalChaining('($foo[1]?)')); + Assert::same('((($_tmp = $foo ?? null) === null ? null : $_tmp[1]))', optionalChaining('($foo?[1])')); + Assert::same('[(($_tmp = $foo ?? null) === null ? null : ($_tmp[1] ?? null))]', optionalChaining('[$foo?[1]?]')); + Assert::same('(($_tmp = $foo ?? null) === null ? null : ($_tmp[ ($a ?? null) ] ?? null))', optionalChaining('$foo?[ $a? ]?')); + Assert::same('(($_tmp = $foo ?? null) === null ? null : ($_tmp[ (($_tmp = $a ?? null) === null ? null : ($_tmp[2] ?? null)) ] ?? null))', optionalChaining('$foo?[ $a?[2]? ]?')); Assert::same('(($_tmp = $foo ?? null) === null ? null : $_tmp->prop)', optionalChaining('$foo?->prop')); Assert::same('($foo->prop ?? null)', optionalChaining('$foo->prop?')); @@ -50,17 +57,20 @@ test(function () { Assert::same('$var->prop->elem[1]->call(2)->item', optionalChaining('$var->prop->elem[1]->call(2)->item')); Assert::same('(($_tmp = $var ?? null) === null ? null : $_tmp->prop->elem[1]->call(2)->item)', optionalChaining('$var?->prop->elem[1]->call(2)->item')); Assert::same('(($_tmp = $var->prop ?? null) === null ? null : $_tmp->elem[1]->call(2)->item)', optionalChaining('$var->prop?->elem[1]->call(2)->item')); + Assert::same('(($_tmp = $var->prop->elem ?? null) === null ? null : $_tmp[1]->call(2)->item)', optionalChaining('$var->prop->elem?[1]->call(2)->item')); Assert::same('(($_tmp = $var->prop->elem[1] ?? null) === null ? null : $_tmp->call(2)->item)', optionalChaining('$var->prop->elem[1]?->call(2)->item')); Assert::same('(($_tmp = $var->prop->elem[1]->call(2) ?? null) === null ? null : $_tmp->item)', optionalChaining('$var->prop->elem[1]->call(2)?->item')); Assert::same('($var->prop->elem[1]->call(2)->item ?? null)', optionalChaining('$var->prop->elem[1]->call(2)->item?')); + Assert::same( + '(($_tmp = $var ?? null) === null ? null : (($_tmp = $_tmp->prop ?? null) === null ? null : (($_tmp = $_tmp->elem ?? null) === null ? null : (($_tmp = $_tmp[1] ?? null) === null ? null : (($_tmp = $_tmp->call(2) ?? null) === null ? null : ($_tmp->item ?? null))))))', + optionalChaining('$var?->prop?->elem?[1]?->call(2)?->item?') + ); }); test(function () { // not allowed Assert::same('$foo ?(hello)', optionalChaining('$foo?(hello)')); Assert::same('$foo->foo ?(hello)', optionalChaining('$foo->foo?(hello)')); - - Assert::same('$foo ?[1]', optionalChaining('$foo?[1]')); // not allowed due to collision with short ternary });