1<?php
2/**
3 * Smarty Internal Plugin Compile Function
4 * Compiles the {function} {/function} tags
5 *
6 * @package    Smarty
7 * @subpackage Compiler
8 * @author     Uwe Tews
9 */
10
11/**
12 * Smarty Internal Plugin Compile Function Class
13 *
14 * @package    Smarty
15 * @subpackage Compiler
16 */
17class Smarty_Internal_Compile_Function extends Smarty_Internal_CompileBase
18{
19
20    /**
21     * Attribute definition: Overwrites base class.
22     *
23     * @var array
24     * @see Smarty_Internal_CompileBase
25     */
26    public $required_attributes = array('name');
27
28    /**
29     * Attribute definition: Overwrites base class.
30     *
31     * @var array
32     * @see Smarty_Internal_CompileBase
33     */
34    public $shorttag_order = array('name');
35
36    /**
37     * Attribute definition: Overwrites base class.
38     *
39     * @var array
40     * @see Smarty_Internal_CompileBase
41     */
42    public $optional_attributes = array('_any');
43
44    /**
45     * Compiles code for the {function} tag
46     *
47     * @param  array                                $args      array with attributes from parser
48     * @param \Smarty_Internal_TemplateCompilerBase $compiler  compiler object
49     * @param  array                                $parameter array with compilation parameter
50     *
51     * @return bool true
52     * @throws \SmartyCompilerException
53     */
54    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
55    {
56        $compiler->loopNesting++;
57        // check and get attributes
58        $_attr = $this->getAttributes($compiler, $args);
59
60        if ($_attr['nocache'] === true) {
61            $compiler->trigger_template_error('nocache option not allowed', null, true);
62        }
63        unset($_attr['nocache']);
64        $_name = trim($_attr['name'], "'\"");
65        $compiler->parent_compiler->tpl_function[$_name] = $compiler->parent_compiler->template->tpl_function[$_name] = array();
66        $save = array($_attr, $compiler->parser->current_buffer, $compiler->template->compiled->has_nocache_code,
67                      $compiler->template->caching);
68        $this->openTag($compiler, 'function', $save);
69        // Init temporary context
70        $compiler->parser->current_buffer = new Smarty_Internal_ParseTree_Template();
71        $compiler->template->compiled->has_nocache_code = false;
72        return true;
73    }
74}
75
76/**
77 * Smarty Internal Plugin Compile Functionclose Class
78 *
79 * @package    Smarty
80 * @subpackage Compiler
81 */
82class Smarty_Internal_Compile_Functionclose extends Smarty_Internal_CompileBase
83{
84
85    /**
86     * Compiler object
87     *
88     * @var object
89     */
90    private $compiler = null;
91
92    /**
93     * Compiles code for the {/function} tag
94     *
95     * @param  array                                       $args      array with attributes from parser
96     * @param object|\Smarty_Internal_TemplateCompilerBase $compiler  compiler object
97     * @param  array                                       $parameter array with compilation parameter
98     *
99     * @return bool true
100     */
101    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
102    {
103        $compiler->loopNesting--;
104        $this->compiler = $compiler;
105        $saved_data = $this->closeTag($compiler, array('function'));
106        $_attr = $saved_data[0];
107        $_name = trim($_attr['name'], "'\"");
108        $compiler->parent_compiler->tpl_function[$_name]['called_functions'] = $compiler->parent_compiler->template->tpl_function[$_name]['called_functions'] = $compiler->called_functions;
109        $compiler->parent_compiler->tpl_function[$_name]['compiled_filepath'] = $compiler->parent_compiler->template->tpl_function[$_name]['compiled_filepath'] = $compiler->parent_compiler->template->compiled->filepath;
110        $compiler->parent_compiler->tpl_function[$_name]['uid'] = $compiler->parent_compiler->template->tpl_function[$_name]['uid'] = $compiler->template->source->uid;
111        $compiler->called_functions = array();
112        $_parameter = $_attr;
113        unset($_parameter['name']);
114        // default parameter
115        $_paramsArray = array();
116        foreach ($_parameter as $_key => $_value) {
117            if (is_int($_key)) {
118                $_paramsArray[] = "$_key=>$_value";
119            } else {
120                $_paramsArray[] = "'$_key'=>$_value";
121            }
122        }
123        if (!empty($_paramsArray)) {
124            $_params = 'array(' . implode(",", $_paramsArray) . ')';
125            $_paramsCode = "\$params = array_merge($_params, \$params);\n";
126        } else {
127            $_paramsCode = '';
128        }
129        $_functionCode = $compiler->parser->current_buffer;
130        // setup buffer for template function code
131        $compiler->parser->current_buffer = new Smarty_Internal_ParseTree_Template();
132
133        $_funcName = "smarty_template_function_{$_name}_{$compiler->template->compiled->nocache_hash}";
134        $_funcNameCaching = $_funcName . '_nocache';
135        if ($compiler->template->compiled->has_nocache_code) {
136            $compiler->parent_compiler->tpl_function[$_name]['call_name_caching'] = $compiler->parent_compiler->template->tpl_function[$_name]['call_name_caching'] = $_funcNameCaching;
137            $output = "<?php\n";
138            $output .= "/* {$_funcNameCaching} */\n";
139            $output .= "if (!function_exists('{$_funcNameCaching}')) {\n";
140            $output .= "function {$_funcNameCaching} (\$_smarty_tpl,\$params) {\n";
141            $output .= "ob_start();\n";
142            $output .= "\$_smarty_tpl->compiled->has_nocache_code = true;\n";
143            $output .= $_paramsCode;
144            $output .= "\$_smarty_tpl->_cache['saved_tpl_vars'][] = \$_smarty_tpl->tpl_vars;\n";
145            $output .= "foreach (\$params as \$key => \$value) {\n\$_smarty_tpl->tpl_vars[\$key] = new Smarty_Variable(\$value);\n}";
146            $output .= "\$params = var_export(\$params, true);\n";
147            $output .= "echo \"/*%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/<?php ";
148            $output .= "\\\$saved_tpl_vars = \\\$_smarty_tpl->tpl_vars;\nforeach (\$params as \\\$key => \\\$value) {\n\\\$_smarty_tpl->tpl_vars[\\\$key] = new Smarty_Variable(\\\$value);\n}\n?>";
149            $output .= "/*/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\n\";?>";
150            $compiler->parser->current_buffer->append_subtree($compiler->parser, new Smarty_Internal_ParseTree_Tag($compiler->parser, $output));
151            $compiler->parser->current_buffer->append_subtree($compiler->parser, $_functionCode);
152            $output = "<?php echo \"/*%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/<?php ";
153            $output .= "foreach (Smarty::\\\$global_tpl_vars as \\\$key => \\\$value){\n";
154            $output .= "if (!isset(\\\$_smarty_tpl->tpl_vars[\\\$key]) || \\\$_smarty_tpl->tpl_vars[\\\$key] === \\\$value) \\\$saved_tpl_vars[\\\$key] = \\\$value;\n}\n";
155            $output .= "\\\$_smarty_tpl->tpl_vars = \\\$saved_tpl_vars;?>\n";
156            $output .= "/*/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\";\n?>";
157            $output .= "<?php echo str_replace('{$compiler->template->compiled->nocache_hash}', \$_smarty_tpl->compiled->nocache_hash, ob_get_clean());\n";
158            $output .= "\$_smarty_tpl->tpl_vars = array_pop(\$_smarty_tpl->_cache['saved_tpl_vars']);\n}\n}\n";
159            $output .= "/*/ {$_funcName}_nocache */\n\n";
160            $output .= "?>\n";
161            $compiler->parser->current_buffer->append_subtree($compiler->parser, new Smarty_Internal_ParseTree_Tag($compiler->parser, $output));
162            $_functionCode = new Smarty_Internal_ParseTree_Tag($compiler->parser, preg_replace_callback("/((<\?php )?echo '\/\*%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%\*\/([\S\s]*?)\/\*\/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%\*\/';(\?>\n)?)/", array($this,
163                                                                                                                                                                                                                                                                                                          'removeNocache'), $_functionCode->to_smarty_php($compiler->parser)));
164        }
165        $compiler->parent_compiler->tpl_function[$_name]['call_name'] = $compiler->parent_compiler->template->tpl_function[$_name]['call_name'] = $_funcName;
166        $output = "<?php\n";
167        $output .= "/* {$_funcName} */\n";
168        $output .= "if (!function_exists('{$_funcName}')) {\n";
169        $output .= "function {$_funcName}(\$_smarty_tpl,\$params) {\n";
170        $output .= "\$saved_tpl_vars = \$_smarty_tpl->tpl_vars;\n";
171        $output .= $_paramsCode;
172        $output .= "foreach (\$params as \$key => \$value) {\n\$_smarty_tpl->tpl_vars[\$key] = new Smarty_Variable(\$value);\n}?>";
173        $compiler->parser->current_buffer->append_subtree($compiler->parser, new Smarty_Internal_ParseTree_Tag($compiler->parser, $output));
174        $compiler->parser->current_buffer->append_subtree($compiler->parser, $_functionCode);
175        $output = "<?php foreach (Smarty::\$global_tpl_vars as \$key => \$value){\n";
176        $output .= "if (!isset(\$_smarty_tpl->tpl_vars[\$key]) || \$_smarty_tpl->tpl_vars[\$key] === \$value) \$saved_tpl_vars[\$key] = \$value;\n}\n";
177        $output .= "\$_smarty_tpl->tpl_vars = \$saved_tpl_vars;\n}\n}\n";
178        $output .= "/*/ {$_funcName} */\n\n";
179        $output .= "?>\n";
180        $compiler->parser->current_buffer->append_subtree($compiler->parser, new Smarty_Internal_ParseTree_Tag($compiler->parser, $output));
181        $compiler->parent_compiler->blockOrFunctionCode .= $compiler->parser->current_buffer->to_smarty_php($compiler->parser);
182        // nocache plugins must be copied
183        if (!empty($compiler->template->compiled->required_plugins['nocache'])) {
184            foreach ($compiler->template->compiled->required_plugins['nocache'] as $plugin => $tmp) {
185                foreach ($tmp as $type => $data) {
186                    $compiler->parent_compiler->template->compiled->required_plugins['compiled'][$plugin][$type] = $data;
187                }
188            }
189        }
190        // restore old buffer
191
192        $compiler->parser->current_buffer = $saved_data[1];
193        // restore old status
194        $compiler->template->compiled->has_nocache_code = $saved_data[2];
195        $compiler->template->caching = $saved_data[3];
196        return true;
197    }
198
199    /**
200     * @param $match
201     *
202     * @return mixed
203     */
204    function removeNocache($match)
205    {
206        $code = preg_replace("/((<\?php )?echo '\/\*%%SmartyNocache:{$this->compiler->template->compiled->nocache_hash}%%\*\/)|(\/\*\/%%SmartyNocache:{$this->compiler->template->compiled->nocache_hash}%%\*\/';(\?>\n)?)/", '', $match[0]);
207        $code = str_replace(array('\\\'', '\\\\\''), array('\'', '\\\''), $code);
208        return $code;
209    }
210}
211