phpBB
Statistics
| Revision:

root / branches / phpBB-3_0_0 / phpBB / includes / functions_template.php

History | View | Annotate | Download (22.6 kB)

1
<?php
2
/**
3
*
4
* @package phpBB3
5
* @version $Id: functions_template.php 11117 2011-04-23 20:00:09Z git-gate $
6
* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc
7
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8
*
9
*/
10
11
/**
12
* @ignore
13
*/
14
if (!defined('IN_PHPBB'))
15
{
16
        exit;
17
}
18
19
/**
20
* Extension of template class - Functions needed for compiling templates only.
21
*
22
* psoTFX, phpBB Development Team - Completion of file caching, decompilation
23
* routines and implementation of conditionals/keywords and associated changes
24
*
25
* The interface was inspired by PHPLib templates,  and the template file (formats are
26
* quite similar)
27
*
28
* The keyword/conditional implementation is currently based on sections of code from
29
* the Smarty templating engine (c) 2001 ispi of Lincoln, Inc. which is released
30
* (on its own and in whole) under the LGPL. Section 3 of the LGPL states that any code
31
* derived from an LGPL application may be relicenced under the GPL, this applies
32
* to this source
33
*
34
* DEFINE directive inspired by a request by Cyberalien
35
*
36
* @package phpBB3
37
*/
38
class template_compile
39
{
40
        var $template;
41
42
        // Various storage arrays
43
        var $block_names = array();
44
        var $block_else_level = array();
45
46
        /**
47
        * constuctor
48
        */
49
        function template_compile(&$template)
50
        {
51
                $this->template = &$template;
52
        }
53
54
        /**
55
        * Load template source from file
56
        * @access private
57
        */
58
        function _tpl_load_file($handle, $store_in_db = false)
59
        {
60
                // Try and open template for read
61
                if (!file_exists($this->template->files[$handle]))
62
                {
63
                        trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR);
64
                }
65
66
                $this->template->compiled_code[$handle] = $this->compile(trim(@file_get_contents($this->template->files[$handle])));
67
68
                // Actually compile the code now.
69
                $this->compile_write($handle, $this->template->compiled_code[$handle]);
70
71
                // Store in database if required...
72
                if ($store_in_db)
73
                {
74
                        global $db, $user;
75
76
                        $sql_ary = array(
77
                                'template_id'                        => $this->template->files_template[$handle],
78
                                'template_filename'                => $this->template->filename[$handle],
79
                                'template_included'                => '',
80
                                'template_mtime'                => time(),
81
                                'template_data'                        => trim(@file_get_contents($this->template->files[$handle])),
82
                        );
83
84
                        $sql = 'INSERT INTO ' . STYLES_TEMPLATE_DATA_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
85
                        $db->sql_query($sql);
86
                }
87
        }
88
89
        /**
90
        * Remove any PHP tags that do not belong, these regular expressions are derived from
91
        * the ones that exist in zend_language_scanner.l
92
        * @access private
93
        */
94
        function remove_php_tags(&$code)
95
        {
96
                // This matches the information gathered from the internal PHP lexer
97
                $match = array(
98
                        '#<([\?%])=?.*?\1>#s',
99
                        '#<script\s+language\s*=\s*(["\']?)php\1\s*>.*?</script\s*>#s',
100
                        '#<\?php(?:\r\n?|[ \n\t]).*?\?>#s'
101
                );
102
103
                $code = preg_replace($match, '', $code);
104
        }
105
106
        /**
107
        * The all seeing all doing compile method. Parts are inspired by or directly from Smarty
108
        * @access private
109
        */
110
        function compile($code, $no_echo = false, $echo_var = '')
