1<?php
2/**
3 * Smarty Internal Plugin Smarty Template Compiler Base
4 * This file contains the basic classes and methods for compiling Smarty templates with lexer/parser
5 *
6 * @package    Smarty
7 * @subpackage Compiler
8 * @author     Uwe Tews
9 */
10
11/**
12 * Class SmartyTemplateCompiler
13 *
14 * @package    Smarty
15 * @subpackage Compiler
16 */
17class Smarty_Internal_SmartyTemplateCompiler extends Smarty_Internal_TemplateCompilerBase
18{
19    /**
20     * Lexer class name
21     *
22     * @var string
23     */
24    public $lexer_class;
25
26    /**
27     * Parser class name
28     *
29     * @var string
30     */
31    public $parser_class;
32
33    /**
34     * array of vars which can be compiled in local scope
35     *
36     * @var array
37     */
38    public $local_var = array();
39
40    /**
41     * array of callbacks called when the normal compile process of template is finished
42     *
43     * @var array
44     */
45    public $postCompileCallbacks = array();
46
47    /**
48     * prefix code
49     *
50     * @var string
51     */
52    public $prefixCompiledCode = '';
53
54    /**
55     * postfix code
56     *
57     * @var string
58     */
59    public $postfixCompiledCode = '';
60
61    /**
62     * Initialize compiler
63     *
64     * @param string $lexer_class  class name
65     * @param string $parser_class class name
66     * @param Smarty $smarty       global instance
67     */
68    public function __construct($lexer_class, $parser_class, Smarty $smarty)
69    {
70        parent::__construct($smarty);
71        // get required plugins
72        $this->lexer_class = $lexer_class;
73        $this->parser_class = $parser_class;
74    }
75
76    /**
77     * method to compile a Smarty template
78     *
79     * @param  mixed $_content template source
80     * @param bool   $isTemplateSource
81     *
82     * @return bool true if compiling succeeded, false if it failed
83     * @throws \SmartyCompilerException
84     */
85    protected function doCompile($_content, $isTemplateSource = false)
86    {
87        /* here is where the compiling takes place. Smarty
88          tags in the templates are replaces with PHP code,
89          then written to compiled files. */
90        // init the lexer/parser to compile the template
91        $this->parser =
92            new $this->parser_class(new $this->lexer_class(str_replace(array("\r\n", "\r"), "\n", $_content), $this),
93                                    $this);
94        if ($isTemplateSource && $this->template->caching) {
95            $this->parser->insertPhpCode("<?php\n\$_smarty_tpl->compiled->nocache_hash = '{$this->nocache_hash}';\n?>\n");
96        }
97        if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
98            $mbEncoding = mb_internal_encoding();
99            mb_internal_encoding('ASCII');
100        } else {
101            $mbEncoding = null;
102        }
103
104        if ($this->smarty->_parserdebug) {
105            $this->parser->PrintTrace();
106            $this->parser->lex->PrintTrace();
107        }
108        // get tokens from lexer and parse them
109        while ($this->parser->lex->yylex()) {
110            if ($this->smarty->_parserdebug) {
111                echo "<pre>Line {$this->parser->lex->line} Parsing  {$this->parser->yyTokenName[$this->parser->lex->token]} Token " .
112                    htmlentities($this->parser->lex->value) . "</pre>";
113            }
114            $this->parser->doParse($this->parser->lex->token, $this->parser->lex->value);
115        }
116
117        // finish parsing process
118        $this->parser->doParse(0, 0);
119        if ($mbEncoding) {
120            mb_internal_encoding($mbEncoding);
121        }
122        // check for unclosed tags
123        if (count($this->_tag_stack) > 0) {
124            // get stacked info
125            list($openTag, $_data) = array_pop($this->_tag_stack);
126            $this->trigger_template_error("unclosed {$this->smarty->left_delimiter}" . $openTag .
127                                          "{$this->smarty->right_delimiter} tag");
128        }
129        // call post compile callbacks
130        foreach ($this->postCompileCallbacks as $cb) {
131            $parameter = $cb;
132            $parameter[0] = $this;
133            call_user_func_array($cb[0], $parameter);
134        }
135        // return compiled code
136        return $this->prefixCompiledCode . $this->parser->retvalue . $this->postfixCompiledCode;
137    }
138
139    /**
140     * Register a post compile callback
141     * - when the callback is called after template compiling the compiler object will be inserted as first parameter
142     *
143     * @param callback $callback
144     * @param array    $parameter optional parameter array
145     * @param string   $key       optional key for callback
146     * @param bool     $replace   if true replace existing keyed callback
147     */
148    public function registerPostCompileCallback($callback, $parameter = array(), $key = null, $replace = false)
149    {
150        array_unshift($parameter, $callback);
151        if (isset($key)) {
152            if ($replace || !isset($this->postCompileCallbacks[$key])) {
153                $this->postCompileCallbacks[$key] = $parameter;
154            }
155        } else {
156            $this->postCompileCallbacks[] = $parameter;
157        }
158    }
159
160    /**
161     * Remove a post compile callback
162     *
163     * @param string $key callback key
164     */
165    public function unregisterPostCompileCallback($key)
166    {
167        unset($this->postCompileCallbacks[$key]);
168    }
169}
170