Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Facades/Antlers.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* @method static Parser parser()
* @method static mixed usingParser(Parser $parser, \Closure $callback)
* @method static AntlersString parse(string $str, array $variables = [])
* @method static AntlersString parse(string $str, array $variables = [], bool $php = false)
* @method static AntlersString parseUserContent(string $str, array $variables = [])
* @method static string parseLoop(string $content, array $data, bool $supplement = true, array $context = [])
* @method static array identifiers(string $content)
Expand Down
2 changes: 1 addition & 1 deletion src/Facades/Endpoint/Parse.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Parse
*/
public function template($str, $variables = [], $context = [], $php = false)
{
return Antlers::parse($str, $variables, $context, $php);
return Antlers::parse($str, array_merge($variables, $context), $php);
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Providers/ViewServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ private function registerAntlers()
$runtimeConfig->guardedContentVariablePatterns = config('statamic.antlers.guardedContentVariables', []);
$runtimeConfig->guardedContentTagPatterns = config('statamic.antlers.guardedContentTags', []);
$runtimeConfig->guardedContentModifiers = config('statamic.antlers.guardedContentModifiers', []);
$runtimeConfig->isPhpEnabled = config('statamic.antlers.allowPhp', true);
$runtimeConfig->allowPhpInUserContent = config('statamic.antlers.allowPhpInContent', false);
$runtimeConfig->allowMethodsInUserContent = config('statamic.antlers.allowMethodsInContent', false);

Expand Down
12 changes: 10 additions & 2 deletions src/View/Antlers/Antlers.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@ public function usingParser(Parser $parser, Closure $callback)
return $contents;
}

public function parse($str, $variables = [])
public function parse($str, $variables = [], $php = false)
{
return $this->parser()->parse($str, $variables);
$parser = $this->parser();
$previousState = GlobalRuntimeState::$isPhpEnabled;
GlobalRuntimeState::$isPhpEnabled = $php;

try {
return $parser->parse($str, $variables);
} finally {
GlobalRuntimeState::$isPhpEnabled = $previousState;
}
}

public function parseUserContent($str, $variables = [])
Expand Down
8 changes: 8 additions & 0 deletions src/View/Antlers/Language/Runtime/GlobalRuntimeState.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ public static function mergeTagRuntimeAssignments($assignments)
*/
public static $allowPhpInContent = false;

/**
* Controls whether PHP execution is globally enabled.
*
* @var bool
*/
public static $isPhpEnabled = true;

/**
* Controls if method invocations are evaluated in user content.
*
Expand Down Expand Up @@ -273,6 +280,7 @@ public static function resetGlobalState()
self::$abandonedNodes = [];
self::$isEvaluatingUserData = false;
self::$isEvaluatingData = false;
self::$isPhpEnabled = true;
self::$userContentEvalState = null;

StackReplacementManager::clearStackState();
Expand Down
14 changes: 13 additions & 1 deletion src/View/Antlers/Language/Runtime/NodeProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,10 @@ public function reduce($processNodes)
}

if ($node instanceof PhpExecutionNode) {
if (! GlobalRuntimeState::$isPhpEnabled) {
continue;
}

if (GlobalRuntimeState::$isEvaluatingUserData && ! GlobalRuntimeState::$allowPhpInContent) {
if (GlobalRuntimeState::$throwErrorOnAccessViolation) {
throw ErrorFactory::makeRuntimeError(
Expand Down Expand Up @@ -2453,7 +2457,7 @@ public function reduce($processNodes)
// one last time to make sure we didn't miss anything.
$this->stopMeasuringTag();

if ($this->allowPhp) {
if ($this->allowPhp && GlobalRuntimeState::$isPhpEnabled) {
$buffer = $this->evaluatePhp($buffer);
}

Expand All @@ -2468,6 +2472,10 @@ public function reduce($processNodes)
*/
protected function evaluatePhp($buffer)
{
if (! GlobalRuntimeState::$isPhpEnabled) {
return is_array($buffer) ? $buffer : StringUtilities::sanitizePhp($buffer);
}

if (is_array($buffer) || $this->isLoopable($buffer)) {
return $buffer;
}
Expand Down Expand Up @@ -2527,6 +2535,10 @@ protected function evaluateDirective(DirectiveNode $directive)

protected function evaluateAntlersPhpNode(PhpExecutionNode $node)
{
if (! GlobalRuntimeState::$isPhpEnabled) {
return '';
}

if (! GlobalRuntimeState::$allowPhpInContent && GlobalRuntimeState::$isEvaluatingUserData) {
return StringUtilities::sanitizePhp($node->content);
}
Expand Down
7 changes: 7 additions & 0 deletions src/View/Antlers/Language/Runtime/RuntimeConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ class RuntimeConfiguration
*/
public $guardedContentModifiers = [];

/**
* Controls whether PHP execution is globally enabled.
*
* @var bool
*/
public $isPhpEnabled = true;

/**
* Indicates if PHP Code should be evaluated in user content.
*
Expand Down
1 change: 1 addition & 0 deletions src/View/Antlers/Language/Runtime/RuntimeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public function __construct(DocumentParser $documentParser, NodeProcessor $nodeP
*/
public function setRuntimeConfiguration(RuntimeConfiguration $configuration)
{
GlobalRuntimeState::$isPhpEnabled = $configuration->isPhpEnabled;
GlobalRuntimeState::$allowPhpInContent = $configuration->allowPhpInUserContent;
GlobalRuntimeState::$allowMethodsInContent = $configuration->allowMethodsInUserContent;
GlobalRuntimeState::$throwErrorOnAccessViolation = $configuration->throwErrorOnAccessViolation;
Expand Down
51 changes: 51 additions & 0 deletions tests/Antlers/Runtime/PhpDisabledTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace Tests\Antlers\Runtime;

use Statamic\Facades\Antlers;
use Tests\TestCase;

class PhpDisabledTest extends TestCase
{
public function test_it_ignores_inline_php_blocks_when_disabled()
{
$result = (string) Antlers::parse('Before {{? echo "hello"; ?}} After', [], false);

$this->assertSame('Before After', $result);
}

public function test_it_ignores_inline_echo_blocks_when_disabled()
{
$result = (string) Antlers::parse('Before {{$ "hello" $}} After', []);

$this->assertSame('Before After', $result);
}

public function test_php_disabled_is_the_default()
{
$result = (string) Antlers::parse('Before {{? echo "hello"; ?}} After', []);

$this->assertSame('Before After', $result);
}

public function test_inline_php_tags_disabled_is_the_default()
{
$result = (string) Antlers::parse('Before <?php echo "hello"; ?> After', []);

$this->assertSame('Before &lt;?php echo "hello"; ?> After', $result);
}

public function test_it_allows_inline_echo_blocks_when_enabled()
{
$result = (string) Antlers::parse('Before {{$ "hello" $}} After', [], true);

$this->assertSame('Before hello After', $result);
}

public function test_it_allow_inline_php_blocks_when_enabled()
{
$result = (string) Antlers::parse('Before {{? echo "hello"; ?}} After', [], true);

$this->assertSame('Before hello After', $result);
}
}