Merge changes made in revisions #r9405 to #r9467
[phpbb.git] / phpBB / includes / classes / template_compile.php
blob098d8f481747d9ea2402092bbb4f60e9347965f9
1 <?php
2 /**
4 * @package phpBB3
5 * @version $Id$
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
9 */
11 /**
12 * @ignore
14 if (!defined('IN_PHPBB'))
16 exit;
19 /**
20 * The template filter that does the actual compilation
21 * @see template_compile
22 * @package phpBB3
24 class phpbb_template_filter extends php_user_filter
26 /**
27 * @var string Replaceable tokens regex
29 private $regex = '~<!-- ([A-Z][A-Z_0-9]+)(?: (.*?) ?)?-->|{((?:[a-z][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~';
31 /**
32 * @var array
34 private $block_names = array();
36 /**
37 * @var array
39 private $block_else_level = array();
41 /**
42 * @var string
44 private $chunk;
46 public function filter($in, $out, &$consumed, $closing)
48 $written = false;
50 while ($bucket = stream_bucket_make_writeable($in))
52 $consumed += $bucket->datalen;
54 $data = $this->chunk . $bucket->data;
55 $last_nl = strrpos($data, "\n");
56 $this->chunk = substr($data, $last_nl);
57 $data = substr($data, 0, $last_nl);
59 if (!strlen($data))
61 continue;
64 $written = true;
66 $bucket->data = $this->compile($data);
67 $bucket->datalen = strlen($bucket->data);
68 stream_bucket_append($out, $bucket);
71 if ($closing && strlen($this->chunk))
73 $written = true;
74 $bucket = stream_bucket_new($this->stream, $this->compile($this->chunk));
75 stream_bucket_append($out, $bucket);
78 return $written ? PSFS_PASS_ON : PSFS_FEED_ME;
81 public function onCreate()
83 $this->chunk = '';
84 return true;
87 private function compile($data)
89 $data = preg_replace('#<(?:[\\?%]|script)#s', '<?php echo\'\\0\';?>', $data);
90 return str_replace('?><?php', '', preg_replace_callback($this->regex, array($this, 'replace'), $data));
93 private function replace($matches)
95 if (isset($matches[3]))
97 return $this->compile_var_tags($matches[0]);
100 switch ($matches[1])
102 case 'BEGIN':
103 $this->block_else_level[] = false;
104 return '<?php ' . $this->compile_tag_block($matches[2]) . ' ?>';
105 break;
107 case 'BEGINELSE':
108 $this->block_else_level[sizeof($this->block_else_level) - 1] = true;
109 return '<?php }} else { ?>';
110 break;
112 case 'END':
113 array_pop($this->block_names);
114 return '<?php ' . ((array_pop($this->block_else_level)) ? '}' : '}}') . ' ?>';
115 break;
117 case 'IF':
118 return '<?php ' . $this->compile_tag_if($matches[2], false) . ' ?>';
119 break;
121 case 'ELSE':
122 return '<?php } else { ?>';
123 break;
125 case 'ELSEIF':
126 return '<?php ' . $this->compile_tag_if($matches[2], true) . ' ?>';
127 break;
129 case 'ENDIF':
130 return '<?php } ?>';
131 break;
133 case 'DEFINE':
134 return '<?php ' . $this->compile_tag_define($matches[2], true) . ' ?>';
135 break;
137 case 'UNDEFINE':
138 return '<?php ' . $this->compile_tag_define($matches[2], false) . ' ?>';
139 break;
141 case 'INCLUDE':
142 return '<?php ' . $this->compile_tag_include($matches[2]) . ' ?>';
143 break;
145 case 'INCLUDEPHP':
146 return (phpbb::$config['tpl_allow_php']) ? '<?php ' . $this->compile_tag_include_php($matches[2]) . ' ?>' : '';
147 break;
149 case 'PHP':
150 return (phpbb::$config['tpl_allow_php']) ? '<?php ' : '<!-- ';
151 break;
153 case 'ENDPHP':
154 return (phpbb::$config['tpl_allow_php']) ? ' ?>' : ' -->';
155 break;
157 default:
158 return $matches[0];
159 break;
162 return '';
166 * Compile variables
167 * @access private
169 private function compile_var_tags(&$text_blocks)
171 // change template varrefs into PHP varrefs
172 $varrefs = array();
174 // This one will handle varrefs WITH namespaces
175 preg_match_all('#\{((?:[a-z0-9\-_]+\.)+)(\$)?([A-Z0-9\-_]+)\}#', $text_blocks, $varrefs, PREG_SET_ORDER);
177 foreach ($varrefs as $var_val)
179 $namespace = $var_val[1];
180 $varname = $var_val[3];
181 $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]);
183 $text_blocks = str_replace($var_val[0], $new, $text_blocks);
186 // Handle special language tags L_ and LA_
187 $this->compile_language_tags($text_blocks);
189 // This will handle the remaining root-level varrefs
190 $text_blocks = preg_replace('#\{([A-Z0-9\-_]+)\}#', "<?php echo (isset(\$_rootref['\\1'])) ? \$_rootref['\\1'] : ''; ?>", $text_blocks);
191 $text_blocks = preg_replace('#\{\$([A-Z0-9\-_]+)\}#', "<?php echo (isset(\$_tpldata['DEFINE']['.']['\\1'])) ? \$_tpldata['DEFINE']['.']['\\1'] : ''; ?>", $text_blocks);
193 return $text_blocks;
197 * Handlse special language tags L_ and LA_
199 private function compile_language_tags(&$text_blocks)
201 // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array
202 if (strpos($text_blocks, '{L_') !== false)
204 $text_blocks = preg_replace('#\{L_([A-Z0-9\-_]+)\}#', "<?php echo ((isset(\$_rootref['L_\\1'])) ? \$_rootref['L_\\1'] : ((isset(\$_lang['\\1'])) ? \$_lang['\\1'] : '{ \\1 }')); ?>", $text_blocks);
207 // Handle addslashed language variables prefixed with LA_
208 // If a template variable already exist, it will be used in favor of it...
209 if (strpos($text_blocks, '{LA_') !== false)
211 $text_blocks = preg_replace('#\{LA_([A-Z0-9\-_]+)\}#', "<?php echo ((isset(\$_rootref['LA_\\1'])) ? \$_rootref['LA_\\1'] : ((isset(\$_rootref['L_\\1'])) ? addslashes(\$_rootref['L_\\1']) : ((isset(\$_lang['\\1'])) ? addslashes(\$_lang['\\1']) : '{ \\1 }'))); ?>", $text_blocks);
216 * Compile blocks
217 * @access private
219 private function compile_tag_block($tag_args)
221 $no_nesting = false;
223 // Is the designer wanting to call another loop in a loop?
224 // <!-- BEGIN loop -->
225 // <!-- BEGIN !loop2 -->
226 // <!-- END !loop2 -->
227 // <!-- END loop -->
228 // 'loop2' is actually on the same nesting level as 'loop' you assign
229 // variables to it with template->assign_block_vars('loop2', array(...))
230 if (strpos($tag_args, '!') === 0)
232 // Count the number if ! occurrences (not allowed in vars)
233 $no_nesting = substr_count($tag_args, '!');
234 $tag_args = substr($tag_args, $no_nesting);
237 // Allow for control of looping (indexes start from zero):
238 // foo(2) : Will start the loop on the 3rd entry
239 // foo(-2) : Will start the loop two entries from the end
240 // foo(3,4) : Will start the loop on the fourth entry and end it on the fifth
241 // foo(3,-4) : Will start the loop on the fourth entry and end it four from last
242 $match = array();
243 if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match))
245 $tag_args = $match[1];
247 if ($match[2] < 0)
249 $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')';
251 else
253 $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')';
256 if (strlen($match[3]) < 1 || $match[3] == -1)
258 $loop_end = '$_' . $tag_args . '_count';
260 else if ($match[3] >= 0)
262 $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')';
264 else //if ($match[3] < -1)
266 $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1);
269 else
271 $loop_start = 0;
272 $loop_end = '$_' . $tag_args . '_count';
275 $tag_template_php = '';
276 array_push($this->block_names, $tag_args);
278 if ($no_nesting !== false)
280 // We need to implode $no_nesting times from the end...
281 $block = array_slice($this->block_names, -$no_nesting);
283 else
285 $block = $this->block_names;
288 if (sizeof($block) < 2)
290 // Block is not nested.
291 $tag_template_php = '$_' . $tag_args . "_count = (isset(\$_tpldata['$tag_args'])) ? sizeof(\$_tpldata['$tag_args']) : 0;";
292 $varref = "\$_tpldata['$tag_args']";
294 else
296 // This block is nested.
297 // Generate a namespace string for this block.
298 $namespace = implode('.', $block);
300 // Get a reference to the data array for this block that depends on the
301 // current indices of all parent blocks.
302 $varref = $this->generate_block_data_ref($namespace, false);
304 // Create the for loop code to iterate over this block.
305 $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;';
308 $tag_template_php .= 'if ($_' . $tag_args . '_count) {';
311 * 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
312 * <code>
313 * if (!$offset)
315 * $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){';
317 * </code>
320 $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){';
321 $tag_template_php .= '$_'. $tag_args . '_val = &' . $varref . '[$_'. $tag_args. '_i];';
323 return $tag_template_php;
327 * Compile a general expression - much of this is from Smarty with
328 * some adaptions for our block level methods
329 * @access private
331 private function compile_expression($tag_args)
333 $match = array();
334 preg_match_all('/(?:
335 "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" |
336 \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' |
337 [(),] |
338 [^\s(),]+)/x', $tag_args, $match);
340 $tokens = $match[0];
341 $is_arg_stack = array();
343 for ($i = 0, $size = sizeof($tokens); $i < $size; $i++)
345 $token = &$tokens[$i];
347 switch ($token)
349 case '!==':
350 case '===':
351 case '<<':
352 case '>>':
353 case '|':
354 case '^':
355 case '&':
356 case '~':
357 case ')':
358 case ',':
359 case '+':
360 case '-':
361 case '*':
362 case '/':
363 case '@':
364 break;
366 case '==':
367 case 'eq':
368 $token = '==';
369 break;
371 case '!=':
372 case '<>':
373 case 'ne':
374 case 'neq':
375 $token = '!=';
376 break;
378 case '<':
379 case 'lt':
380 $token = '<';
381 break;
383 case '<=':
384 case 'le':
385 case 'lte':
386 $token = '<=';
387 break;
389 case '>':
390 case 'gt':
391 $token = '>';
392 break;
394 case '>=':
395 case 'ge':
396 case 'gte':
397 $token = '>=';
398 break;
400 case '&&':
401 case 'and':
402 $token = '&&';
403 break;
405 case '||':
406 case 'or':
407 $token = '||';
408 break;
410 case '!':
411 case 'not':
412 $token = '!';
413 break;
415 case '%':
416 case 'mod':
417 $token = '%';
418 break;
420 case '(':
421 array_push($is_arg_stack, $i);
422 break;
424 case 'is':
425 $is_arg_start = ($tokens[$i-1] == ')') ? array_pop($is_arg_stack) : $i-1;
426 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
428 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
430 array_splice($tokens, $is_arg_start, sizeof($tokens), $new_tokens);
432 $i = $is_arg_start;
434 // no break
436 default:
437 $varrefs = array();
438 if (preg_match('#^((?:[a-z0-9\-_]+\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs))
440 if (!empty($varrefs[1]))
442 $namespace = substr($varrefs[1], 0, -1);
443 $namespace = (strpos($namespace, '.') === false) ? $namespace : strrchr($namespace, '.');
445 // S_ROW_COUNT is deceptive, it returns the current row number now the number of rows
446 // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM
447 switch ($varrefs[3])
449 case 'S_ROW_NUM':
450 case 'S_ROW_COUNT':
451 $token = "\$_${namespace}_i";
452 break;
454 case 'S_NUM_ROWS':
455 $token = "\$_${namespace}_count";
456 break;
458 case 'S_FIRST_ROW':
459 $token = "(\$_${namespace}_i == 0)";
460 break;
462 case 'S_LAST_ROW':
463 $token = "(\$_${namespace}_i == \$_${namespace}_count - 1)";
464 break;
466 case 'S_BLOCK_NAME':
467 $token = "'$namespace'";
468 break;
470 default:
471 $token = $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']';
472 break;
475 else
477 $token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']';
480 else if (preg_match('#^\.((?:[a-z0-9\-_]+\.?)+)$#s', $token, $varrefs))
482 // Allow checking if loops are set with .loopname
483 // It is also possible to check the loop count by doing <!-- IF .loopname > 1 --> for example
484 $blocks = explode('.', $varrefs[1]);
486 // If the block is nested, we have a reference that we can grab.
487 // If the block is not nested, we just go and grab the block from _tpldata
488 if (sizeof($blocks) > 1)
490 $block = array_pop($blocks);
491 $namespace = implode('.', $blocks);
492 $varref = $this->generate_block_data_ref($namespace, true);
494 // Add the block reference for the last child.
495 $varref .= "['" . $block . "']";
497 else
499 $varref = '$_tpldata';
501 // Add the block reference for the last child.
502 $varref .= "['" . $blocks[0] . "']";
504 $token = "isset($varref) && sizeof($varref)";
507 break;
511 return $tokens;
515 private function compile_tag_if($tag_args, $elseif)
517 $tokens = $this->compile_expression($tag_args);
519 // @todo We suppress notices within IF statements until we find a way to correctly check them
520 $tpl = ($elseif) ? '} else if (@(' : 'if (@(';
521 $tpl .= implode(' ', $tokens);
522 $tpl .= ')) { ';
524 return $tpl;
528 * Compile DEFINE tags
529 * @access private
531 private function compile_tag_define($tag_args, $op)
533 $match = array();
534 preg_match('#^((?:[a-z0-9\-_]+\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (.*?))?$#', $tag_args, $match);
536 if (empty($match[2]) || (!isset($match[3]) && $op))
538 return '';
541 if (!$op)
543 return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');';
546 $parsed_statement = implode(' ', $this->compile_expression($match[3]));
548 return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $parsed_statement . ';';
552 * Compile INCLUDE tag
553 * @access private
555 private function compile_tag_include($tag_args)
557 return "\$this->_tpl_include('$tag_args');";
561 * Compile INCLUDE_PHP tag
562 * @access private
564 private function compile_tag_include_php($tag_args)
566 return "include('" . $tag_args . "');";
570 * parse expression
571 * This is from Smarty
572 * @access private
574 private function _parse_is_expr($is_arg, $tokens)
576 $expr_end = 0;
577 $negate_expr = false;
579 if (($first_token = array_shift($tokens)) == 'not')
581 $negate_expr = true;
582 $expr_type = array_shift($tokens);
584 else
586 $expr_type = $first_token;
589 switch ($expr_type)
591 case 'even':
592 if (@$tokens[$expr_end] == 'by')
594 $expr_end++;
595 $expr_arg = $tokens[$expr_end++];
596 $expr = "!(($is_arg / $expr_arg) & 1)";
598 else
600 $expr = "!($is_arg & 1)";
602 break;
604 case 'odd':
605 if (@$tokens[$expr_end] == 'by')
607 $expr_end++;
608 $expr_arg = $tokens[$expr_end++];
609 $expr = "(($is_arg / $expr_arg) & 1)";
611 else
613 $expr = "($is_arg & 1)";
615 break;
617 case 'div':
618 if (@$tokens[$expr_end] == 'by')
620 $expr_end++;
621 $expr_arg = $tokens[$expr_end++];
622 $expr = "!($is_arg % $expr_arg)";
624 break;
627 if ($negate_expr)
629 $expr = "!($expr)";
632 array_splice($tokens, 0, $expr_end, $expr);
634 return $tokens;
638 * Generates a reference to the given variable inside the given (possibly nested)
639 * block namespace. This is a string of the form:
640 * ' . $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . '
641 * It's ready to be inserted into an "echo" line in one of the templates.
643 * @access private
644 * @param string $namespace Namespace to access (expects a trailing "." on the namespace)
645 * @param string $varname Variable name to use
646 * @param bool $echo If true return an echo statement, otherwise a reference to the internal variable
647 * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable
648 * @return string Code to access variable or echo it if $echo is true
650 private function generate_block_varref($namespace, $varname, $echo = true, $defop = false)
652 // Strip the trailing period.
653 $namespace = substr($namespace, 0, -1);
655 $expr = true;
657 // S_ROW_COUNT is deceptive, it returns the current row number now the number of rows
658 // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM
659 switch ($varname)
661 case 'S_ROW_NUM':
662 case 'S_ROW_COUNT':
663 $varref = "\$_${namespace}_i";
664 break;
666 case 'S_NUM_ROWS':
667 $varref = "\$_${namespace}_count";
668 break;
670 case 'S_FIRST_ROW':
671 $varref = "(\$_${namespace}_i == 0)";
672 break;
674 case 'S_LAST_ROW':
675 $varref = "(\$_${namespace}_i == \$_${namespace}_count - 1)";
676 break;
678 case 'S_BLOCK_NAME':
679 $varref = "'$namespace'";
680 break;
682 default:
683 // Get a reference to the data block for this namespace.
684 $varref = $this->generate_block_data_ref($namespace, true, $defop);
685 // Prepend the necessary code to stick this in an echo line.
687 // Append the variable reference.
688 $varref .= "['$varname']";
690 $expr = false;
691 break;
693 // @todo Test the !$expr more
694 $varref = ($echo) ? "<?php echo $varref; ?>" : (($expr || isset($varref)) ? $varref : '');
696 return $varref;
700 * Generates a reference to the array of data values for the given
701 * (possibly nested) block namespace. This is a string of the form:
702 * $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN']
704 * @access private
705 * @param string $blockname Block to access (does not expect a trailing "." on the blockname)
706 * @param bool $include_last_iterator If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above.
707 * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable
708 * @return string Code to access variable
710 private function generate_block_data_ref($blockname, $include_last_iterator, $defop = false)
712 // Get an array of the blocks involved.
713 $blocks = explode('.', $blockname);
714 $blockcount = sizeof($blocks) - 1;
716 // DEFINE is not an element of any referenced variable, we must use _tpldata to access it
717 if ($defop)
719 $varref = '$_tpldata[\'DEFINE\']';
720 // Build up the string with everything but the last child.
721 for ($i = 0; $i < $blockcount; $i++)
723 $varref .= "['" . $blocks[$i] . "'][\$_" . $blocks[$i] . '_i]';
725 // Add the block reference for the last child.
726 $varref .= "['" . $blocks[$blockcount] . "']";
727 // Add the iterator for the last child if requried.
728 if ($include_last_iterator)
730 $varref .= '[$_' . $blocks[$blockcount] . '_i]';
732 return $varref;
734 else if ($include_last_iterator)
736 return '$_'. $blocks[$blockcount] . '_val';
738 else
740 return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']';
745 stream_filter_register('phpbb_template', 'phpbb_template_filter');
748 * Extension of template class - Functions needed for compiling templates only.
750 * psoTFX, phpBB Development Team - Completion of file caching, decompilation
751 * routines and implementation of conditionals/keywords and associated changes
753 * The interface was inspired by PHPLib templates, and the template file (formats are
754 * quite similar)
756 * The keyword/conditional implementation is currently based on sections of code from
757 * the Smarty templating engine (c) 2001 ispi of Lincoln, Inc. which is released
758 * (on its own and in whole) under the LGPL. Section 3 of the LGPL states that any code
759 * derived from an LGPL application may be relicenced under the GPL, this applies
760 * to this source
762 * DEFINE directive inspired by a request by Cyberalien
764 * @package phpBB3
765 * @uses template_filter As a PHP stream filter to perform compilation of templates
767 class phpbb_template_compile
770 * @var template Reference to the {@link template template} object performing compilation
772 private $template;
775 * Constructor
776 * @param template $template {@link template Template} object performing compilation
778 function __construct(phpbb_template $template)
780 $this->template = $template;
784 * Load template source from file
785 * @access public
786 * @param string $handle Template handle we wish to load
787 * @return bool Return true on success otherwise false
789 public function _tpl_load_file($handle)
791 // Try and open template for read
792 if (!file_exists($this->template->files[$handle]))
794 trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR);
797 // Actually compile the code now.
798 return $this->compile_write($handle, $this->template->files[$handle]);
802 * Load template source from file
803 * @access public
804 * @param string $handle Template handle we wish to compile
805 * @return string|bool Return compiled code on successful compilation otherwise false
807 public function _tpl_gen_src($handle)
809 // Try and open template for read
810 if (!file_exists($this->template->files[$handle]))
812 trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR);
815 // Actually compile the code now.
816 return $this->compile_gen($this->template->files[$handle]);
820 * Write compiled file to cache directory
821 * @access private
822 * @param string $handle Template handle to compile
823 * @param string $source_file Source template file
824 * @return bool Return true on success otherwise false
826 private function compile_write($handle, $source_file)
828 $filename = $this->template->cachepath . str_replace('/', '.', $this->template->filename[$handle]) . '.' . PHP_EXT;
830 $source_handle = @fopen($source_file, 'rb');
831 $destination_handle = @fopen($filename, 'wb');
833 if (!$source_handle || !$destination_handle)
835 return false;
838 @flock($destination_handle, LOCK_EX);
840 stream_filter_append($source_handle, 'phpbb_template');
841 stream_copy_to_stream($source_handle, $destination_handle);
843 @fclose($source_handle);
844 @flock($destination_handle, LOCK_UN);
845 @fclose($destination_handle);
847 phpbb::$system->chmod($filename, phpbb::CHMOD_READ | phpbb::CHMOD_WRITE);
849 clearstatcache();
851 return true;
855 * Generate source for eval()
856 * @access private
857 * @param string $source_file Source template file
858 * @return string|bool Return compiled code on successful compilation otherwise false
860 private function compile_gen($source_file)
862 $source_handle = @fopen($source_file, 'rb');
863 $destination_handle = @fopen('php://temp' ,'r+b');
865 if (!$source_handle || !$destination_handle)
867 return false;
870 stream_filter_append($source_handle, 'phpbb_template');
871 stream_copy_to_stream($source_handle, $destination_handle);
873 @fclose($source_handle);
875 rewind($destination_handle);
876 return stream_get_contents($destination_handle);