1<?php
2/**
3 * Smarty Internal Plugin Config File Compiler
4 * This is the config file compiler class. It calls the lexer and parser to
5 * perform the compiling.
6 *
7 * @package    Smarty
8 * @subpackage Config
9 * @author     Uwe Tews
10 */
11
12/**
13 * Main config file compiler class
14 *
15 * @package    Smarty
16 * @subpackage Config
17 */
18class Smarty_Internal_Config_File_Compiler
19{
20    /**
21     * Lexer class name
22     *
23     * @var string
24     */
25    public $lexer_class;
26
27    /**
28     * Parser class name
29     *
30     * @var string
31     */
32    public $parser_class;
33
34    /**
35     * Lexer object
36     *
37     * @var object
38     */
39    public $lex;
40
41    /**
42     * Parser object
43     *
44     * @var object
45     */
46    public $parser;
47
48    /**
49     * Smarty object
50     *
51     * @var Smarty object
52     */
53    public $smarty;
54
55    /**
56     * Smarty object
57     *
58     * @var Smarty_Internal_Template object
59     */
60    public $template;
61
62    /**
63     * Compiled config data sections and variables
64     *
65     * @var array
66     */
67    public $config_data = array();
68
69    /**
70     * compiled config data must always be written
71     *
72     * @var bool
73     */
74    public $write_compiled_code = true;
75
76    /**
77     * Initialize compiler
78     *
79     * @param string $lexer_class  class name
80     * @param string $parser_class class name
81     * @param Smarty $smarty       global instance
82     */
83    public function __construct($lexer_class, $parser_class, Smarty $smarty)
84    {
85        $this->smarty = $smarty;
86        // get required plugins
87        $this->lexer_class = $lexer_class;
88        $this->parser_class = $parser_class;
89        $this->smarty = $smarty;
90        $this->config_data[ 'sections' ] = array();
91        $this->config_data[ 'vars' ] = array();
92    }
93
94    /**
95     * Method to compile Smarty config source.
96     *
97     * @param Smarty_Internal_Template $template
98     *
99     * @return bool true if compiling succeeded, false if it failed
100     * @throws \SmartyException
101     */
102    public function compileTemplate(Smarty_Internal_Template $template)
103    {
104        $this->template = $template;
105        $this->template->compiled->file_dependency[ $this->template->source->uid ] =
106            array(
107                $this->template->source->filepath,
108                $this->template->source->getTimeStamp(),
109                $this->template->source->type
110            );
111        if ($this->smarty->debugging) {
112            if (!isset($this->smarty->_debug)) {
113                $this->smarty->_debug = new Smarty_Internal_Debug();
114            }
115            $this->smarty->_debug->start_compile($this->template);
116        }
117        // init the lexer/parser to compile the config file
118        /* @var Smarty_Internal_ConfigFileLexer $this ->lex */
119        $this->lex = new $this->lexer_class(
120            str_replace(
121                array(
122                    "\r\n",
123                    "\r"
124                ),
125                "\n",
126                $template->source->getContent()
127            ) . "\n",
128            $this
129        );
130        /* @var Smarty_Internal_ConfigFileParser $this ->parser */
131        $this->parser = new $this->parser_class($this->lex, $this);
132        if (function_exists('mb_internal_encoding')
133            && function_exists('ini_get')
134            && ((int)ini_get('mbstring.func_overload')) & 2
135        ) {
136            $mbEncoding = mb_internal_encoding();
137            mb_internal_encoding('ASCII');
138        } else {
139            $mbEncoding = null;
140        }
141        if ($this->smarty->_parserdebug) {
142            $this->parser->PrintTrace();
143        }
144        // get tokens from lexer and parse them
145        while ($this->lex->yylex()) {
146            if ($this->smarty->_parserdebug) {
147                echo "<br>Parsing  {$this->parser->yyTokenName[$this->lex->token]} Token {$this->lex->value} Line {$this->lex->line} \n";
148            }
149            $this->parser->doParse($this->lex->token, $this->lex->value);
150        }
151        // finish parsing process
152        $this->parser->doParse(0, 0);
153        if ($mbEncoding) {
154            mb_internal_encoding($mbEncoding);
155        }
156        if ($this->smarty->debugging) {
157            $this->smarty->_debug->end_compile($this->template);
158        }
159        // template header code
160        $template_header =
161            "<?php /* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") .
162            "\n";
163        $template_header .= "         compiled from '{$this->template->source->filepath}' */ ?>\n";
164        $code = '<?php $_smarty_tpl->smarty->ext->configLoad->_loadConfigVars($_smarty_tpl, ' .
165                var_export($this->config_data, true) . '); ?>';
166        return $template_header . $this->template->smarty->ext->_codeFrame->create($this->template, $code);
167    }
168
169    /**
170     * display compiler error messages without dying
171     * If parameter $args is empty it is a parser detected syntax error.
172     * In this case the parser is called to obtain information about expected tokens.
173     * If parameter $args contains a string this is used as error message
174     *
175     * @param string $args individual error message or null
176     *
177     * @throws SmartyCompilerException
178     */
179    public function trigger_config_file_error($args = null)
180    {
181        // get config source line which has error
182        $line = $this->lex->line;
183        if (isset($args)) {
184            // $line--;
185        }
186        $match = preg_split("/\n/", $this->lex->data);
187        $error_text =
188            "Syntax error in config file '{$this->template->source->filepath}' on line {$line} '{$match[$line - 1]}' ";
189        if (isset($args)) {
190            // individual error message
191            $error_text .= $args;
192        } else {
193            // expected token from parser
194            foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
195                $exp_token = $this->parser->yyTokenName[ $token ];
196                if (isset($this->lex->smarty_token_names[ $exp_token ])) {
197                    // token type from lexer
198                    $expect[] = '"' . $this->lex->smarty_token_names[ $exp_token ] . '"';
199                } else {
200                    // otherwise internal token name
201                    $expect[] = $this->parser->yyTokenName[ $token ];
202                }
203            }
204            // output parser error message
205            $error_text .= ' - Unexpected "' . $this->lex->value . '", expected one of: ' . implode(' , ', $expect);
206        }
207        throw new SmartyCompilerException($error_text);
208    }
209}
210