1: <?php
2:
3: /**
4: * Inheritance Runtime Methods processBlock, endChild, init
5: *
6: * @package Smarty
7: * @subpackage PluginsInternal
8: * @author Uwe Tews
9: **/
10: class 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->child->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: