1<?php
2
3/**
4 * TplFunction Runtime Methods callTemplateFunction
5 *
6 * @package    Smarty
7 * @subpackage PluginsInternal
8 * @author     Uwe Tews
9 **/
10class Smarty_Internal_Runtime_TplFunction
11{
12    /**
13     * Call template function
14     *
15     * @param \Smarty_Internal_Template $tpl     template object
16     * @param string                    $name    template function name
17     * @param array                     $params  parameter array
18     * @param bool                      $nocache true if called nocache
19     *
20     * @throws \SmartyException
21     */
22    public function callTemplateFunction(Smarty_Internal_Template $tpl, $name, $params, $nocache)
23    {
24        $funcParam = isset($tpl->tplFunctions[ $name ]) ? $tpl->tplFunctions[ $name ] :
25            (isset($tpl->smarty->tplFunctions[ $name ]) ? $tpl->smarty->tplFunctions[ $name ] : null);
26        if (isset($funcParam)) {
27            if (!$tpl->caching || ($tpl->caching && $nocache)) {
28                $function = $funcParam[ 'call_name' ];
29            } else {
30                if (isset($funcParam[ 'call_name_caching' ])) {
31                    $function = $funcParam[ 'call_name_caching' ];
32                } else {
33                    $function = $funcParam[ 'call_name' ];
34                }
35            }
36            if (function_exists($function)) {
37                $this->saveTemplateVariables($tpl, $name);
38                $function($tpl, $params);
39                $this->restoreTemplateVariables($tpl, $name);
40                return;
41            }
42            // try to load template function dynamically
43            if ($this->addTplFuncToCache($tpl, $name, $function)) {
44                $this->saveTemplateVariables($tpl, $name);
45                $function($tpl, $params);
46                $this->restoreTemplateVariables($tpl, $name);
47                return;
48            }
49        }
50        throw new SmartyException("Unable to find template function '{$name}'");
51    }
52
53    /**
54     * Register template functions defined by template
55     *
56     * @param \Smarty|\Smarty_Internal_Template|\Smarty_Internal_TemplateBase $obj
57     * @param array                                                           $tplFunctions source information array of
58     *                                                                                      template functions defined
59     *                                                                                      in template
60     * @param bool                                                            $override     if true replace existing
61     *                                                                                      functions with same name
62     */
63    public function registerTplFunctions(Smarty_Internal_TemplateBase $obj, $tplFunctions, $override = true)
64    {
65        $obj->tplFunctions =
66            $override ? array_merge($obj->tplFunctions, $tplFunctions) : array_merge($tplFunctions, $obj->tplFunctions);
67        // make sure that the template functions are known in parent templates
68        if ($obj->_isSubTpl()) {
69            $obj->smarty->ext->_tplFunction->registerTplFunctions($obj->parent, $tplFunctions, false);
70        } else {
71            $obj->smarty->tplFunctions = $override ? array_merge($obj->smarty->tplFunctions, $tplFunctions) :
72                array_merge($tplFunctions, $obj->smarty->tplFunctions);
73        }
74    }
75
76    /**
77     * Return source parameter array for single or all template functions
78     *
79     * @param \Smarty_Internal_Template $tpl  template object
80     * @param null|string               $name template function name
81     *
82     * @return array|bool|mixed
83     */
84    public function getTplFunction(Smarty_Internal_Template $tpl, $name = null)
85    {
86        if (isset($name)) {
87            return isset($tpl->tplFunctions[ $name ]) ? $tpl->tplFunctions[ $name ] :
88                (isset($tpl->smarty->tplFunctions[ $name ]) ? $tpl->smarty->tplFunctions[ $name ] : false);
89        } else {
90            return empty($tpl->tplFunctions) ? $tpl->smarty->tplFunctions : $tpl->tplFunctions;
91        }
92    }
93
94    /**
95     * Add template function to cache file for nocache calls
96     *
97     * @param Smarty_Internal_Template $tpl
98     * @param string                   $_name     template function name
99     * @param string                   $_function PHP function name
100     *
101     * @return bool
102     */
103    public function addTplFuncToCache(Smarty_Internal_Template $tpl, $_name, $_function)
104    {
105        $funcParam = $tpl->tplFunctions[ $_name ];
106        if (is_file($funcParam[ 'compiled_filepath' ])) {
107            // read compiled file
108            $code = file_get_contents($funcParam[ 'compiled_filepath' ]);
109            // grab template function
110            if (preg_match("/\/\* {$_function} \*\/([\S\s]*?)\/\*\/ {$_function} \*\//", $code, $match)) {
111                // grab source info from file dependency
112                preg_match("/\s*'{$funcParam['uid']}'([\S\s]*?)\),/", $code, $match1);
113                unset($code);
114                // make PHP function known
115                eval($match[ 0 ]);
116                if (function_exists($_function)) {
117                    // search cache file template
118                    $tplPtr = $tpl;
119                    while (!isset($tplPtr->cached) && isset($tplPtr->parent)) {
120                        $tplPtr = $tplPtr->parent;
121                    }
122                    // add template function code to cache file
123                    if (isset($tplPtr->cached)) {
124                        $content = $tplPtr->cached->read($tplPtr);
125                        if ($content) {
126                            // check if we must update file dependency
127                            if (!preg_match("/'{$funcParam['uid']}'(.*?)'nocache_hash'/", $content, $match2)) {
128                                $content = preg_replace("/('file_dependency'(.*?)\()/", "\\1{$match1[0]}", $content);
129                            }
130                            $tplPtr->smarty->ext->_updateCache->write(
131                                $tplPtr,
132                                preg_replace('/\s*\?>\s*$/', "\n", $content) .
133                                "\n" . preg_replace(
134                                    array(
135                                        '/^\s*<\?php\s+/',
136                                        '/\s*\?>\s*$/',
137                                    ),
138                                    "\n",
139                                    $match[ 0 ]
140                                )
141                            );
142                        }
143                    }
144                    return true;
145                }
146            }
147        }
148        return false;
149    }
150
151    /**
152     * Save current template variables on stack
153     *
154     * @param \Smarty_Internal_Template $tpl
155     * @param string                    $name stack name
156     */
157    public function saveTemplateVariables(Smarty_Internal_Template $tpl, $name)
158    {
159        $tpl->_cache[ 'varStack' ][] =
160            array('tpl' => $tpl->tpl_vars, 'config' => $tpl->config_vars, 'name' => "_tplFunction_{$name}");
161    }
162
163    /**
164     * Restore saved variables into template objects
165     *
166     * @param \Smarty_Internal_Template $tpl
167     * @param string                    $name stack name
168     */
169    public function restoreTemplateVariables(Smarty_Internal_Template $tpl, $name)
170    {
171        if (isset($tpl->_cache[ 'varStack' ])) {
172            $vars = array_pop($tpl->_cache[ 'varStack' ]);
173            $tpl->tpl_vars = $vars[ 'tpl' ];
174            $tpl->config_vars = $vars[ 'config' ];
175        }
176    }
177}
178