Skip to content

Commit

Permalink
Fix lambda resolution in dotted names
Browse files Browse the repository at this point in the history
Fixes #415
  • Loading branch information
bobthecow committed May 13, 2024
1 parent 92076e6 commit 1423c75
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 15 deletions.
45 changes: 33 additions & 12 deletions src/Mustache/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ private function block($nodes)
}

const SECTION_CALL = '
$value = $context->%s(%s);%s
$value = $context->%s(%s%s);%s
$buffer .= $this->section%s($context, $indent, $value);
';

Expand Down Expand Up @@ -390,13 +390,14 @@ private function section($nodes, $id, $filters, $start, $end, $otag, $ctag, $lev

$method = $this->getFindMethod($id);
$id = var_export($id, true);
$findArg = $this->getFindMethodArgs($method);
$filters = $this->getFilters($filters, $level);

return sprintf($this->prepare(self::SECTION_CALL, $level), $method, $id, $filters, $key);
return sprintf($this->prepare(self::SECTION_CALL, $level), $method, $id, $findArg, $filters, $key);
}

const INVERTED_SECTION = '
$value = $context->%s(%s);%s
$value = $context->%s(%s%s);%s
if (empty($value)) {
%s
}
Expand All @@ -416,12 +417,13 @@ private function invertedSection($nodes, $id, $filters, $level)
{
$method = $this->getFindMethod($id);
$id = var_export($id, true);
$findArg = $this->getFindMethodArgs($method);
$filters = $this->getFilters($filters, $level);

return sprintf($this->prepare(self::INVERTED_SECTION, $level), $method, $id, $filters, $this->walk($nodes, $level));
return sprintf($this->prepare(self::INVERTED_SECTION, $level), $method, $id, $findArg, $filters, $this->walk($nodes, $level));
}

const DYNAMIC_NAME = '$this->resolveValue($context->%s(%s), $context)';
const DYNAMIC_NAME = '$this->resolveValue($context->%s(%s%s), $context)';

/**
* Generate Mustache Template dynamic name resolution PHP source.
Expand All @@ -437,12 +439,13 @@ private function resolveDynamicName($id, $dynamic)
return var_export($id, true);
}

$method = $this->getFindMethod($id);
$id = ($method !== 'last') ? var_export($id, true) : '';
$method = $this->getFindMethod($id);
$id = ($method !== 'last') ? var_export($id, true) : '';
$findArg = $this->getFindMethodArgs($method);

// TODO: filters?
return sprintf(self::DYNAMIC_NAME, $method, $id);
return sprintf(self::DYNAMIC_NAME, $method, $id, $findArg);
}

const PARTIAL_INDENT = ', $indent . %s';
Expand Down Expand Up @@ -532,7 +535,7 @@ private static function onlyBlockArgs(array $node)
}

const VARIABLE = '
$value = $this->resolveValue($context->%s(%s), $context);%s
$value = $this->resolveValue($context->%s(%s%s), $context);%s
$buffer .= %s($value === null ? \'\' : %s);
';

Expand All @@ -550,14 +553,15 @@ private function variable($id, $filters, $escape, $level)
{
$method = $this->getFindMethod($id);
$id = ($method !== 'last') ? var_export($id, true) : '';
$findArg = $this->getFindMethodArgs($method);
$filters = $this->getFilters($filters, $level);
$value = $escape ? $this->getEscape() : '$value';

return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value);
return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $findArg, $filters, $this->flushIndent(), $value);
}

const FILTER = '
$filter = $context->%s(%s);
$filter = $context->%s(%s%s);
if (!(%s)) {
throw new Mustache_Exception_UnknownFilterException(%s);
}
Expand All @@ -581,10 +585,11 @@ private function getFilters(array $filters, $level)
$name = array_shift($filters);
$method = $this->getFindMethod($name);
$filter = ($method !== 'last') ? var_export($name, true) : '';
$findArg = $this->getFindMethodArgs($method);
$callable = $this->getCallable('$filter');
$msg = var_export($name, true);

return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilters($filters, $level));
return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $findArg, $callable, $msg, $this->getFilters($filters, $level));
}

const LINE = '$buffer .= "\n";';
Expand Down Expand Up @@ -681,6 +686,22 @@ private function getFindMethod($id)
return 'findDot';
}

/**
* Get the args needed for a given find method.
*
* In this case, it's "true" iff it's a "find dot" method and strict callables is enabled.
*
* @param string $method Find method name
*/
private function getFindMethodArgs($method)
{
if (($method === 'findDot' || $method === 'findAnchoredDot') && $this->strictCallables) {
return ', true';
}

return '';
}

const IS_CALLABLE = '!is_string(%s) && is_callable(%s)';
const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)';

Expand Down
16 changes: 13 additions & 3 deletions src/Mustache/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,28 @@ public function find($id)
* ... the `name` value is only searched for within the `child` value of the global Context, not within parent
* Context frames.
*
* @param string $id Dotted variable selector
* @param string $id Dotted variable selector
* @param bool $strictCallables (default: false)
*
* @return mixed Variable value, or '' if not found
*/
public function findDot($id)
public function findDot($id, $strictCallables = false)
{
$chunks = explode('.', $id);
$first = array_shift($chunks);
$value = $this->findVariableInStack($first, $this->stack);

// This wasn't really a dotted name, so we can just return the value.
if (empty($chunks)) {
return $value;
}

foreach ($chunks as $chunk) {
if ($value === '') {
$isCallable = $strictCallables ? (is_object($value) && is_callable($value)) : (!is_string($value) && is_callable($value));

if ($isCallable) {
$value = $value();
} elseif ($value === '') {
return $value;
}

Expand Down

0 comments on commit 1423c75

Please sign in to comment.