Skip to content

Commit abcf18f

Browse files
[6.x] Allow PHP Tags in Antlers to Override any Variable (#11802)
1 parent 543c00c commit abcf18f

2 files changed

Lines changed: 124 additions & 22 deletions

File tree

src/View/Antlers/Language/Runtime/NodeProcessor.php

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,51 +2454,53 @@ protected function evaluatePhp($buffer)
24542454
return ob_get_clean();
24552455
}
24562456

2457-
protected function evaluateAntlersPhpNode(PhpExecutionNode $node)
2457+
protected function evaluateAntlersPhpNode(PhpExecutionNode $___node)
24582458
{
24592459
if (! GlobalRuntimeState::$allowPhpInContent == false && GlobalRuntimeState::$isEvaluatingUserData) {
2460-
return StringUtilities::sanitizePhp($node->content);
2460+
return StringUtilities::sanitizePhp($___node->content);
24612461
}
24622462

2463-
$phpBuffer = '';
2463+
$___phpBuffer = '';
24642464

2465-
if ($node->isEchoNode == false) {
2466-
$phpBuffer = $node->content;
2465+
if ($___node->isEchoNode == false) {
2466+
$___phpBuffer = $___node->content;
24672467

2468-
if (! Str::contains($node->content, $this->validPhpOpenTags)) {
2469-
$phpBuffer = '<?php '.$node->content.' ?>';
2468+
if (! Str::contains($___node->content, $this->validPhpOpenTags)) {
2469+
$___phpBuffer = '<?php '.$___node->content.' ?>';
24702470
}
24712471
} else {
2472-
$phpBuffer = '<?php echo '.$node->content.'; ?>';
2472+
$___phpBuffer = '<?php echo '.$___node->content.'; ?>';
24732473
}
24742474

2475-
$phpRuntimeAssignments = [];
2475+
$___phpRuntimeAssignments = [];
24762476
ob_start();
24772477

24782478
try {
24792479
extract($this->getActiveData());
24802480
$___antlersVarBefore = get_defined_vars();
2481-
eval('?>'.$phpBuffer.'<?php ');
2481+
eval('?>'.$___phpBuffer.'<?php ');
24822482
$___antlersPhpExecutionResult = ob_get_clean();
24832483

2484-
if (! $node->isEchoNode) {
2484+
if (! $___node->isEchoNode) {
24852485
$___antlersVarAfter = get_defined_vars();
24862486

2487-
foreach ($___antlersVarAfter as $varKey => $varValue) {
2488-
if (! array_key_exists($varKey, $___antlersVarBefore) || array_key_exists($varKey, $this->previousAssignments) || array_key_exists($varKey, $this->runtimeAssignments)) {
2489-
$phpRuntimeAssignments[$varKey] = $varValue;
2487+
foreach ($___antlersVarAfter as $___varKey => $___varValue) {
2488+
if (str_starts_with($___varKey, '___')) {
2489+
continue;
24902490
}
2491+
2492+
$___phpRuntimeAssignments[$___varKey] = $___varValue;
24912493
}
24922494
}
24932495
} catch (ParseError $e) {
2494-
throw new SyntaxError("{$e->getMessage()} on line {$e->getLine()} of:\n\n{$phpBuffer}");
2496+
throw new SyntaxError("{$e->getMessage()} on line {$e->getLine()} of:\n\n{$___phpBuffer}");
24952497
}
24962498

2497-
if (! $node->isEchoNode && ! empty($phpRuntimeAssignments)) {
2498-
unset($phpRuntimeAssignments['___antlersVarBefore']);
2499-
unset($phpRuntimeAssignments['___antlersPhpExecutionResult']);
2499+
if (! $___node->isEchoNode && ! empty($___phpRuntimeAssignments)) {
2500+
unset($___phpRuntimeAssignments['___antlersVarBefore']);
2501+
unset($___phpRuntimeAssignments['___antlersPhpExecutionResult']);
25002502

2501-
$this->processAssignments($phpRuntimeAssignments);
2503+
$this->processAssignments($___phpRuntimeAssignments);
25022504
}
25032505

25042506
return $___antlersPhpExecutionResult;

tests/Antlers/Runtime/PhpEnabledTest.php

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
use Statamic\View\Antlers\Language\Utilities\StringUtilities;
1010
use Tests\Antlers\ParserTestCase;
1111
use Tests\Factories\EntryFactory;
12+
use Tests\FakesViews;
1213
use Tests\PreventSavingStacheItemsToDisk;
1314

1415
class PhpEnabledTest extends ParserTestCase
1516
{
16-
use PreventSavingStacheItemsToDisk;
17+
use FakesViews,
18+
PreventSavingStacheItemsToDisk;
1719

1820
public function test_php_has_access_to_scope_data()
1921
{
@@ -513,8 +515,8 @@ public function test_php_node_assignments_within_loops()
513515
public function test_assignments_from_php_nodes()
514516
{
515517
$template = <<<'EOT'
516-
{{?
517-
$value_one = 100;
518+
{{?
519+
$value_one = 100;
518520
$value_two = 0;
519521
?}}
520522
@@ -533,4 +535,102 @@ public function test_assignments_from_php_nodes()
533535
$this->assertStringContainsString('<value_one: 1125>', $result);
534536
$this->assertStringContainsString('<value_two: 1025>', $result);
535537
}
538+
539+
public function test_updating_variables_within_scope_using_php()
540+
{
541+
$data = [
542+
'blocks' => [
543+
[
544+
'type' => 'the_block',
545+
],
546+
],
547+
];
548+
549+
$outerPartial = <<<'EOT'
550+
Outer Partial Before: {{ view.blocks }}{{ type }}{{ /view.blocks }}
551+
{{ partial:inner_partial :blocks="blocks" /}}
552+
Outer Partial After: {{ view.blocks }}{{ type }}{{ /view.blocks }}
553+
EOT;
554+
555+
$innerPartial = <<<'EOT'
556+
Inner Partial Before: {{ view.blocks }}{{ type }} {{ /view.blocks }}
557+
558+
{{ if view.blocks.0 && view.blocks.0.type != 'hero_block' }}
559+
{{?
560+
array_unshift($view['blocks'], [
561+
'type' => 'hero_block',
562+
'simple_bard_field' => [
563+
'type' => 'text',
564+
'text' => 'The Text',
565+
],
566+
]);
567+
?}}
568+
{{ /if }}
569+
570+
Inner Partial After: {{ view.blocks }}{{ type }} {{ /view.blocks }}
571+
EOT;
572+
573+
$this->withFakeViews();
574+
$this->viewShouldReturnRaw('outer_partial', $outerPartial);
575+
$this->viewShouldReturnRaw('inner_partial', $innerPartial);
576+
577+
$expected = <<<'EXPECTED'
578+
Outer Partial Before: the_block
579+
Inner Partial Before: the_block
580+
581+
582+
583+
584+
Inner Partial After: hero_block the_block
585+
Outer Partial After: the_block
586+
EXPECTED;
587+
588+
$this->assertSame(
589+
(string) str($expected)->squish(),
590+
(string) str($this->renderString('{{ partial:outer_partial :blocks="blocks" /}}', $data))->squish(),
591+
);
592+
}
593+
594+
public function test_variables_created_inside_php_do_not_override_injected_values()
595+
{
596+
$this->withFakeViews();
597+
598+
$partial = <<<'EOT'
599+
{{? $title = 'The Title'; ?}}
600+
601+
Partial: {{ title }}
602+
EOT;
603+
604+
$this->viewShouldReturnRaw('the_partial', $partial);
605+
606+
$template = <<<'EOT'
607+
Before: {{ title }}
608+
{{ partial:the_partial /}}
609+
After: {{ title }}
610+
EOT;
611+
612+
$expected = <<<'EXPECTED'
613+
Before: The Original Title
614+
615+
616+
Partial: The Title
617+
After: The Original Title
618+
EXPECTED;
619+
620+
$this->assertSame(
621+
$expected,
622+
$this->renderString($template, ['title' => 'The Original Title']),
623+
);
624+
625+
$template = <<<'EOT'
626+
Before: {{ title }}
627+
{{ partial:the_partial :title="title" /}}
628+
After: {{ title }}
629+
EOT;
630+
631+
$this->assertSame(
632+
$expected,
633+
$this->renderString($template, ['title' => 'The Original Title']),
634+
);
635+
}
536636
}

0 commit comments

Comments
 (0)