111
        {
112
                global $config;
113
114
                if ($echo_var)
115
                {
116
                        global $$echo_var;
117
                }
118
119
                // Remove any "loose" php ... we want to give admins the ability
120
                // to switch on/off PHP for a given template. Allowing unchecked
121
                // php is a no-no. There is a potential issue here in that non-php
122
                // content may be removed ... however designers should use entities
123
                // if they wish to display < and >
124
                $this->remove_php_tags($code);
125
126
                // Pull out all block/statement level elements and separate plain text
127
                preg_match_all('#<!-- PHP -->(.*?)<!-- ENDPHP -->#s', $code, $matches);
128
                $php_blocks = $matches[1];
129
                $code = preg_replace('#<!-- PHP -->.*?<!-- ENDPHP -->#s', '<!-- PHP -->', $code);
130
131
                preg_match_all('#<!-- INCLUDE (\{\$?[A-Z0-9\-_]+\}|[a-zA-Z0-9\_\-\+\./]+) -->#', $code, $matches);
132
                $include_blocks = $matches[1];
133
                $code = preg_replace('#<!-- INCLUDE (?:\{\$?[A-Z0-9\-_]+\}|[a-zA-Z0-9\_\-\+\./]+) -->#', '<!-- INCLUDE -->', $code);
134
135
                preg_match_all('#<!-- INCLUDEPHP ([a-zA-Z0-9\_\-\+\./]+) -->#', $code, $matches);
136
                $includephp_blocks = $matches[1];
137
                $code = preg_replace('#<!-- INCLUDEPHP [a-zA-Z0-9\_\-\+\./]+ -->#', '<!-- INCLUDEPHP -->', $code);
138
139
                preg_match_all('#<!-- ([^<].*?) (.*?)? ?-->#', $code, $blocks, PREG_SET_ORDER);
140
141
                $text_blocks = preg_split('#<!-- [^<].*? (?:.*?)? ?-->#', $code);
142
143
                for ($i = 0, $j = sizeof($text_blocks); $i < $j; $i++)
144
                {
145
                        $this->compile_var_tags($text_blocks[$i]);
146
                }
147
                $compile_blocks = array();
148
149
                for ($curr_tb = 0, $tb_size = sizeof($blocks); $curr_tb < $tb_size; $curr_tb++)
150
                {
151
                        $block_val = &$blocks[$curr_tb];
152
153
                        switch ($block_val[1])
154
                        {
155
                                case 'BEGIN':
156
                                        $this->block_else_level[] = false;
157
                                        $compile_blocks[] = '<?php ' . $this->compile_tag_block($block_val[2]) . ' ?>';
158
                                break;
159
160
                                case 'BEGINELSE':
161
                                        $this->block_else_level[sizeof($this->block_else_level) - 1] = true;
162
                                        $compile_blocks[] = '<?php }} else { ?>';
163
                                break;
164
165
                                case 'END':
166
                                        array_pop($this->block_names);
167
                                        $compile_blocks[] = '<?php ' . ((array_pop($this->block_else_level)) ? '}' : '}}') . ' ?>';
168
                                break;
169
170
                                case 'IF':
171
                                        $compile_blocks[] = '<?php ' . $this->compile_tag_if($block_val[2], false) . ' ?>';
172
                                break;
173
174
                                case 'ELSE':
175
                                        $compile_blocks[] = '<?php } else { ?>';
176
                                break;
177
178
                                case 'ELSEIF':
179
                                        $compile_blocks[] = '<?php ' . $this->compile_tag_if($block_val[2], true) . ' ?>';
180
                                break;
181
182
                                case 'ENDIF':
183
                                        $compile_blocks[] = '<?php } ?>';
184
                                break;
185
186
                                case 'DEFINE':
187
                                        $compile_blocks[] = '<?php ' . $this->compile_tag_define($block_val[2], true) . ' ?>';
188
                                break;
189
190
                                case 'UNDEFINE':
191
                                        $compile_blocks[] = '<?php ' . $this->compile_tag_define($block_val[2], false) . ' ?>';
192
                                break;
193
194
                                case 'INCLUDE':
195
                                        $temp = array_shift($include_blocks);
196
197
                                        // Dynamic includes
198
                                        // Cheap match rather than a full blown regexp, we already know
199
                                        // the format of the input so just use string manipulation.
200
                                        if ($temp[0] == '{')
201
                                        {
202
                                                $file = false;
203
204
                                                if ($temp[1] == '$')
205
                                                {
206
                                                        $var = substr($temp, 2, -1);
207
                                                        //$file = $this->template->_tpldata['DEFINE']['.'][$var];
208
                                                        $temp = "\$this->_tpldata['DEFINE']['.']['$var']";
209
                                                }
210
                                                else
211
                                                {
212
                                                        $var = substr($temp, 1, -1);
213
                                                        //$file = $this->template->_rootref[$var];
214
                                                        $temp = "\$this->_rootref['$var']";
215
                                                }
216
                                        }
217
                                        else
218
                                        {
219
                                                $file = $temp;
220
                                        }
221
222
                                        $compile_blocks[] = '<?php ' . $this->compile_tag_include($temp) . ' ?>';
223
224
                                        // No point in checking variable includes
225
                                        if ($file)
226
                                        {
227
                                                $this->template->_tpl_include($file, false);
228
                                        }
229
                                break;
230
231
                                case 'INCLUDEPHP':
232
                                        $compile_blocks[] = ($config['tpl_allow_php']) ? '<?php ' . $this->compile_tag_include_php(array_shift($includephp_blocks)) . ' ?>' : '';
233
                                break;
234
235
                                case 'PHP':
236
                                        $compile_blocks[] = ($config['tpl_allow_php']) ? '<?php ' . array_shift($php_blocks) . ' ?>' : '';
237
                                break;
238
239
                                default:
240
                                        $this->compile_var_tags($block_val[0]);
241
                                        $trim_check = trim($block_val[0]);
242
                                        $compile_blocks[] = (!$no_echo) ? ((!empty($trim_check)) ? $block_val[0] : '') : ((!empty($trim_check)) ? $block_val[0] : '');
243
                                break;
244
                        }
245
                }
246
247
                $template_php = '';
248
                for ($i = 0, $size = sizeof($text_blocks); $i < $size; $i++)
249
                {
250
                        $trim_check_text = trim($text_blocks[$i]);
251
                        $template_php .= (!$no_echo) ? (($trim_check_text != '') ? $text_blocks[$i] : '') . ((isset($compile_blocks[$i])) ? $compile_blocks[$i] : '') : (($trim_check_text != '') ? $text_blocks[$i] : '') . ((isset($compile_blocks[$i])) ? $compile_blocks[$i] : '');
252
                }
253
254
                // Remove unused opening/closing tags
255
                $template_php = str_replace(' ?><?php ', ' ', $template_php);
256
257
                // Now add a newline after each php closing tag which already has a newline
258
                // PHP itself strips a newline if a closing tag is used (this is documented behaviour) and it is mostly not intended by style authors to remove newlines
259
                $template_php = preg_replace('#\?\>([\r\n])#', '?>\1\1', $template_php);
260
261
                // There will be a number of occasions where we switch into and out of
262
                // PHP mode instantaneously. Rather than "burden" the parser with this
263
                // we'll strip out such occurences, minimising such switching
264
                if ($no_echo)
265
                {
266
                        return "\$$echo_var .= '" . $template_php . "'";
267
                }
268
269
                return $template_php;
270
        }
