1<?php
2/**
3 * Smarty Internal Plugin Debug
4 * Class to collect data for the Smarty Debugging Console
5 *
6 * @package    Smarty
7 * @subpackage Debug
8 * @author     Uwe Tews
9 */
10
11/**
12 * Smarty Internal Plugin Debug Class
13 *
14 * @package    Smarty
15 * @subpackage Debug
16 */
17class Smarty_Internal_Debug extends Smarty_Internal_Data
18{
19    /**
20     * template data
21     *
22     * @var array
23     */
24    public $template_data = array();
25
26    /**
27     * List of uid's which shall be ignored
28     *
29     * @var array
30     */
31    public $ignore_uid = array();
32
33    /**
34     * Index of display() and fetch() calls
35     *
36     * @var int
37     */
38    public $index = 0;
39
40    /**
41     * Counter for window offset
42     *
43     * @var int
44     */
45    public $offset = 0;
46
47    /**
48     * Start logging template
49     *
50     * @param \Smarty_Internal_Template $template template
51     * @param null                      $mode     true: display   false: fetch  null: subtemplate
52     */
53    public function start_template(Smarty_Internal_Template $template, $mode = null)
54    {
55        if (isset($mode) && !$template->_isSubTpl()) {
56            $this->index ++;
57            $this->offset ++;
58            $this->template_data[ $this->index ] = null;
59        }
60        $key = $this->get_key($template);
61        $this->template_data[ $this->index ][ $key ][ 'start_template_time' ] = microtime(true);
62    }
63
64    /**
65     * End logging of cache time
66     *
67     * @param \Smarty_Internal_Template $template cached template
68     */
69    public function end_template(Smarty_Internal_Template $template)
70    {
71        $key = $this->get_key($template);
72        $this->template_data[ $this->index ][ $key ][ 'total_time' ] +=
73            microtime(true) - $this->template_data[ $this->index ][ $key ][ 'start_template_time' ];
74        //$this->template_data[$this->index][$key]['properties'] = $template->properties;
75    }
76
77    /**
78     * Start logging of compile time
79     *
80     * @param \Smarty_Internal_Template $template
81     */
82    public function start_compile(Smarty_Internal_Template $template)
83    {
84        static $_is_stringy = array('string' => true, 'eval' => true);
85        if (!empty($template->compiler->trace_uid)) {
86            $key = $template->compiler->trace_uid;
87            if (!isset($this->template_data[ $this->index ][ $key ])) {
88                if (isset($_is_stringy[ $template->source->type ])) {
89                    $this->template_data[ $this->index ][ $key ][ 'name' ] =
90                        '\'' . substr($template->source->name, 0, 25) . '...\'';
91                } else {
92                    $this->template_data[ $this->index ][ $key ][ 'name' ] = $template->source->filepath;
93                }
94                $this->template_data[ $this->index ][ $key ][ 'compile_time' ] = 0;
95                $this->template_data[ $this->index ][ $key ][ 'render_time' ] = 0;
96                $this->template_data[ $this->index ][ $key ][ 'cache_time' ] = 0;
97            }
98        } else {
99            if (isset($this->ignore_uid[ $template->source->uid ])) {
100                return;
101            }
102            $key = $this->get_key($template);
103        }
104        $this->template_data[ $this->index ][ $key ][ 'start_time' ] = microtime(true);
105    }
106
107    /**
108     * End logging of compile time
109     *
110     * @param \Smarty_Internal_Template $template
111     */
112    public function end_compile(Smarty_Internal_Template $template)
113    {
114        if (!empty($template->compiler->trace_uid)) {
115            $key = $template->compiler->trace_uid;
116        } else {
117            if (isset($this->ignore_uid[ $template->source->uid ])) {
118                return;
119            }
120
121            $key = $this->get_key($template);
122        }
123        $this->template_data[ $this->index ][ $key ][ 'compile_time' ] +=
124            microtime(true) - $this->template_data[ $this->index ][ $key ][ 'start_time' ];
125    }
126
127    /**
128     * Start logging of render time
129     *
130     * @param \Smarty_Internal_Template $template
131     */
132    public function start_render(Smarty_Internal_Template $template)
133    {
134        $key = $this->get_key($template);
135        $this->template_data[ $this->index ][ $key ][ 'start_time' ] = microtime(true);
136    }
137
138    /**
139     * End logging of compile time
140     *
141     * @param \Smarty_Internal_Template $template
142     */
143    public function end_render(Smarty_Internal_Template $template)
144    {
145        $key = $this->get_key($template);
146        $this->template_data[ $this->index ][ $key ][ 'render_time' ] +=
147            microtime(true) - $this->template_data[ $this->index ][ $key ][ 'start_time' ];
148    }
149
150    /**
151     * Start logging of cache time
152     *
153     * @param \Smarty_Internal_Template $template cached template
154     */
155    public function start_cache(Smarty_Internal_Template $template)
156    {
157        $key = $this->get_key($template);
158        $this->template_data[ $this->index ][ $key ][ 'start_time' ] = microtime(true);
159    }
160
161    /**
162     * End logging of cache time
163     *
164     * @param \Smarty_Internal_Template $template cached template
165     */
166    public function end_cache(Smarty_Internal_Template $template)
167    {
168        $key = $this->get_key($template);
169        $this->template_data[ $this->index ][ $key ][ 'cache_time' ] +=
170            microtime(true) - $this->template_data[ $this->index ][ $key ][ 'start_time' ];
171    }
172
173    /**
174     * Register template object
175     *
176     * @param \Smarty_Internal_Template $template cached template
177     */
178    public function register_template(Smarty_Internal_Template $template)
179    {
180    }
181
182    /**
183     * Register data object
184     *
185     * @param \Smarty_Data $data data object
186     */
187    public static function register_data(Smarty_Data $data)
188    {
189    }
190
191    /**
192     * Opens a window for the Smarty Debugging Console and display the data
193     *
194     * @param Smarty_Internal_Template|Smarty $obj object to debug
195     * @param bool                            $full
196     */
197    public function display_debug($obj, $full = false)
198    {
199        if (!$full) {
200            $this->offset ++;
201            $savedIndex = $this->index;
202            $this->index = 9999;
203        }
204        $smarty = $obj->_getSmartyObj();
205        // create fresh instance of smarty for displaying the debug console
206        // to avoid problems if the application did overload the Smarty class
207        $debObj = new Smarty();
208        // copy the working dirs from application
209        $debObj->setCompileDir($smarty->getCompileDir());
210        // init properties by hand as user may have edited the original Smarty class
211        $debObj->setPluginsDir(is_dir(__DIR__ . '/../plugins') ? __DIR__ . '/../plugins' : $smarty->getPluginsDir());
212        $debObj->force_compile = false;
213        $debObj->compile_check = true;
214        $debObj->left_delimiter = '{';
215        $debObj->right_delimiter = '}';
216        $debObj->security_policy = null;
217        $debObj->debugging = false;
218        $debObj->debugging_ctrl = 'NONE';
219        $debObj->error_reporting = E_ALL & ~E_NOTICE;
220        $debObj->debug_tpl = isset($smarty->debug_tpl) ? $smarty->debug_tpl : 'file:' . __DIR__ . '/../debug.tpl';
221        $debObj->registered_plugins = array();
222        $debObj->registered_resources = array();
223        $debObj->registered_filters = array();
224        $debObj->autoload_filters = array();
225        $debObj->default_modifiers = array();
226        $debObj->escape_html = true;
227        $debObj->caching = false;
228        $debObj->compile_id = null;
229        $debObj->cache_id = null;
230        // prepare information of assigned variables
231        $ptr = $this->get_debug_vars($obj);
232        $_assigned_vars = $ptr->tpl_vars;
233        ksort($_assigned_vars);
234        $_config_vars = $ptr->config_vars;
235        ksort($_config_vars);
236        $debugging = $smarty->debugging;
237
238        $_template = new Smarty_Internal_Template($debObj->debug_tpl, $debObj);
239        if ($obj->_isTplObj()) {
240            $_template->assign('template_name', $obj->source->type . ':' . $obj->source->name);
241        }
242        if ($obj->_objType == 1 || $full) {
243            $_template->assign('template_data', $this->template_data[ $this->index ]);
244        } else {
245            $_template->assign('template_data', null);
246        }
247        $_template->assign('assigned_vars', $_assigned_vars);
248        $_template->assign('config_vars', $_config_vars);
249        $_template->assign('execution_time', microtime(true) - $smarty->start_time);
250        $_template->assign('display_mode', $debugging == 2 || !$full);
251        $_template->assign('offset', $this->offset * 50);
252        echo $_template->fetch();
253        if (isset($full)) {
254            $this->index --;
255        }
256        if (!$full) {
257            $this->index = $savedIndex;
258        }
259    }
260
261    /**
262     * Recursively gets variables from all template/data scopes
263     *
264     * @param  Smarty_Internal_Template|Smarty_Data $obj object to debug
265     *
266     * @return StdClass
267     */
268    public function get_debug_vars($obj)
269    {
270        $config_vars = array();
271        foreach ($obj->config_vars as $key => $var) {
272            $config_vars[ $key ][ 'value' ] = $var;
273            if ($obj->_isTplObj()) {
274                $config_vars[ $key ][ 'scope' ] = $obj->source->type . ':' . $obj->source->name;
275            } elseif ($obj->_isDataObj()) {
276                $tpl_vars[ $key ][ 'scope' ] = $obj->dataObjectName;
277            } else {
278                $config_vars[ $key ][ 'scope' ] = 'Smarty object';
279            }
280        }
281        $tpl_vars = array();
282        foreach ($obj->tpl_vars as $key => $var) {
283            foreach ($var as $varkey => $varvalue) {
284                if ($varkey == 'value') {
285                    $tpl_vars[ $key ][ $varkey ] = $varvalue;
286                } else {
287                    if ($varkey == 'nocache') {
288                        if ($varvalue == true) {
289                            $tpl_vars[ $key ][ $varkey ] = $varvalue;
290                        }
291                    } else {
292                        if ($varkey != 'scope' || $varvalue !== 0) {
293                            $tpl_vars[ $key ][ 'attributes' ][ $varkey ] = $varvalue;
294                        }
295                    }
296                }
297            }
298            if ($obj->_isTplObj()) {
299                $tpl_vars[ $key ][ 'scope' ] = $obj->source->type . ':' . $obj->source->name;
300            } elseif ($obj->_isDataObj()) {
301                $tpl_vars[ $key ][ 'scope' ] = $obj->dataObjectName;
302            } else {
303                $tpl_vars[ $key ][ 'scope' ] = 'Smarty object';
304            }
305        }
306
307        if (isset($obj->parent)) {
308            $parent = $this->get_debug_vars($obj->parent);
309            foreach ($parent->tpl_vars as $name => $pvar) {
310                if (isset($tpl_vars[ $name ]) && $tpl_vars[ $name ][ 'value' ] === $pvar[ 'value' ]) {
311                    $tpl_vars[ $name ][ 'scope' ] = $pvar[ 'scope' ];
312                }
313            }
314            $tpl_vars = array_merge($parent->tpl_vars, $tpl_vars);
315
316            foreach ($parent->config_vars as $name => $pvar) {
317                if (isset($config_vars[ $name ]) && $config_vars[ $name ][ 'value' ] === $pvar[ 'value' ]) {
318                    $config_vars[ $name ][ 'scope' ] = $pvar[ 'scope' ];
319                }
320            }
321            $config_vars = array_merge($parent->config_vars, $config_vars);
322        } else {
323            foreach (Smarty::$global_tpl_vars as $key => $var) {
324                if (!array_key_exists($key, $tpl_vars)) {
325                    foreach ($var as $varkey => $varvalue) {
326                        if ($varkey == 'value') {
327                            $tpl_vars[ $key ][ $varkey ] = $varvalue;
328                        } else {
329                            if ($varkey == 'nocache') {
330                                if ($varvalue == true) {
331                                    $tpl_vars[ $key ][ $varkey ] = $varvalue;
332                                }
333                            } else {
334                                if ($varkey != 'scope' || $varvalue !== 0) {
335                                    $tpl_vars[ $key ][ 'attributes' ][ $varkey ] = $varvalue;
336                                }
337                            }
338                        }
339                    }
340                    $tpl_vars[ $key ][ 'scope' ] = 'Global';
341                }
342            }
343        }
344
345        return (object) array('tpl_vars' => $tpl_vars, 'config_vars' => $config_vars);
346    }
347
348    /**
349     * Return key into $template_data for template
350     *
351     * @param \Smarty_Internal_Template $template template object
352     *
353     * @return string key into $template_data
354     */
355    private function get_key(Smarty_Internal_Template $template)
356    {
357        static $_is_stringy = array('string' => true, 'eval' => true);
358        // calculate Uid if not already done
359        if ($template->source->uid == '') {
360            $template->source->filepath;
361        }
362        $key = $template->source->uid;
363        if (isset($this->template_data[ $this->index ][ $key ])) {
364            return $key;
365        } else {
366            if (isset($_is_stringy[ $template->source->type ])) {
367                $this->template_data[ $this->index ][ $key ][ 'name' ] =
368                    '\'' . substr($template->source->name, 0, 25) . '...\'';
369            } else {
370                $this->template_data[ $this->index ][ $key ][ 'name' ] = $template->source->filepath;
371            }
372            $this->template_data[ $this->index ][ $key ][ 'compile_time' ] = 0;
373            $this->template_data[ $this->index ][ $key ][ 'render_time' ] = 0;
374            $this->template_data[ $this->index ][ $key ][ 'cache_time' ] = 0;
375            $this->template_data[ $this->index ][ $key ][ 'total_time' ] = 0;
376
377            return $key;
378        }
379    }
380
381    /**
382     * Ignore template
383     *
384     * @param \Smarty_Internal_Template $template
385     */
386    public function ignore(Smarty_Internal_Template $template)
387    {
388        // calculate Uid if not already done
389        if ($template->source->uid == '') {
390            $template->source->filepath;
391        }
392        $this->ignore_uid[ $template->source->uid ] = true;
393    }
394
395    /**
396     * handle 'URL' debugging mode
397     *
398     * @param Smarty $smarty
399     */
400    public function debugUrl(Smarty $smarty)
401    {
402        if (isset($_SERVER[ 'QUERY_STRING' ])) {
403            $_query_string = $_SERVER[ 'QUERY_STRING' ];
404        } else {
405            $_query_string = '';
406        }
407        if (false !== strpos($_query_string, $smarty->smarty_debug_id)) {
408            if (false !== strpos($_query_string, $smarty->smarty_debug_id . '=on')) {
409                // enable debugging for this browser session
410                setcookie('SMARTY_DEBUG', true);
411                $smarty->debugging = true;
412            } elseif (false !== strpos($_query_string, $smarty->smarty_debug_id . '=off')) {
413                // disable debugging for this browser session
414                setcookie('SMARTY_DEBUG', false);
415                $smarty->debugging = false;
416            } else {
417                // enable debugging for this page
418                $smarty->debugging = true;
419            }
420        } else {
421            if (isset($_COOKIE[ 'SMARTY_DEBUG' ])) {
422                $smarty->debugging = true;
423            }
424        }
425    }
426}
427