1<?php 2 3/* 4 * This file is part of Mustache.php. 5 * 6 * (c) 2010-2017 Justin Hileman 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12/** 13 * Abstract Mustache Template class. 14 * 15 * @abstract 16 */ 17abstract class Mustache_Template 18{ 19 /** 20 * @var Mustache_Engine 21 */ 22 protected $mustache; 23 24 /** 25 * @var bool 26 */ 27 protected $strictCallables = false; 28 29 /** 30 * Mustache Template constructor. 31 * 32 * @param Mustache_Engine $mustache 33 */ 34 public function __construct(Mustache_Engine $mustache) 35 { 36 $this->mustache = $mustache; 37 } 38 39 /** 40 * Mustache Template instances can be treated as a function and rendered by simply calling them. 41 * 42 * $m = new Mustache_Engine; 43 * $tpl = $m->loadTemplate('Hello, {{ name }}!'); 44 * echo $tpl(array('name' => 'World')); // "Hello, World!" 45 * 46 * @see Mustache_Template::render 47 * 48 * @param mixed $context Array or object rendering context (default: array()) 49 * 50 * @return string Rendered template 51 */ 52 public function __invoke($context = array()) 53 { 54 return $this->render($context); 55 } 56 57 /** 58 * Render this template given the rendering context. 59 * 60 * @param mixed $context Array or object rendering context (default: array()) 61 * 62 * @return string Rendered template 63 */ 64 public function render($context = array()) 65 { 66 return $this->renderInternal( 67 $this->prepareContextStack($context) 68 ); 69 } 70 71 /** 72 * Internal rendering method implemented by Mustache Template concrete subclasses. 73 * 74 * This is where the magic happens :) 75 * 76 * NOTE: This method is not part of the Mustache.php public API. 77 * 78 * @param Mustache_Context $context 79 * @param string $indent (default: '') 80 * 81 * @return string Rendered template 82 */ 83 abstract public function renderInternal(Mustache_Context $context, $indent = ''); 84 85 /** 86 * Tests whether a value should be iterated over (e.g. in a section context). 87 * 88 * In most languages there are two distinct array types: list and hash (or whatever you want to call them). Lists 89 * should be iterated, hashes should be treated as objects. Mustache follows this paradigm for Ruby, Javascript, 90 * Java, Python, etc. 91 * 92 * PHP, however, treats lists and hashes as one primitive type: array. So Mustache.php needs a way to distinguish 93 * between between a list of things (numeric, normalized array) and a set of variables to be used as section context 94 * (associative array). In other words, this will be iterated over: 95 * 96 * $items = array( 97 * array('name' => 'foo'), 98 * array('name' => 'bar'), 99 * array('name' => 'baz'), 100 * ); 101 * 102 * ... but this will be used as a section context block: 103 * 104 * $items = array( 105 * 1 => array('name' => 'foo'), 106 * 'banana' => array('name' => 'bar'), 107 * 42 => array('name' => 'baz'), 108 * ); 109 * 110 * @param mixed $value 111 * 112 * @return bool True if the value is 'iterable' 113 */ 114 protected function isIterable($value) 115 { 116 switch (gettype($value)) { 117 case 'object': 118 return $value instanceof Traversable; 119 120 case 'array': 121 $i = 0; 122 foreach ($value as $k => $v) { 123 if ($k !== $i++) { 124 return false; 125 } 126 } 127 128 return true; 129 130 default: 131 return false; 132 } 133 } 134 135 /** 136 * Helper method to prepare the Context stack. 137 * 138 * Adds the Mustache HelperCollection to the stack's top context frame if helpers are present. 139 * 140 * @param mixed $context Optional first context frame (default: null) 141 * 142 * @return Mustache_Context 143 */ 144 protected function prepareContextStack($context = null) 145 { 146 $stack = new Mustache_Context(); 147 148 $helpers = $this->mustache->getHelpers(); 149 if (!$helpers->isEmpty()) { 150 $stack->push($helpers); 151 } 152 153 if (!empty($context)) { 154 $stack->push($context); 155 } 156 157 return $stack; 158 } 159 160 /** 161 * Resolve a context value. 162 * 163 * Invoke the value if it is callable, otherwise return the value. 164 * 165 * @param mixed $value 166 * @param Mustache_Context $context 167 * 168 * @return string 169 */ 170 protected function resolveValue($value, Mustache_Context $context) 171 { 172 if (($this->strictCallables ? is_object($value) : !is_string($value)) && is_callable($value)) { 173 return $this->mustache 174 ->loadLambda((string) call_user_func($value)) 175 ->renderInternal($context); 176 } 177 178 return $value; 179 } 180} 181