1<?php
2
3/**
4 * Runtime Extension Capture
5 *
6 * @package    Smarty
7 * @subpackage PluginsInternal
8 * @author     Uwe Tews
9 */
10class Smarty_Internal_Runtime_Capture
11{
12    /**
13     * Flag that this instance  will not be cached
14     *
15     * @var bool
16     */
17    public $isPrivateExtension = true;
18
19    /**
20     * Stack of capture parameter
21     *
22     * @var array
23     */
24    private $captureStack = array();
25
26    /**
27     * Current open capture sections
28     *
29     * @var int
30     */
31    private $captureCount = 0;
32
33    /**
34     * Count stack
35     *
36     * @var int[]
37     */
38    private $countStack = array();
39
40    /**
41     * Named buffer
42     *
43     * @var string[]
44     */
45    private $namedBuffer = array();
46
47    /**
48     * Flag if callbacks are registered
49     *
50     * @var bool
51     */
52    private $isRegistered = false;
53
54    /**
55     * Open capture section
56     *
57     * @param \Smarty_Internal_Template $_template
58     * @param string                    $buffer capture name
59     * @param string                    $assign variable name
60     * @param string                    $append variable name
61     */
62    public function open(Smarty_Internal_Template $_template, $buffer, $assign, $append)
63    {
64        if (!$this->isRegistered) {
65            $this->register($_template);
66        }
67        $this->captureStack[] = array(
68            $buffer,
69            $assign,
70            $append
71        );
72        $this->captureCount++;
73        ob_start();
74    }
75
76    /**
77     * Register callbacks in template class
78     *
79     * @param \Smarty_Internal_Template $_template
80     */
81    private function register(Smarty_Internal_Template $_template)
82    {
83        $_template->startRenderCallbacks[] = array(
84            $this,
85            'startRender'
86        );
87        $_template->endRenderCallbacks[] = array(
88            $this,
89            'endRender'
90        );
91        $this->startRender($_template);
92        $this->isRegistered = true;
93    }
94
95    /**
96     * Start render callback
97     *
98     * @param \Smarty_Internal_Template $_template
99     */
100    public function startRender(Smarty_Internal_Template $_template)
101    {
102        $this->countStack[] = $this->captureCount;
103        $this->captureCount = 0;
104    }
105
106    /**
107     * Close capture section
108     *
109     * @param \Smarty_Internal_Template $_template
110     *
111     * @throws \SmartyException
112     */
113    public function close(Smarty_Internal_Template $_template)
114    {
115        if ($this->captureCount) {
116            list($buffer, $assign, $append) = array_pop($this->captureStack);
117            $this->captureCount--;
118            if (isset($assign)) {
119                $_template->assign($assign, ob_get_contents());
120            }
121            if (isset($append)) {
122                $_template->append($append, ob_get_contents());
123            }
124            $this->namedBuffer[ $buffer ] = ob_get_clean();
125        } else {
126            $this->error($_template);
127        }
128    }
129
130    /**
131     * Error exception on not matching {capture}{/capture}
132     *
133     * @param \Smarty_Internal_Template $_template
134     *
135     * @throws \SmartyException
136     */
137    public function error(Smarty_Internal_Template $_template)
138    {
139        throw new SmartyException("Not matching {capture}{/capture} in '{$_template->template_resource}'");
140    }
141
142    /**
143     * Return content of named capture buffer by key or as array
144     *
145     * @param \Smarty_Internal_Template $_template
146     * @param string|null               $name
147     *
148     * @return string|string[]|null
149     */
150    public function getBuffer(Smarty_Internal_Template $_template, $name = null)
151    {
152        if (isset($name)) {
153            return isset($this->namedBuffer[ $name ]) ? $this->namedBuffer[ $name ] : null;
154        } else {
155            return $this->namedBuffer;
156        }
157    }
158
159    /**
160     * End render callback
161     *
162     * @param \Smarty_Internal_Template $_template
163     *
164     * @throws \SmartyException
165     */
166    public function endRender(Smarty_Internal_Template $_template)
167    {
168        if ($this->captureCount) {
169            $this->error($_template);
170        } else {
171            $this->captureCount = array_pop($this->countStack);
172        }
173    }
174}
175