271
272
        /**
273
        * Compile variables
274
        * @access private
275
        */
276
        function compile_var_tags(&$text_blocks)
277
        {
278
                // change template varrefs into PHP varrefs
279
                $varrefs = array();
280
281
                // This one will handle varrefs WITH namespaces
282
                preg_match_all('#\{((?:[a-z0-9\-_]+\.)+)(\$)?([A-Z0-9\-_]+)\}#', $text_blocks, $varrefs, PREG_SET_ORDER);
283
284
                foreach ($varrefs as $var_val)
285
                {
286
                        $namespace = $var_val[1];
287
                        $varname = $var_val[3];
288
                        $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]);
289
290
                        $text_blocks = str_replace($var_val[0], $new, $text_blocks);
291
                }
292
293
                // This will handle the remaining root-level varrefs
294
                // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array
295
                if (strpos($text_blocks, '{L_') !== false)
296
                {
297
                        $text_blocks = preg_replace('#\{L_([A-Z0-9\-_]+)\}#', "<?php echo ((isset(\$this->_rootref['L_\\1'])) ? \$this->_rootref['L_\\1'] : ((isset(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '{ \\1 }')); ?>", $text_blocks);
298
                }
299
300
                // Handle addslashed language variables prefixed with LA_
301
                // If a template variable already exist, it will be used in favor of it...
302
                if (strpos($text_blocks, '{LA_') !== false)
