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            $key = $this->get_key($template);
121        }
122        $this->template_data[ $this->index ][ $key ][ 'compile_time' ] +=
123            microtime(true) - $this->template_data[ $this->index ][ $key ][ 'start_time' ];
124    }
125
126    /**
127     * Start logging of render time
128     *
129     * @param \Smarty_Internal_Template $template
130     */
131    public function start_render(Smarty_Internal_Template $template)
132    {
133        $key = $this->get_key($template);
134        $this->template_data[ $this->index ][ $key ][ 'start_time' ] = microtime(true);
135    }
136
137    /**
138     * End logging of compile time
139     *
140     * @param \Smarty_Internal_Template $template
141     */
142    public function end_render(Smarty_Internal_Template $template)
143    {
144        $key = $this->get_key($template);
145        $this->template_data[ $this->index ][ $key ][ 'render_time' ] +=
146            microtime(true) - $this->template_data[ $this->index ][ $key ][ 'start_time' ];
147    }
148
149    /**
150     * Start logging of cache time
151     *
152     * @param \Smarty_Internal_Template $template cached template
153     */
154    public function start_cache(Smarty_Internal_Template $template)
155    {
156        $key = $this->get_key($template);
157        $this->template_data[ $this->index ][ $key ][ 'start_time' ] = microtime(true);
158    }
159
160    /**
161     * End logging of cache time
162     *
163     * @param \Smarty_Internal_Template $template cached template
164     */
165    public function end_cache(Smarty_Internal_Template $template)
166    {
167        $key = $this->get_key($template);
168        $this->template_data[ $this->index ][ $key ][ 'cache_time' ] +=
169            microtime(true) - $this->template_data[ $this->index ][ $key ][ 'start_time' ];
170    }
171
172    /**
173     * Register template object
174     *
175     * @param \Smarty_Internal_Template $template cached template
176     */
177    public function register_template(Smarty_Internal_Template $template)
178    {
179    }
180
181    /**
182     * Register data object
183     *
184     * @param \Smarty_Data $data data object
185     */
186    public static function register_data(Smarty_Data $data)
187    {
188    }
189
190    /**
191     * Opens a window for the Smarty Debugging Console and display the data
192     *
193     * @param Smarty_Internal_Template|Smarty $obj object to debug
194     * @param bool                            $full
195     *
196     * @throws \Exception
197     * @throws \SmartyException
198     */
199    public function display_debug($obj, $full = false)
200    {
201        if (!$full) {
202            $this->offset++;
203            $savedIndex = $this->index;
204            $this->index = 9999;
205        }
206        $smarty = $obj->_getSmartyObj();
207        // create fresh instance of smarty for displaying the debug console
208        // to avoid problems if the application did overload the Smarty class
209        $debObj = new Smarty();
210        // copy the working dirs from application
211        $debObj->setCompileDir($smarty->getCompileDir());
212        // init properties by hand as user may have edited the original Smarty class
213        $debObj->setPluginsDir(is_dir(dirname(__FILE__) . '/../plugins') ? dirname(__FILE__) .
214                                                                           '/../plugins' : $smarty->getPluginsDir());
215        $debObj->force_compile = false;
216        $debObj->compile_check = Smarty::COMPILECHECK_ON;
217        $debObj->left_delimiter = '{';
218        $debObj->right_delimiter = '}';
219        $debObj->security_policy = null;
220        $debObj->debugging = false;
221        $debObj->debugging_ctrl = 'NONE';
222        $debObj->error_reporting = E_ALL & ~E_NOTICE;
223        $debObj->debug_tpl =
224            isset($smarty->debug_tpl) ? $smarty->debug_tpl : 'file:' . dirname(__FILE__) . '/../debug.tpl';
225        $debObj->registered_plugins = array();
226        $debObj->registered_resources = array();
227        $debObj->registered_filters = array();
228        $debObj->autoload_filters = array();
229        $debObj->default_modifiers = array();
230        $debObj->escape_html = true;
231        $debObj->caching = Smarty::CACHING_OFF;
232        $debObj->compile_id = null;
233        $debObj->cache_id = null;
234        // prepare information of assigned variables
235        $ptr = $this->get_debug_vars($obj);
236        $_assigned_vars = $ptr->tpl_vars;
237        ksort($_assigned_vars);
238        $_config_vars = $ptr->config_vars;
239        ksort($_config_vars);
240        $debugging = $smarty->debugging;
241        $_template = new Smarty_Internal_Template($debObj->debug_tpl, $debObj);
242        if ($obj->_isTplObj()) {
243            $_template->assign('template_name', $obj->source->type . ':' . $obj->source->name);
244        }
245        if ($obj->_objType === 1 || $full) {
246            $_template->assign('template_data', $this->template_data[ $this->index ]);
247        } else {
248            $_template->assign('template_data', null);
249        }
250        $_template->assign('assigned_vars', $_assigned_vars);
251        $_template->assign('config_vars', $_config_vars);
252        $_template->assign('execution_time', microtime(true) - $smarty->start_time);
253        $_template->assign('display_mode', $debugging === 2 || !$full);
254        $_template->assign('offset', $this->offset * 50);
255        echo $_template->fetch();
256        if (isset($full)) {
257            $this->index--;
258        }
259        if (!$full) {
260            $this->index = $savedIndex;
261        }
262    }
263
264    /**
265     * Recursively gets variables from all template/data scopes
266     *
267     * @param Smarty_Internal_Template|Smarty_Data $obj object to debug
268     *
269     * @return StdClass
270     */
271    public function get_debug_vars($obj)
272    {
273        $config_vars = array();
274        foreach ($obj->config_vars as $key => $var) {
275            $config_vars[ $key ][ 'value' ] = $var;
276            if ($obj->_isTplObj()) {
277                $config_vars[ $key ][ 'scope' ] = $obj->source->type . ':' . $obj->source->name;
278            } elseif ($obj->_isDataObj()) {
279                $tpl_vars[ $key ][ 'scope' ] = $obj->dataObjectName;
280            } else {
281                $config_vars[ $key ][ 'scope' ] = 'Smarty object';
282            }
283        }
284        $tpl_vars = array();
285        foreach ($obj->tpl_vars as $key => $var) {
286            foreach ($var as $varkey => $varvalue) {
287                if ($varkey === 'value') {
288                    $tpl_vars[ $key ][ $varkey ] = $varvalue;
289                } else {
290                    if ($varkey === 'nocache') {
291                        if ($varvalue === true) {
292                            $tpl_vars[ $key ][ $varkey ] = $varvalue;
293                        }
294                    } else {
295                        if ($varkey !== 'scope' || $varvalue !== 0) {
296                            $tpl_vars[ $key ][ 'attributes' ][ $varkey ] = $varvalue;
297                        }
298                    }
299                }
300            }
301            if ($obj->_isTplObj()) {
302                $tpl_vars[ $key ][ 'scope' ] = $obj->source->type . ':' . $obj->source->name;
303            } elseif ($obj->_isDataObj()) {
304                $tpl_vars[ $key ][ 'scope' ] = $obj->dataObjectName;
305            } else {
306                $tpl_vars[ $key ][ 'scope' ] = 'Smarty object';
307            }
308        }
309        if (isset($obj->parent)) {
310            $parent = $this->get_debug_vars($obj->parent);
311            foreach ($parent->tpl_vars as $name => $pvar) {
312                if (isset($tpl_vars[ $name ]) && $tpl_vars[ $name ][ 'value' ] === $pvar[ 'value' ]) {
313                    $tpl_vars[ $name ][ 'scope' ] = $pvar[ 'scope' ];
314                }
315            }
316            $tpl_vars = array_merge($parent->tpl_vars, $tpl_vars);
317            foreach ($parent->config_vars as $name => $pvar) {
318                if (isset($config_vars[ $name ]) && $config_vars[ $name ][ 'value' ] === $pvar[ 'value' ]) {
319                    $config_vars[ $name ][ 'scope' ] = $pvar[ 'scope' ];
320                }
321            }
322            $config_vars = array_merge($parent->config_vars, $config_vars);
323        } else {
324            foreach (Smarty::$global_tpl_vars as $key => $var) {
325                if (!array_key_exists($key, $tpl_vars)) {
326                    foreach ($var as $varkey => $varvalue) {
327                        if ($varkey === 'value') {
328                            $tpl_vars[ $key ][ $varkey ] = $varvalue;
329                        } else {
330                            if ($varkey === 'nocache') {
331                                if ($varvalue === true) {
332                                    $tpl_vars[ $key ][ $varkey ] = $varvalue;
333                                }
334                            } else {
335                                if ($varkey !== 'scope' || $varvalue !== 0) {
336                                    $tpl_vars[ $key ][ 'attributes' ][ $varkey ] = $varvalue;
337                                }
338                            }
339                        }
340                    }
341                    $tpl_vars[ $key ][ 'scope' ] = 'Global';
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            return $key;
377        }
378    }
379
380    /**
381     * Ignore template
382     *
383     * @param \Smarty_Internal_Template $template
384     */
385    public function ignore(Smarty_Internal_Template $template)
386    {
387        // calculate Uid if not already done
388        if ($template->source->uid === '') {
389            $template->source->filepath;
390        }
391        $this->ignore_uid[ $template->source->uid ] = true;
392    }
393
394    /**
395     * handle 'URL' debugging mode
396     *
397     * @param Smarty $smarty
398     */
399    public function debugUrl(Smarty $smarty)
400    {
401        if (isset($_SERVER[ 'QUERY_STRING' ])) {
402            $_query_string = $_SERVER[ 'QUERY_STRING' ];
403        } else {
404            $_query_string = '';
405        }
406        if (false !== strpos($_query_string, $smarty->smarty_debug_id)) {
407            if (false !== strpos($_query_string, $smarty->smarty_debug_id . '=on')) {
408                // enable debugging for this browser session
409                setcookie('SMARTY_DEBUG', true);
410                $smarty->debugging = true;
411            } elseif (false !== strpos($_query_string, $smarty->smarty_debug_id . '=off')) {
412                // disable debugging for this browser session
413                setcookie('SMARTY_DEBUG', false);
414                $smarty->debugging = false;
415            } else {
416                // enable debugging for this page
417                $smarty->debugging = true;
418            }
419        } else {
420            if (isset($_COOKIE[ 'SMARTY_DEBUG' ])) {
421                $smarty->debugging = true;
422            }
423        }
424    }
425}
426