1<?php
2
3/**
4 * Smarty Resource Data Object
5 * Meta Data Container for Template Files
6 *
7 * @package    Smarty
8 * @subpackage TemplateResources
9 * @author     Rodney Rehm
10 * @property   string $content compiled content
11 */
12class Smarty_Template_Compiled extends Smarty_Template_Resource_Base
13{
14    /**
15     * nocache hash
16     *
17     * @var string|null
18     */
19    public $nocache_hash = null;
20
21    /**
22     * get a Compiled Object of this source
23     *
24     * @param Smarty_Internal_Template $_template template object
25     *
26     * @return Smarty_Template_Compiled compiled object
27     */
28    public static function load($_template)
29    {
30        $compiled = new Smarty_Template_Compiled();
31        if ($_template->source->handler->hasCompiledHandler) {
32            $_template->source->handler->populateCompiledFilepath($compiled, $_template);
33        } else {
34            $compiled->populateCompiledFilepath($_template);
35        }
36        return $compiled;
37    }
38
39    /**
40     * populate Compiled Object with compiled filepath
41     *
42     * @param Smarty_Internal_Template $_template template object
43     **/
44    public function populateCompiledFilepath(Smarty_Internal_Template $_template)
45    {
46        $source = &$_template->source;
47        $smarty = &$_template->smarty;
48        $this->filepath = $smarty->getCompileDir();
49        if (isset($_template->compile_id)) {
50            $this->filepath .= preg_replace('![^\w]+!', '_', $_template->compile_id) .
51                               ($smarty->use_sub_dirs ? DIRECTORY_SEPARATOR : '^');
52        }
53        // if use_sub_dirs, break file into directories
54        if ($smarty->use_sub_dirs) {
55            $this->filepath .= $source->uid[ 0 ] . $source->uid[ 1 ] . DIRECTORY_SEPARATOR . $source->uid[ 2 ] .
56                               $source->uid[ 3 ] . DIRECTORY_SEPARATOR . $source->uid[ 4 ] . $source->uid[ 5 ] .
57                               DIRECTORY_SEPARATOR;
58        }
59        $this->filepath .= $source->uid . '_';
60        if ($source->isConfig) {
61            $this->filepath .= (int)$smarty->config_read_hidden + (int)$smarty->config_booleanize * 2 +
62                               (int)$smarty->config_overwrite * 4;
63        } else {
64            $this->filepath .= (int)$smarty->merge_compiled_includes + (int)$smarty->escape_html * 2 +
65                               (($smarty->merge_compiled_includes && $source->type === 'extends') ?
66                                   (int)$smarty->extends_recursion * 4 : 0);
67        }
68        $this->filepath .= '.' . $source->type;
69        $basename = $source->handler->getBasename($source);
70        if (!empty($basename)) {
71            $this->filepath .= '.' . $basename;
72        }
73        if ($_template->caching) {
74            $this->filepath .= '.cache';
75        }
76        $this->filepath .= '.php';
77        $this->timestamp = $this->exists = is_file($this->filepath);
78        if ($this->exists) {
79            $this->timestamp = filemtime($this->filepath);
80        }
81    }
82
83    /**
84     * render compiled template code
85     *
86     * @param Smarty_Internal_Template $_template
87     *
88     * @return string
89     * @throws Exception
90     */
91    public function render(Smarty_Internal_Template $_template)
92    {
93        // checks if template exists
94        if (!$_template->source->exists) {
95            $type = $_template->source->isConfig ? 'config' : 'template';
96            throw new SmartyException("Unable to load {$type} '{$_template->source->type}:{$_template->source->name}'");
97        }
98        if ($_template->smarty->debugging) {
99            if (!isset($_template->smarty->_debug)) {
100                $_template->smarty->_debug = new Smarty_Internal_Debug();
101            }
102            $_template->smarty->_debug->start_render($_template);
103        }
104        if (!$this->processed) {
105            $this->process($_template);
106        }
107        if (isset($_template->cached)) {
108            $_template->cached->file_dependency =
109                array_merge($_template->cached->file_dependency, $this->file_dependency);
110        }
111        if ($_template->source->handler->uncompiled) {
112            $_template->source->handler->renderUncompiled($_template->source, $_template);
113        } else {
114            $this->getRenderedTemplateCode($_template);
115        }
116        if ($_template->caching && $this->has_nocache_code) {
117            $_template->cached->hashes[ $this->nocache_hash ] = true;
118        }
119        if ($_template->smarty->debugging) {
120            $_template->smarty->_debug->end_render($_template);
121        }
122    }
123
124    /**
125     * load compiled template or compile from source
126     *
127     * @param Smarty_Internal_Template $_smarty_tpl do not change variable name, is used by compiled template
128     *
129     * @throws Exception
130     */
131    public function process(Smarty_Internal_Template $_smarty_tpl)
132    {
133        $source = &$_smarty_tpl->source;
134        $smarty = &$_smarty_tpl->smarty;
135        if ($source->handler->recompiled) {
136            $source->handler->process($_smarty_tpl);
137        } elseif (!$source->handler->uncompiled) {
138            if (!$this->exists || $smarty->force_compile
139                || ($_smarty_tpl->compile_check && $source->getTimeStamp() > $this->getTimeStamp())
140            ) {
141                $this->compileTemplateSource($_smarty_tpl);
142                $compileCheck = $_smarty_tpl->compile_check;
143                $_smarty_tpl->compile_check = Smarty::COMPILECHECK_OFF;
144                $this->loadCompiledTemplate($_smarty_tpl);
145                $_smarty_tpl->compile_check = $compileCheck;
146            } else {
147                $_smarty_tpl->mustCompile = true;
148                @include $this->filepath;
149                if ($_smarty_tpl->mustCompile) {
150                    $this->compileTemplateSource($_smarty_tpl);
151                    $compileCheck = $_smarty_tpl->compile_check;
152                    $_smarty_tpl->compile_check = Smarty::COMPILECHECK_OFF;
153                    $this->loadCompiledTemplate($_smarty_tpl);
154                    $_smarty_tpl->compile_check = $compileCheck;
155                }
156            }
157            $_smarty_tpl->_subTemplateRegister();
158            $this->processed = true;
159        }
160    }
161
162    /**
163     * compile template from source
164     *
165     * @param Smarty_Internal_Template $_template
166     *
167     * @throws Exception
168     */
169    public function compileTemplateSource(Smarty_Internal_Template $_template)
170    {
171        $this->file_dependency = array();
172        $this->includes = array();
173        $this->nocache_hash = null;
174        $this->unifunc = null;
175        // compile locking
176        if ($saved_timestamp = (!$_template->source->handler->recompiled && is_file($this->filepath))) {
177            $saved_timestamp = $this->getTimeStamp();
178            touch($this->filepath);
179        }
180        // compile locking
181        try {
182            // call compiler
183            $_template->loadCompiler();
184            $this->write($_template, $_template->compiler->compileTemplate($_template));
185        } catch (Exception $e) {
186            // restore old timestamp in case of error
187            if ($saved_timestamp && is_file($this->filepath)) {
188                touch($this->filepath, $saved_timestamp);
189            }
190            unset($_template->compiler);
191            throw $e;
192        }
193        // release compiler object to free memory
194        unset($_template->compiler);
195    }
196
197    /**
198     * Write compiled code by handler
199     *
200     * @param Smarty_Internal_Template $_template template object
201     * @param string                   $code      compiled code
202     *
203     * @return bool success
204     * @throws \SmartyException
205     */
206    public function write(Smarty_Internal_Template $_template, $code)
207    {
208        if (!$_template->source->handler->recompiled) {
209            if ($_template->smarty->ext->_writeFile->writeFile($this->filepath, $code, $_template->smarty) === true) {
210                $this->timestamp = $this->exists = is_file($this->filepath);
211                if ($this->exists) {
212                    $this->timestamp = filemtime($this->filepath);
213                    return true;
214                }
215            }
216            return false;
217        }
218        return true;
219    }
220
221    /**
222     * Read compiled content from handler
223     *
224     * @param Smarty_Internal_Template $_template template object
225     *
226     * @return string content
227     */
228    public function read(Smarty_Internal_Template $_template)
229    {
230        if (!$_template->source->handler->recompiled) {
231            return file_get_contents($this->filepath);
232        }
233        return isset($this->content) ? $this->content : false;
234    }
235
236    /**
237     * Load fresh compiled template by including the PHP file
238     * HHVM requires a work around because of a PHP incompatibility
239     *
240     * @param \Smarty_Internal_Template $_smarty_tpl do not change variable name, is used by compiled template
241     */
242    private function loadCompiledTemplate(Smarty_Internal_Template $_smarty_tpl)
243    {
244        if (function_exists('opcache_invalidate')
245            && (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1)
246        ) {
247            opcache_invalidate($this->filepath, true);
248        } elseif (function_exists('apc_compile_file')) {
249            apc_compile_file($this->filepath);
250        }
251        if (defined('HHVM_VERSION')) {
252            eval('?>' . file_get_contents($this->filepath));
253        } else {
254            include $this->filepath;
255        }
256    }
257}
258