303
                {
304
                        $text_blocks = preg_replace('#\{LA_([A-Z0-9\-_]+)\}#', "<?php echo ((isset(\$this->_rootref['LA_\\1'])) ? \$this->_rootref['LA_\\1'] : ((isset(\$this->_rootref['L_\\1'])) ? addslashes(\$this->_rootref['L_\\1']) : ((isset(\$user->lang['\\1'])) ? addslashes(\$user->lang['\\1']) : '{ \\1 }'))); ?>", $text_blocks);
305
                }
306
307
                // Handle remaining varrefs
308
                $text_blocks = preg_replace('#\{([A-Z0-9\-_]+)\}#', "<?php echo (isset(\$this->_rootref['\\1'])) ? \$this->_rootref['\\1'] : ''; ?>", $text_blocks);
309
                $text_blocks = preg_replace('#\{\$([A-Z0-9\-_]+)\}#', "<?php echo (isset(\$this->_tpldata['DEFINE']['.']['\\1'])) ? \$this->_tpldata['DEFINE']['.']['\\1'] : ''; ?>", $text_blocks);
310
311
                return;
312
        }
313
314
        /**
315
        * Compile blocks
316
        * @access private
317
        */
318
        function compile_tag_block($tag_args)
319
        {
320
                $no_nesting = false;
321
322
                // Is the designer wanting to call another loop in a loop?
323
                if (strpos($tag_args, '!') === 0)
324
                {
325
                        // Count the number of ! occurrences (not allowed in vars)
326
                        $no_nesting = substr_count($tag_args, '!');
327
                        $tag_args = substr($tag_args, $no_nesting);
328
                }
329
330
                // Allow for control of looping (indexes start from zero):
331
                // foo(2)    : Will start the loop on the 3rd entry
332
                // foo(-2)   : Will start the loop two entries from the end
333
                // foo(3,4)  : Will start the loop on the fourth entry and end it on the fifth
334
                // foo(3,-4) : Will start the loop on the fourth entry and end it four from last
335
                if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match))
336
                {
337
                        $tag_args = $match[1];
338
339
                        if ($match[2] < 0)
340
                        {
341
                                $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')';
342
                        }
343
                        else
344
                        {
345
                                $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')';
346
                        }
347
348
                        if (strlen($match[3]) < 1 || $match[3] == -1)
349
                        {
350
                                $loop_end = '$_' . $tag_args . '_count';
351
                        }
352
                        else if ($match[3] >= 0)
353
                        {
354
                                $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')';
355
                        }
356
                        else //if ($match[3] < -1)
357
                        {
358
                                $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1);
359
                        }
360
                }
361
                else
362
                {
363
                        $loop_start = 0;
364
                        $loop_end = '$_' . $tag_args . '_count';
365
                }
366
367
                $tag_template_php = '';
368
                array_push($this->block_names, $tag_args);
369
370
                if ($no_nesting !== false)
371
                {
372
                        // We need to implode $no_nesting times from the end...
373
                        $block = array_slice($this->block_names, -$no_nesting);
374
                }
375
                else
376
                {
377
                        $block = $this->block_names;
378
                }
379
380
                if (sizeof($block) < 2)
381
                {
382
                        // Block is not nested.
383
                        $tag_template_php = '$_' . $tag_args . "_count = (isset(\$this->_tpldata['$tag_args'])) ? sizeof(\$this->_tpldata['$tag_args']) : 0;";
384
                        $varref = "\$this->_tpldata['$tag_args']";
385
                }
386
                else
387
                {
388
                        // This block is nested.
389
                        // Generate a namespace string for this block.
390
                        $namespace = implode('.', $block);
391
392
                        // Get a reference to the data array for this block that depends on the
393
                        // current indices of all parent blocks.
394
                        $varref = $this->generate_block_data_ref($namespace, false);
395
396
                        // Create the for loop code to iterate over this block.
397
                        $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;';
398
                }
399
400
                $tag_template_php .= 'if ($_' . $tag_args . '_count) {';
401
402
                /**
403
                * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory
404
                * <code>
405
                *        if (!$offset)
406
                *        {
407
                *                $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){';
408
                *        }
409
                * </code>
410
                */
411
412
                $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){';
413
                $tag_template_php .= '$_'. $tag_args . '_val = &' . $varref . '[$_'. $tag_args. '_i];';
414
415
                return $tag_template_php;
416
        }
