1<?php 2 3/** 4 * Inheritance Runtime Methods processBlock, endChild, init 5 * 6 * @package Smarty 7 * @subpackage PluginsInternal 8 * @author Uwe Tews 9 **/ 10class Smarty_Internal_Runtime_Inheritance 11{ 12 /** 13 * State machine 14 * - 0 idle next extends will create a new inheritance tree 15 * - 1 processing child template 16 * - 2 wait for next inheritance template 17 * - 3 assume parent template, if child will loaded goto state 1 18 * a call to a sub template resets the state to 0 19 * 20 * @var int 21 */ 22 public $state = 0; 23 24 /** 25 * Array of root child {block} objects 26 * 27 * @var Smarty_Internal_Block[] 28 */ 29 public $childRoot = array(); 30 31 /** 32 * inheritance template nesting level 33 * 34 * @var int 35 */ 36 public $inheritanceLevel = 0; 37 38 /** 39 * inheritance template index 40 * 41 * @var int 42 */ 43 public $tplIndex = -1; 44 45 /** 46 * Array of template source objects 47 * 48 * @var Smarty_Template_Source[] 49 */ 50 public $sources = array(); 51 52 /** 53 * Stack of source objects while executing block code 54 * 55 * @var Smarty_Template_Source[] 56 */ 57 public $sourceStack = array(); 58 59 /** 60 * Initialize inheritance 61 * 62 * @param \Smarty_Internal_Template $tpl template object of caller 63 * @param bool $initChild if true init for child template 64 * @param array $blockNames outer level block name 65 */ 66 public function init(Smarty_Internal_Template $tpl, $initChild, $blockNames = array()) 67 { 68 // if called while executing parent template it must be a sub-template with new inheritance root 69 if ($initChild && $this->state === 3 && (strpos($tpl->template_resource, 'extendsall') === false)) { 70 $tpl->inheritance = new Smarty_Internal_Runtime_Inheritance(); 71 $tpl->inheritance->init($tpl, $initChild, $blockNames); 72 return; 73 } 74 ++$this->tplIndex; 75 $this->sources[ $this->tplIndex ] = $tpl->source; 76 // start of child sub template(s) 77 if ($initChild) { 78 $this->state = 1; 79 if (!$this->inheritanceLevel) { 80 //grab any output of child templates 81 ob_start(); 82 } 83 ++$this->inheritanceLevel; 84 // $tpl->startRenderCallbacks[ 'inheritance' ] = array($this, 'subTemplateStart'); 85 // $tpl->endRenderCallbacks[ 'inheritance' ] = array($this, 'subTemplateEnd'); 86 } 87 // if state was waiting for parent change state to parent 88 if ($this->state === 2) { 89 $this->state = 3; 90 } 91 } 92 93 /** 94 * End of child template(s) 95 * - if outer level is reached flush output buffer and switch to wait for parent template state 96 * 97 * @param \Smarty_Internal_Template $tpl 98 * @param null|string $template optional name of inheritance parent template 99 * @param null|string $uid uid of inline template 100 * @param null|string $func function call name of inline template 101 * 102 * @throws \Exception 103 * @throws \SmartyException 104 */ 105 public function endChild(Smarty_Internal_Template $tpl, $template = null, $uid = null, $func = null) 106 { 107 --$this->inheritanceLevel; 108 if (!$this->inheritanceLevel) { 109 ob_end_clean(); 110 $this->state = 2; 111 } 112 if (isset($template) && (($tpl->parent->_isTplObj() && $tpl->parent->source->type !== 'extends') 113 || $tpl->smarty->extends_recursion) 114 ) { 115 $tpl->_subTemplateRender( 116 $template, 117 $tpl->cache_id, 118 $tpl->compile_id, 119 $tpl->caching ? 9999 : 0, 120 $tpl->cache_lifetime, 121 array(), 122 2, 123 false, 124 $uid, 125 $func 126 ); 127 } 128 } 129 130 /** 131 * Smarty_Internal_Block constructor. 132 * - if outer level {block} of child template ($state === 1) save it as child root block 133 * - otherwise process inheritance and render 134 * 135 * @param \Smarty_Internal_Template $tpl 136 * @param $className 137 * @param string $name 138 * @param int|null $tplIndex index of outer level {block} if nested 139 * 140 * @throws \SmartyException 141 */ 142 public function instanceBlock(Smarty_Internal_Template $tpl, $className, $name, $tplIndex = null) 143 { 144 $block = new $className($name, isset($tplIndex) ? $tplIndex : $this->tplIndex); 145 if (isset($this->childRoot[ $name ])) { 146 $block->child = $this->childRoot[ $name ]; 147 } 148 if ($this->state === 1) { 149 $this->childRoot[ $name ] = $block; 150 return; 151 } 152 // make sure we got child block of child template of current block 153 while ($block->child && $block->tplIndex <= $block->child->tplIndex) { 154 $block->child = $block->child->child; 155 } 156 $this->process($tpl, $block); 157 } 158 159 /** 160 * Goto child block or render this 161 * 162 * @param \Smarty_Internal_Template $tpl 163 * @param \Smarty_Internal_Block $block 164 * @param \Smarty_Internal_Block|null $parent 165 * 166 * @throws \SmartyException 167 */ 168 public function process( 169 Smarty_Internal_Template $tpl, 170 Smarty_Internal_Block $block, 171 Smarty_Internal_Block $parent = null 172 ) { 173 if ($block->hide && !isset($block->child)) { 174 return; 175 } 176 if (isset($block->child) && $block->child->hide && !isset($block->child->child)) { 177 $block->child = null; 178 } 179 $block->parent = $parent; 180 if ($block->append && !$block->prepend && isset($parent)) { 181 $this->callParent($tpl, $block, '\'{block append}\''); 182 } 183 if ($block->callsChild || !isset($block->child) || ($block->child->hide && !isset($block->child->child))) { 184 $this->callBlock($block, $tpl); 185 } else { 186 $this->process($tpl, $block->child, $block); 187 } 188 if ($block->prepend && isset($parent)) { 189 $this->callParent($tpl, $block, '{block prepend}'); 190 if ($block->append) { 191 if ($block->callsChild || !isset($block->child) 192 || ($block->child->hide && !isset($block->child->child)) 193 ) { 194 $this->callBlock($block, $tpl); 195 } else { 196 $this->process($tpl, $block->child, $block); 197 } 198 } 199 } 200 $block->parent = null; 201 } 202 203 /** 204 * Render child on \$smarty.block.child 205 * 206 * @param \Smarty_Internal_Template $tpl 207 * @param \Smarty_Internal_Block $block 208 * 209 * @return null|string block content 210 * @throws \SmartyException 211 */ 212 public function callChild(Smarty_Internal_Template $tpl, Smarty_Internal_Block $block) 213 { 214 if (isset($block->child)) { 215 $this->process($tpl, $block->child, $block); 216 } 217 } 218 219 /** 220 * Render parent block on \$smarty.block.parent or {block append/prepend} 221 * 222 * @param \Smarty_Internal_Template $tpl 223 * @param \Smarty_Internal_Block $block 224 * @param string $tag 225 * 226 * @return null|string block content 227 * @throws \SmartyException 228 */ 229 public function callParent(Smarty_Internal_Template $tpl, Smarty_Internal_Block $block, $tag) 230 { 231 if (isset($block->parent)) { 232 $this->callBlock($block->parent, $tpl); 233 } else { 234 throw new SmartyException("inheritance: illegal '{$tag}' used in child template '{$tpl->inheritance->sources[$block->tplIndex]->filepath}' block '{$block->name}'"); 235 } 236 } 237 238 /** 239 * render block 240 * 241 * @param \Smarty_Internal_Block $block 242 * @param \Smarty_Internal_Template $tpl 243 */ 244 public function callBlock(Smarty_Internal_Block $block, Smarty_Internal_Template $tpl) 245 { 246 $this->sourceStack[] = $tpl->source; 247 $tpl->source = $this->sources[ $block->tplIndex ]; 248 $block->callBlock($tpl); 249 $tpl->source = array_pop($this->sourceStack); 250 } 251} 252