Skip to content

Commit

Permalink
Allow backwards compatible null property behavior.
Browse files Browse the repository at this point in the history
Per the Mustache spec, keys of a value higher in the context stack shadow similarly named keys lower in the stack. For example, in the template `{{# foo }}{{ bar }}{{/ foo }}` if the value for `foo` has a method, property, or key named `bar`, it will prevent looking lower in the context stack for a another value named `bar`.

In Mustache.php, setting the value of an array key to null prevents lookups higher in the context stack. The behavior should have been identical for object properties (and ArrayAccess) as well, but a bug in the context lookup logic meant that a property which exists but is set to null would not prevent further context lookup.

This change adds a `buggy_property_shadowing` configuration option to preserve the behavior from Mustache.php <= 2.14.2, to ease the transition to the correct behavior.

See #410
  • Loading branch information
bobthecow committed May 10, 2024
1 parent ef5b784 commit 66ecb32
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 10 deletions.
32 changes: 23 additions & 9 deletions src/Mustache/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@ class Mustache_Context
private $stack = array();
private $blockStack = array();

private $buggyPropertyShadowing = false;

/**
* Mustache rendering Context constructor.
*
* @param mixed $context Default rendering context (default: null)
* @param mixed $context Default rendering context (default: null)
* @param bool $buggyPropertyShadowing See Mustache_Engine::useBuggyPropertyShadowing (default: false)
*/
public function __construct($context = null)
public function __construct($context = null, $buggyPropertyShadowing = false)
{
if ($context !== null) {
$this->stack = array($context);
}

$this->buggyPropertyShadowing = $buggyPropertyShadowing;
}

/**
Expand Down Expand Up @@ -223,15 +228,24 @@ private function findVariableInStack($id, array $stack)
return $frame->$id;
}

if (property_exists($frame, $id)) {
$rp = new \ReflectionProperty($frame, $id);
if ($rp->isPublic()) {
return $frame->$id;
// Preserve backwards compatibility with a property shadowing bug in
// Mustache.php <= 2.14.2
// See https://github.com/bobthecow/mustache.php/pull/410
if ($this->buggyPropertyShadowing) {
if ($frame instanceof ArrayAccess && isset($frame[$id])) {
return $frame[$id];
}
} else {
if (property_exists($frame, $id)) {
$rp = new \ReflectionProperty($frame, $id);
if ($rp->isPublic()) {
return $frame->$id;
}
}
}

if ($frame instanceof ArrayAccess && isset($frame[$id])) {
return $frame[$id];
if ($frame instanceof ArrayAccess && $frame->offsetExists($id)) {
return $frame[$id];
}
}
}
break;
Expand Down
15 changes: 15 additions & 0 deletions src/Mustache/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class Mustache_Engine
private $strictCallables = false;
private $pragmas = array();
private $delimiters;
private $buggyPropertyShadowing = false;

// Services
private $tokenizer;
Expand Down Expand Up @@ -218,6 +219,10 @@ public function __construct(array $options = array())
$this->pragmas[$pragma] = true;
}
}

if (isset($options['buggy_property_shadowing'])) {
$this->buggyPropertyShadowing = (bool) $options['buggy_property_shadowing'];
}
}

/**
Expand Down Expand Up @@ -268,6 +273,16 @@ public function getCharset()
return $this->charset;
}

/**
* Check whether to use buggy property shadowing.
*
* See https://github.com/bobthecow/mustache.php/pull/410
*/
public function useBuggyPropertyShadowing()
{
return $this->buggyPropertyShadowing;
}

/**
* Get the current globally enabled pragmas.
*
Expand Down
2 changes: 1 addition & 1 deletion src/Mustache/Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ protected function isIterable($value)
*/
protected function prepareContextStack($context = null)
{
$stack = new Mustache_Context();
$stack = new Mustache_Context(null, $this->mustache->useBuggyPropertyShadowing());

$helpers = $this->mustache->getHelpers();
if (!$helpers->isEmpty()) {
Expand Down
9 changes: 9 additions & 0 deletions test/Mustache/Test/EngineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,15 @@ public function testCustomDelimiters()
$tpl = $mustache->loadTemplate('[[> one ]]');
$this->assertEquals('b', $tpl->render(array('a' => 'b')));
}

public function testBuggyPropertyShadowing()
{
$mustache = new Mustache_Engine();
$this->assertFalse($mustache->useBuggyPropertyShadowing());

$mustache = new Mustache_Engine(array('buggy_property_shadowing' => true));
$this->assertTrue($mustache->useBuggyPropertyShadowing());
}
}

class MustacheStub extends Mustache_Engine
Expand Down

0 comments on commit 66ecb32

Please sign in to comment.