417
418
        /**
419
        * Compile IF tags - much of this is from Smarty with
420
        * some adaptions for our block level methods
421
        * @access private
422
        */
423
        function compile_tag_if($tag_args, $elseif)
424
        {
425
                // Tokenize args for 'if' tag.
426
                preg_match_all('/(?:
427
                        "[^"\\\\]*(?:\\\\.[^"\\\\]*)*"         |
428
                        \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'     |
429
                        [(),]                                  |
430
                        [^\s(),]+)/x', $tag_args, $match);
431
432
                $tokens = $match[0];
433
                $is_arg_stack = array();
434
435
                for ($i = 0, $size = sizeof($tokens); $i < $size; $i++)
436
                {
437
                        $token = &$tokens[$i];
438
439
                        switch ($token)
440
                        {
441
                                case '!==':
442
                                case '===':
443
                                case '<<':
444
                                case '>>':
445
                                case '|':
446
                                case '^':
447
                                case '&':
448
                                case '~':
449
                                case ')':
450
                                case ',':
451
                                case '+':
452
                                case '-':
453
                                case '*':
454
                                case '/':
455
                                case '@':
456
                                break;
457
458
                                case '==':
459
                                case 'eq':
460
                                        $token = '==';
461
                                break;
462
463
                                case '!=':
464
                                case '<>':
465
                                case 'ne':
466
                                case 'neq':
467
                                        $token = '!=';
468
                                break;
469
470
                                case '<':
471
                                case 'lt':
472
                                        $token = '<';
473
                                break;
474
475
                                case '<=':
476
                                case 'le':
477
                                case 'lte':
478
                                        $token = '<=';
479
                                break;
480
481
                                case '>':
482
                                case 'gt':
483
                                        $token = '>';
484
                                break;
485
486
                                case '>=':
487
                                case 'ge':
488
                                case 'gte':
489
                                        $token = '>=';
490
                                break;
491
492
                                case '&&':
493
                                case 'and':
494
                                        $token = '&&';
495
                                break;
496
497
                                case '||':
498
                                case 'or':
499
                                        $token = '||';
500
                                break;
501
502
                                case '!':
503
                                case 'not':
504
                                        $token = '!';
505
                                break;
506
507
                                case '%':
508
                                case 'mod':
509
                                        $token = '%';
510
                                break;
511
512
                                case '(':
513
                                        array_push($is_arg_stack, $i);
514
                                break;
515
516
                                case 'is':
517
                                        $is_arg_start = ($tokens[$i-1] == ')') ? array_pop($is_arg_stack) : $i-1;
518
                                        $is_arg        = implode('        ', array_slice($tokens,        $is_arg_start, $i -        $is_arg_start));
519
520
                                        $new_tokens        = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
521
522
                                        array_splice($tokens, $is_arg_start, sizeof($tokens), $new_tokens);
523
524
                                        $i = $is_arg_start;
525
526
                                // no break
527
528
                                default:
529
                                        if (preg_match('#^((?:[a-z0-9\-_]+\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs))
530
                                        {
531
                                                $token = (!empty($varrefs[1])) ? $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']' : (($varrefs[2]) ? '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$this->_rootref[\'' . $varrefs[3] . '\']');
532
                                        }
533
                                        else if (preg_match('#^\.((?:[a-z0-9\-_]+\.?)+)$#s', $token, $varrefs))
534
                                        {
535
                                                // Allow checking if loops are set with .loopname
536
                                                // It is also possible to check the loop count by doing <!-- IF .loopname > 1 --> for example
537
                                                $blocks = explode('.', $varrefs[1]);
538
539
                                                // If the block is nested, we have a reference that we can grab.
540
                                                // If the block is not nested, we just go and grab the block from _tpldata
541
                                                if (sizeof($blocks) > 1)
542
                                                {
543
                                                        $block = array_pop($blocks);
544
                                                        $namespace = implode('.', $blocks);
545
                                                        $varref = $this->generate_block_data_ref($namespace, true);
546
547
                                                        // Add the block reference for the last child.
548
                                                        $varref .= "['" . $block . "']";
549
                                                }
550
                                                else
551
                                                {
552
                                                        $varref = '$this->_tpldata';
553
554
                                                        // Add the block reference for the last child.
555
                                                        $varref .= "['" . $blocks[0] . "']";
556
                                                }
557
                                                $token = "sizeof($varref)";
558
                                        }
559
                                        else if (!empty($token))
560
                                        {
561
                                                $token = '(' . $token . ')';
562
                                        }
563
564
                                break;
565
                        }
566
                }
567
568
                // If there are no valid tokens left or only control/compare characters left, we do skip this statement
569
                if (!sizeof($tokens) || str_replace(array(' ', '=', '!', '<', '>', '&', '|', '%', '(', ')'), '', implode('', $tokens)) == '')
570
                {
571
                        $tokens = array('false');
572
                }
573
                return (($elseif) ? '} else if (' : 'if (') . (implode(' ', $tokens) . ') { ');
574
        }
575
576
        /**
577
        * Compile DEFINE tags
578
        * @access private
579
        */
580
        function compile_tag_define($tag_args, $op)
581
        {
582
                preg_match('#^((?:[a-z0-9\-_]+\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (\'?)([^\']*)(\'?))?$#', $tag_args, $match);
583
584
                if (empty($match[2]) || (!isset($match[4]) && $op))
585
                {
586
                        return '';
587
                }
588
589
                if (!$op)
590
                {
591
                        return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');';
592
                }
593
594
                // Are we a string?
595
                if ($match[3] && $match[5])
596
                {
597
                        $match[4] = str_replace(array('\\\'', '\\\\', '\''), array('\'', '\\', '\\\''), $match[4]);
598
599
                        // Compile reference, we allow template variables in defines...
600
                        $match[4] = $this->compile($match[4]);
601
602
                        // Now replace the php code
603
                        $match[4] = "'" . str_replace(array('<?php echo ', '; ?>'), array("' . ", " . '"), $match[4]) . "'";
604
                }
605
                else
606
                {
607
                        preg_match('#true|false|\.#i', $match[4], $type);
608
609
                        switch (strtolower($type[0]))
610
                        {
611
                                case 'true':
612
                                case 'false':
613
                                        $match[4] = strtoupper($match[4]);
614
                                break;
615
616
                                case '.':
617
                                        $match[4] = doubleval($match[4]);
618
                                break;
619
620
                                default:
621
                                        $match[4] = intval($match[4]);
622
                                break;
623
                        }
624
                }
625
626
                return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $match[4] . ';';
627
        }
628
629
        /**
630
        * Compile INCLUDE tag
631
        * @access private
632
        */
633
        function compile_tag_include($tag_args)
634
        {
635
                // Process dynamic includes
636
                if ($tag_args[0] == '$')
637
                {
638
                        return "if (isset($tag_args)) { \$this->_tpl_include($tag_args); }";
639
                }
640
641
                return "\$this->_tpl_include('$tag_args');";
642
        }
643
644
        /**
645
        * Compile INCLUDE_PHP tag
646
        * @access private
647
        */
648
        function compile_tag_include_php($tag_args)
649
        {
650
                return "\$this->_php_include('$tag_args');";
651
        }
652
653
        /**
654
        * parse expression
655
        * This is from Smarty
656
        * @access private
657
        */
658
        function _parse_is_expr($is_arg, $tokens)
659
        {
660
                $expr_end = 0;
661
                $negate_expr = false;
662
663
                if (($first_token = array_shift($tokens)) == 'not')
664
                {
665
                        $negate_expr = true;
666
                        $expr_type = array_shift($tokens);
667
                }
668
                else
669
                {
670
                        $expr_type = $first_token;
671
                }
672
673
                switch ($expr_type)
674
                {
675
                        case 'even':
676
                                if (@$tokens[$expr_end] == 'by')
677
                                {
678
                                        $expr_end++;
679
                                        $expr_arg = $tokens[$expr_end++];
680
                                        $expr = "!(($is_arg / $expr_arg) % $expr_arg)";
681
                                }
682
                                else
683
                                {
684
                                        $expr = "!($is_arg & 1)";
685
                                }
686
                        break;
687
688
                        case 'odd':
689
                                if (@$tokens[$expr_end] == 'by')
690
                                {
691
                                        $expr_end++;
692
                                        $expr_arg = $tokens[$expr_end++];
693
                                        $expr = "(($is_arg / $expr_arg) % $expr_arg)";
694
                                }
695
                                else
696
                                {
697
                                        $expr = "($is_arg & 1)";
698
                                }
699
                        break;
700
701
                        case 'div':
702
                                if (@$tokens[$expr_end] == 'by')
703
                                {
704
                                        $expr_end++;
705
                                        $expr_arg = $tokens[$expr_end++];
706
                                        $expr = "!($is_arg % $expr_arg)";
707
                                }
708
                        break;
709
                }
710
711
                if ($negate_expr)
712
                {
713
                        $expr = "!($expr)";
714
                }
715
716
                array_splice($tokens, 0, $expr_end, $expr);
717
718
                return $tokens;
719
        }
720
721
        /**
722
        * Generates a reference to the given variable inside the given (possibly nested)
723
        * block namespace. This is a string of the form:
724
        * ' . $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . '
725
        * It's ready to be inserted into an "echo" line in one of the templates.
726
        * NOTE: expects a trailing "." on the namespace.
727
        * @access private
728
        */
729
        function generate_block_varref($namespace, $varname, $echo = true, $defop = false)
730
        {
731
                // Strip the trailing period.
732
                $namespace = substr($namespace, 0, -1);
733
734
                // Get a reference to the data block for this namespace.
735
                $varref = $this->generate_block_data_ref($namespace, true, $defop);
736
                // Prepend the necessary code to stick this in an echo line.
737
738
                // Append the variable reference.
739
                $varref .= "['$varname']";
740
                $varref = ($echo) ? "<?php echo $varref; ?>" : ((isset($varref)) ? $varref : '');
741
742
                return $varref;
743
        }
744
745
        /**
746
        * Generates a reference to the array of data values for the given
747
        * (possibly nested) block namespace. This is a string of the form:
748
        * $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN']
749
        *
750
        * If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above.
751
        * NOTE: does not expect a trailing "." on the blockname.
752
        * @access private
753
        */
754
        function generate_block_data_ref($blockname, $include_last_iterator, $defop = false)
755
        {
756
                // Get an array of the blocks involved.
757
                $blocks = explode('.', $blockname);
758
                $blockcount = sizeof($blocks) - 1;
759
760
                // DEFINE is not an element of any referenced variable, we must use _tpldata to access it
761
                if ($defop)
762
                {
763
                        $varref = '$this->_tpldata[\'DEFINE\']';
764
                        // Build up the string with everything but the last child.
765
                        for ($i = 0; $i < $blockcount; $i++)
766
                        {
767
                                $varref .= "['" . $blocks[$i] . "'][\$_" . $blocks[$i] . '_i]';
768
                        }
769
                        // Add the block reference for the last child.
770
                        $varref .= "['" . $blocks[$blockcount] . "']";
771
                        // Add the iterator for the last child if requried.
772
                        if ($include_last_iterator)
773
                        {
774
                                $varref .= '[$_' . $blocks[$blockcount] . '_i]';
775
                        }
776
                        return $varref;
777
                }
778
                else if ($include_last_iterator)
779
                {
780
                        return '$_'. $blocks[$blockcount] . '_val';
781
                }
782
                else
783
                {
784
                        return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']';
785
                }
786
        }
787
788
        /**
789
        * Write compiled file to cache directory
790
        * @access private
791
        */
792
        function compile_write($handle, $data)
793
        {
794
                global $phpEx;
795
796
                $filename = $this->template->cachepath . str_replace('/', '.', $this->template->filename[$handle]) . '.' . $phpEx;
797
798
                $data = "<?php if (!defined('IN_PHPBB')) exit;" . ((strpos($data, '<?php') === 0) ? substr($data, 5) : ' ?>' . $data);
799
800
                if ($fp = @fopen($filename, 'wb'))
801
                {
802
                        @flock($fp, LOCK_EX);
803
                        @fwrite ($fp, $data);
804
                        @flock($fp, LOCK_UN);
805
                        @fclose($fp);
806
807
                        phpbb_chmod($filename, CHMOD_READ | CHMOD_WRITE);
808
                }
809
810
                return;
811
        }
812
}
813
814
?>