Advertisement
Guest User

RainTPL

a guest
Aug 4th, 2015
254
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 35.11 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4.  *  RainTPL
  5.  *  -------
  6.  *  Realized by Federico Ulfo & maintained by the Rain Team
  7.  *  Distributed under GNU/LGPL 3 License
  8.  *
  9.  *  @version 2.7.2
  10.  */
  11.  
  12.  
  13. class RainTPL{
  14.  
  15.     // -------------------------
  16.     //  CONFIGURATION
  17.     // -------------------------
  18.  
  19.         /**
  20.          * Template directory
  21.          *
  22.          * @var string
  23.          */
  24.         static $tpl_dir = "templates/";
  25.  
  26.  
  27.         /**
  28.          * Cache directory. Is the directory where RainTPL will compile the template and save the cache
  29.          *
  30.          * @var string
  31.          */
  32.         static $cache_dir = "tmp/";
  33.  
  34.  
  35.         /**
  36.          * Template base URL. RainTPL will add this URL to the relative paths of element selected in $path_replace_list.
  37.          *
  38.          * @var string
  39.          */
  40.         static $base_url = null;
  41.  
  42.  
  43.         /**
  44.          * Template extension.
  45.          *
  46.          * @var string
  47.          */
  48.         static $tpl_ext = "html";
  49.  
  50.  
  51.         /**
  52.          * Path replace is a cool features that replace all relative paths of images (<img src="...">), stylesheet (<link href="...">), script (<script src="...">) and link (<a href="...">)
  53.          * Set true to enable the path replace.
  54.          *
  55.          * @var unknown_type
  56.          */
  57.         static $path_replace = true;
  58.  
  59.  
  60.         /**
  61.          * You can set what the path_replace method will replace.
  62.          * Avaible options: a, img, link, script, input
  63.          *
  64.          * @var array
  65.          */
  66.         static $path_replace_list = array( 'a', 'img', 'link', 'script', 'input' );
  67.  
  68.  
  69.         /**
  70.          * You can define in the black list what string are disabled into the template tags
  71.          *
  72.          * @var unknown_type
  73.          */
  74.         static $black_list = array( '\$this', 'raintpl::', 'self::', '_SESSION', '_SERVER', '_ENV',  'eval', 'exec', 'unlink', 'rmdir' );
  75.  
  76.  
  77.         /**
  78.          * Check template.
  79.          * true: checks template update time, if changed it compile them
  80.          * false: loads the compiled template. Set false if server doesn't have write permission for cache_directory.
  81.          *
  82.          */
  83.         static $check_template_update = true;
  84.                
  85.  
  86.         /**
  87.          * PHP tags <? ?>
  88.          * True: php tags are enabled into the template
  89.          * False: php tags are disabled into the template and rendered as html
  90.          *
  91.          * @var bool
  92.          */
  93.         static $php_enabled = false;
  94.  
  95.        
  96.         /**
  97.          * Debug mode flag.
  98.          * True: debug mode is used, syntax errors are displayed directly in template. Execution of script is not terminated.
  99.          * False: exception is thrown on found error.
  100.          *
  101.          * @var bool
  102.          */
  103.         static $debug = false;
  104.  
  105.     // -------------------------
  106.  
  107.  
  108.     // -------------------------
  109.     //  RAINTPL VARIABLES
  110.     // -------------------------
  111.  
  112.         /**
  113.          * Is the array where RainTPL keep the variables assigned
  114.          *
  115.          * @var array
  116.          */
  117.         public $var = array();
  118.  
  119.         protected $tpl = array(),       // variables to keep the template directories and info
  120.                   $cache = false,       // static cache enabled / disabled
  121.                   $cache_id = null;       // identify only one cache
  122.  
  123.                 protected static $config_name_sum = array();   // takes all the config to create the md5 of the file
  124.  
  125.     // -------------------------
  126.  
  127.  
  128.  
  129.     const CACHE_EXPIRE_TIME = 3600; // default cache expire time = hour
  130.  
  131.  
  132.  
  133.     /**
  134.      * Assign variable
  135.      * eg.  $t->assign('name','mickey');
  136.      *
  137.      * @param mixed $variable_name Name of template variable or associative array name/value
  138.      * @param mixed $value value assigned to this variable. Not set if variable_name is an associative array
  139.      */
  140.  
  141.     function assign( $variable, $value = null ){
  142.         if( is_array( $variable ) )
  143.             $this->var += $variable;
  144.         else
  145.             $this->var[ $variable ] = $value;
  146.     }
  147.  
  148.  
  149.  
  150.     /**
  151.      * Draw the template
  152.      * eg.  $html = $tpl->draw( 'demo', TRUE ); // return template in string
  153.      * or   $tpl->draw( $tpl_name ); // echo the template
  154.      *
  155.      * @param string $tpl_name  template to load
  156.      * @param boolean $return_string  true=return a string, false=echo the template
  157.      * @return string
  158.      */
  159.  
  160.     function draw( $tpl_name, $return_string = false ){
  161.  
  162.         try {
  163.             // compile the template if necessary and set the template filepath
  164.             $this->check_template( $tpl_name );
  165.         } catch (RainTpl_Exception $e) {
  166.             $output = $this->printDebug($e);
  167.             die($output);
  168.         }
  169.  
  170.         // Cache is off and, return_string is false
  171.         // Rain just echo the template
  172.  
  173.         if( !$this->cache && !$return_string ){
  174.             extract( $this->var );
  175.             include $this->tpl['compiled_filename'];
  176.             unset( $this->tpl );
  177.         }
  178.  
  179.  
  180.         // cache or return_string are enabled
  181.         // rain get the output buffer to save the output in the cache or to return it as string
  182.  
  183.         else{
  184.  
  185.             //----------------------
  186.             // get the output buffer
  187.             //----------------------
  188.                 ob_start();
  189.                 extract( $this->var );
  190.                 include $this->tpl['compiled_filename'];
  191.                 $raintpl_contents = ob_get_clean();
  192.             //----------------------
  193.  
  194.  
  195.             // save the output in the cache
  196.             if( $this->cache )
  197.                 file_put_contents( $this->tpl['cache_filename'], "<?php if(!class_exists('raintpl')){exit;}?>" . $raintpl_contents );
  198.  
  199.             // free memory
  200.             unset( $this->tpl );
  201.  
  202.             // return or print the template
  203.             if( $return_string ) return $raintpl_contents; else echo $raintpl_contents;
  204.  
  205.         }
  206.  
  207.     }
  208.  
  209.  
  210.  
  211.     /**
  212.      * If exists a valid cache for this template it returns the cache
  213.      *
  214.      * @param string $tpl_name Name of template (set the same of draw)
  215.      * @param int $expiration_time Set after how many seconds the cache expire and must be regenerated
  216.      * @return string it return the HTML or null if the cache must be recreated
  217.      */
  218.  
  219.     function cache( $tpl_name, $expire_time = self::CACHE_EXPIRE_TIME, $cache_id = null ){
  220.  
  221.         // set the cache_id
  222.         $this->cache_id = $cache_id;
  223.  
  224.         if( !$this->check_template( $tpl_name ) && file_exists( $this->tpl['cache_filename'] ) && ( time() - filemtime( $this->tpl['cache_filename'] ) < $expire_time ) )
  225.             return substr( file_get_contents( $this->tpl['cache_filename'] ), 43 );
  226.         else{
  227.             //delete the cache of the selected template
  228.             if (file_exists($this->tpl['cache_filename']))
  229.             unlink($this->tpl['cache_filename'] );
  230.             $this->cache = true;
  231.         }
  232.     }
  233.  
  234.  
  235.  
  236.     /**
  237.      * Configure the settings of RainTPL
  238.      *
  239.      */
  240.     static function configure( $setting, $value = null ){
  241.         if( is_array( $setting ) )
  242.             foreach( $setting as $key => $value )
  243.                 self::configure( $key, $value );
  244.         else if( property_exists( __CLASS__, $setting ) ){
  245.             self::$$setting = $value;
  246.             self::$config_name_sum[ $setting ] = $value; // take trace of all config
  247.         }
  248.     }
  249.  
  250.  
  251.  
  252.     // check if has to compile the template
  253.     // return true if the template has changed
  254.     protected function check_template( $tpl_name ){
  255.  
  256.         if( !isset($this->tpl['checked']) ){
  257.  
  258.             $tpl_basename                       = basename( $tpl_name );                                                        // template basename
  259.             $tpl_basedir                        = strpos($tpl_name,"/") ? dirname($tpl_name) . '/' : null;                      // template basedirectory
  260.             $tpl_dir                            = self::$tpl_dir . $tpl_basedir;                                // template directory
  261.             $this->tpl['tpl_filename']          = $tpl_dir . $tpl_basename . '.' . self::$tpl_ext;  // template filename
  262.             $temp_compiled_filename             = self::$cache_dir . $tpl_basename . "." . md5( $tpl_dir . serialize(self::$config_name_sum));
  263.             $this->tpl['compiled_filename']     = $temp_compiled_filename . '.rtpl.php';    // cache filename
  264.             $this->tpl['cache_filename']        = $temp_compiled_filename . '.s_' . $this->cache_id . '.rtpl.php';  // static cache filename
  265.  
  266.             // if the template doesn't exsist throw an error
  267.             if( self::$check_template_update && !file_exists( $this->tpl['tpl_filename'] ) ){
  268.                 $e = new RainTpl_NotFoundException( 'Template '. $tpl_basename .' not found!' );
  269.                 throw $e->setTemplateFile($this->tpl['tpl_filename']);
  270.             }
  271.  
  272.             // file doesn't exsist, or the template was updated, Rain will compile the template
  273.             if( !file_exists( $this->tpl['compiled_filename'] ) || ( self::$check_template_update && filemtime($this->tpl['compiled_filename']) < filemtime( $this->tpl['tpl_filename'] ) ) ){
  274.                 $this->compileFile( $tpl_basename, $tpl_basedir, $this->tpl['tpl_filename'], self::$cache_dir, $this->tpl['compiled_filename'] );
  275.                 return true;
  276.             }
  277.             $this->tpl['checked'] = true;
  278.         }
  279.     }
  280.  
  281.  
  282.     /**
  283.     * execute stripslaches() on the xml block. Invoqued by preg_replace_callback function below
  284.     * @access protected
  285.     */
  286.     protected function xml_reSubstitution($capture) {
  287.             return "<?php echo '<?xml ".stripslashes($capture[1])." ?>'; ?>";
  288.     }
  289.  
  290.     /**
  291.      * Compile and write the compiled template file
  292.      * @access protected
  293.      */
  294.     protected function compileFile( $tpl_basename, $tpl_basedir, $tpl_filename, $cache_dir, $compiled_filename ){
  295.  
  296.         //read template file
  297.         $this->tpl['source'] = $template_code = file_get_contents( $tpl_filename );
  298.  
  299.         //xml substitution
  300.         $template_code = preg_replace( "/<\?xml(.*?)\?>/s", "##XML\\1XML##", $template_code );
  301.  
  302.         //disable php tag
  303.         if( !self::$php_enabled )
  304.             $template_code = str_replace( array("<?","?>"), array("&lt;?","?&gt;"), $template_code );
  305.  
  306.         //xml re-substitution
  307.         $template_code = preg_replace_callback ( "/##XML(.*?)XML##/s", array($this, 'xml_reSubstitution'), $template_code );
  308.  
  309.         //compile template
  310.         $template_compiled = "<?php if(!class_exists('raintpl')){exit;}?>" . $this->compileTemplate( $template_code, $tpl_basedir );
  311.        
  312.  
  313.         // fix the php-eating-newline-after-closing-tag-problem
  314.         $template_compiled = str_replace( "?>\n", "?>\n\n", $template_compiled );
  315.  
  316.         // create directories
  317.         if( !is_dir( $cache_dir ) )
  318.             mkdir( $cache_dir, 0755, true );
  319.  
  320.         if( !is_writable( $cache_dir ) )
  321.             throw new RainTpl_Exception ('Cache directory ' . $cache_dir . 'doesn\'t have write permission. Set write permission or set RAINTPL_CHECK_TEMPLATE_UPDATE to false. More details on http://www.raintpl.com/Documentation/Documentation-for-PHP-developers/Configuration/');
  322.  
  323.         //write compiled file
  324.         file_put_contents( $compiled_filename, $template_compiled );
  325.     }
  326.  
  327.  
  328.  
  329.     /**
  330.      * Compile template
  331.      * @access protected
  332.      */
  333.     protected function compileTemplate( $template_code, $tpl_basedir ){
  334.  
  335.         //tag list
  336.         $tag_regexp = array( 'loop'         => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})',
  337.                              'loop_close'   => '(\{\/loop\})',
  338.                              'if'           => '(\{if(?: condition){0,1}="[^"]*"\})',
  339.                              'elseif'       => '(\{elseif(?: condition){0,1}="[^"]*"\})',
  340.                              'else'         => '(\{else\})',
  341.                              'if_close'     => '(\{\/if\})',
  342.                              'function'     => '(\{function="[^"]*"\})',
  343.                              'noparse'      => '(\{noparse\})',
  344.                              'noparse_close'=> '(\{\/noparse\})',
  345.                              'ignore'       => '(\{ignore\}|\{\*)',
  346.                              'ignore_close' => '(\{\/ignore\}|\*\})',
  347.                              'include'      => '(\{include="[^"]*"(?: cache="[^"]*")?\})',
  348.                              'template_info'=> '(\{\$template_info\})',
  349.                              'function'     => '(\{function="(\w*?)(?:.*?)"\})'
  350.                             );
  351.  
  352.         $tag_regexp = "/" . join( "|", $tag_regexp ) . "/";
  353.  
  354.         //split the code with the tags regexp
  355.         $template_code = preg_split ( $tag_regexp, $template_code, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
  356.  
  357.         //path replace (src of img, background and href of link)
  358.         $template_code = $this->path_replace( $template_code, $tpl_basedir );
  359.  
  360.         //compile the code
  361.         $compiled_code = $this->compileCode( $template_code );
  362.  
  363.         //return the compiled code
  364.         return $compiled_code;
  365.  
  366.     }
  367.  
  368.  
  369.  
  370.     /**
  371.      * Compile the code
  372.      * @access protected
  373.      */
  374.     protected function compileCode( $parsed_code ){
  375.  
  376.         //variables initialization
  377.         $compiled_code = $open_if = $comment_is_open = $ignore_is_open = null;
  378.         $loop_level = 0;
  379.  
  380.         //read all parsed code
  381.         while( $html = array_shift( $parsed_code ) ){
  382.  
  383.             //close ignore tag
  384.             if( !$comment_is_open && ( strpos( $html, '{/ignore}' ) !== FALSE || strpos( $html, '*}' ) !== FALSE ) )
  385.                 $ignore_is_open = false;
  386.  
  387.             //code between tag ignore id deleted
  388.             elseif( $ignore_is_open ){
  389.                 //ignore the code
  390.             }
  391.  
  392.             //close no parse tag
  393.             elseif( strpos( $html, '{/noparse}' ) !== FALSE )
  394.                 $comment_is_open = false;
  395.  
  396.             //code between tag noparse is not compiled
  397.             elseif( $comment_is_open )
  398.                 $compiled_code .= $html;
  399.  
  400.             //ignore
  401.             elseif( strpos( $html, '{ignore}' ) !== FALSE || strpos( $html, '{*' ) !== FALSE )
  402.                 $ignore_is_open = true;
  403.  
  404.             //noparse
  405.             elseif( strpos( $html, '{noparse}' ) !== FALSE )
  406.                 $comment_is_open = true;
  407.  
  408.             //include tag
  409.             elseif( preg_match( '/\{include="([^"]*)"(?: cache="([^"]*)"){0,1}\}/', $html, $code ) ){
  410.  
  411.                 //variables substitution
  412.                 $include_var = $this->var_replace( $code[ 1 ], $left_delimiter = null, $right_delimiter = null, $php_left_delimiter = '".' , $php_right_delimiter = '."', $loop_level );
  413.  
  414.                 // if the cache is active
  415.                 if( isset($code[ 2 ]) ){
  416.                    
  417.                     //dynamic include
  418.                     $compiled_code .= '<?php $tpl = new '.get_class($this).';' .
  419.                                  'if( $cache = $tpl->cache( $template = basename("'.$include_var.'") ) )' .
  420.                                  '  echo $cache;' .
  421.                                  'else{' .
  422.                                  '  $tpl_dir_temp = self::$tpl_dir;' .
  423.                                  '  $tpl->assign( $this->var );' .
  424.                                     ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
  425.                                  '  $tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
  426.                                  '} ?>';
  427.                 }
  428.                 else{
  429.    
  430.                     //dynamic include
  431.                     $compiled_code .= '<?php $tpl = new '.get_class($this).';' .
  432.                                       '$tpl_dir_temp = self::$tpl_dir;' .
  433.                                       '$tpl->assign( $this->var );' .
  434.                                       ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
  435.                                       '$tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
  436.                                       '?>';
  437.                    
  438.                    
  439.                 }
  440.  
  441.             }
  442.  
  443.             //loop
  444.             elseif( preg_match( '/\{loop(?: name){0,1}="\${0,1}([^"]*)"\}/', $html, $code ) ){
  445.  
  446.                 //increase the loop counter
  447.                 $loop_level++;
  448.  
  449.                 //replace the variable in the loop
  450.                 $var = $this->var_replace( '$' . $code[ 1 ], $tag_left_delimiter=null, $tag_right_delimiter=null, $php_left_delimiter=null, $php_right_delimiter=null, $loop_level-1 );
  451.  
  452.                 //loop variables
  453.                 $counter = "\$counter$loop_level";       // count iteration
  454.                 $key = "\$key$loop_level";               // key
  455.                 $value = "\$value$loop_level";           // value
  456.  
  457.                 //loop code
  458.                 $compiled_code .=  "<?php $counter=-1; if( isset($var) && is_array($var) && sizeof($var) ) foreach( $var as $key => $value ){ $counter++; ?>";
  459.  
  460.             }
  461.  
  462.             //close loop tag
  463.             elseif( strpos( $html, '{/loop}' ) !== FALSE ) {
  464.  
  465.                 //iterator
  466.                 $counter = "\$counter$loop_level";
  467.  
  468.                 //decrease the loop counter
  469.                 $loop_level--;
  470.  
  471.                 //close loop code
  472.                 $compiled_code .=  "<?php } ?>";
  473.  
  474.             }
  475.  
  476.             //if
  477.             elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
  478.  
  479.                 //increase open if counter (for intendation)
  480.                 $open_if++;
  481.  
  482.                 //tag
  483.                 $tag = $code[ 0 ];
  484.  
  485.                 //condition attribute
  486.                 $condition = $code[ 1 ];
  487.  
  488.                 // check if there's any function disabled by black_list
  489.                 $this->function_check( $tag );
  490.  
  491.                 //variable substitution into condition (no delimiter into the condition)
  492.                 $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
  493.  
  494.                 //if code
  495.                 $compiled_code .=   "<?php if( $parsed_condition ){ ?>";
  496.  
  497.             }
  498.  
  499.             //elseif
  500.             elseif( preg_match( '/\{elseif(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
  501.  
  502.                 //tag
  503.                 $tag = $code[ 0 ];
  504.  
  505.                 //condition attribute
  506.                 $condition = $code[ 1 ];
  507.  
  508.                 //variable substitution into condition (no delimiter into the condition)
  509.                 $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
  510.  
  511.                 //elseif code
  512.                 $compiled_code .=   "<?php }elseif( $parsed_condition ){ ?>";
  513.             }
  514.  
  515.             //else
  516.             elseif( strpos( $html, '{else}' ) !== FALSE ) {
  517.  
  518.                 //else code
  519.                 $compiled_code .=   '<?php }else{ ?>';
  520.  
  521.             }
  522.  
  523.             //close if tag
  524.             elseif( strpos( $html, '{/if}' ) !== FALSE ) {
  525.  
  526.                 //decrease if counter
  527.                 $open_if--;
  528.  
  529.                 // close if code
  530.                 $compiled_code .=   '<?php } ?>';
  531.  
  532.             }
  533.  
  534.             //function
  535.             elseif( preg_match( '/\{function="(\w*)(.*?)"\}/', $html, $code ) ){
  536.  
  537.                 //tag
  538.                 $tag = $code[ 0 ];
  539.  
  540.                 //function
  541.                 $function = $code[ 1 ];
  542.  
  543.                 // check if there's any function disabled by black_list
  544.                 $this->function_check( $tag );
  545.  
  546.                 if( empty( $code[ 2 ] ) )
  547.                     $parsed_function = $function . "()";
  548.                 else
  549.                     // parse the function
  550.                     $parsed_function = $function . $this->var_replace( $code[ 2 ], $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
  551.                
  552.                 //if code
  553.                 $compiled_code .=   "<?php echo $parsed_function; ?>";
  554.             }
  555.  
  556.             // show all vars
  557.             elseif ( strpos( $html, '{$template_info}' ) !== FALSE ) {
  558.  
  559.                 //tag
  560.                 $tag  = '{$template_info}';
  561.  
  562.                 //if code
  563.                 $compiled_code .=   '<?php echo "<pre>"; print_r( $this->var ); echo "</pre>"; ?>';
  564.             }
  565.  
  566.  
  567.             //all html code
  568.             else{
  569.  
  570.                 //variables substitution (es. {$title})
  571.                 $html = $this->var_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
  572.                 //const substitution (es. {#CONST#})
  573.                 $html = $this->const_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
  574.                 //functions substitution (es. {"string"|functions})
  575.                 $compiled_code .= $this->func_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
  576.             }
  577.         }
  578.  
  579.         if( $open_if > 0 ) {
  580.             $e = new RainTpl_SyntaxException('Error! You need to close an {if} tag in ' . $this->tpl['tpl_filename'] . ' template');
  581.             throw $e->setTemplateFile($this->tpl['tpl_filename']);
  582.         }
  583.         return $compiled_code;
  584.     }
  585.    
  586.    
  587.     /**
  588.      * Reduce a path, eg. www/library/../filepath//file => www/filepath/file
  589.      * @param type $path
  590.      * @return type
  591.      */
  592.     protected function reduce_path( $path ){
  593.         $path = str_replace( "://", "@not_replace@", $path );
  594.         $path = str_replace( "//", "/", $path );
  595.         $path = str_replace( "@not_replace@", "://", $path );
  596.         return preg_replace('/\w+\/\.\.\//', '', $path );
  597.     }
  598.  
  599.  
  600.  
  601.     /**
  602.      * replace the path of image src, link href and a href.
  603.      * url => template_dir/url
  604.      * url# => url
  605.      * http://url => http://url
  606.      *
  607.      * @param string $html
  608.      * @return string html sostituito
  609.      */
  610.     protected function path_replace( $html, $tpl_basedir ){
  611.  
  612.         if( self::$path_replace ){
  613.  
  614.             $tpl_dir = self::$base_url . self::$tpl_dir . $tpl_basedir;
  615.            
  616.             // reduce the path
  617.             $path = $this->reduce_path($tpl_dir);
  618.  
  619.             $exp = $sub = array();
  620.  
  621.             if( in_array( "img", self::$path_replace_list ) ){
  622.                 $exp = array( '/<img(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<img(.*?)src=(?:")([^"]+?)#(?:")/i', '/<img(.*?)src="(.*?)"/', '/<img(.*?)src=(?:\@)([^"]+?)(?:\@)/i' );
  623.                 $sub = array( '<img$1src=@$2://$3@', '<img$1src=@$2@', '<img$1src="' . $path . '$2"', '<img$1src="$2"' );
  624.             }
  625.  
  626.             if( in_array( "script", self::$path_replace_list ) ){
  627.                 $exp = array_merge( $exp , array( '/<script(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<script(.*?)src=(?:")([^"]+?)#(?:")/i', '/<script(.*?)src="(.*?)"/', '/<script(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
  628.                 $sub = array_merge( $sub , array( '<script$1src=@$2://$3@', '<script$1src=@$2@', '<script$1src="' . $path . '$2"', '<script$1src="$2"' ) );
  629.             }
  630.  
  631.             if( in_array( "link", self::$path_replace_list ) ){
  632.                 $exp = array_merge( $exp , array( '/<link(.*?)href=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<link(.*?)href=(?:")([^"]+?)#(?:")/i', '/<link(.*?)href="(.*?)"/', '/<link(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
  633.                 $sub = array_merge( $sub , array( '<link$1href=@$2://$3@', '<link$1href=@$2@' , '<link$1href="' . $path . '$2"', '<link$1href="$2"' ) );
  634.             }
  635.  
  636.             if( in_array( "a", self::$path_replace_list ) ){
  637.                 $exp = array_merge( $exp , array( '/<a(.*?)href=(?:")(http\:\/\/|https\:\/\/|javascript:)([^"]+?)(?:")/i', '/<a(.*?)href="(.*?)"/', '/<a(.*?)href=(?:\@)([^"]+?)(?:\@)/i'  ) );
  638.                 $sub = array_merge( $sub , array( '<a$1href=@$2$3@', '<a$1href="' . self::$base_url . '$2"', '<a$1href="$2"' ) );
  639.             }
  640.  
  641.             if( in_array( "input", self::$path_replace_list ) ){
  642.                 $exp = array_merge( $exp , array( '/<input(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<input(.*?)src=(?:")([^"]+?)#(?:")/i', '/<input(.*?)src="(.*?)"/', '/<input(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
  643.                 $sub = array_merge( $sub , array( '<input$1src=@$2://$3@', '<input$1src=@$2@', '<input$1src="' . $path . '$2"', '<input$1src="$2"' ) );
  644.             }
  645.  
  646.             return preg_replace( $exp, $sub, $html );
  647.  
  648.         }
  649.         else
  650.             return $html;
  651.  
  652.     }
  653.  
  654.  
  655.  
  656.  
  657.  
  658.     // replace const
  659.     function const_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
  660.         // const
  661.         return preg_replace( '/\{\#(\w+)\#{0,1}\}/', $php_left_delimiter . ( $echo ? " echo " : null ) . '\\1' . $php_right_delimiter, $html );
  662.     }
  663.  
  664.  
  665.  
  666.     // replace functions/modifiers on constants and strings
  667.     function func_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
  668.  
  669.         preg_match_all( '/' . '\{\#{0,1}(\"{0,1}.*?\"{0,1})(\|\w.*?)\#{0,1}\}' . '/', $html, $matches );
  670.  
  671.         for( $i=0, $n=count($matches[0]); $i<$n; $i++ ){
  672.  
  673.             //complete tag ex: {$news.title|substr:0,100}
  674.             $tag = $matches[ 0 ][ $i ];
  675.  
  676.             //variable name ex: news.title
  677.             $var = $matches[ 1 ][ $i ];
  678.  
  679.             //function and parameters associate to the variable ex: substr:0,100
  680.             $extra_var = $matches[ 2 ][ $i ];
  681.  
  682.             // check if there's any function disabled by black_list
  683.             $this->function_check( $tag );
  684.  
  685.             $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
  686.            
  687.  
  688.             // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
  689.             $is_init_variable = preg_match( "/^(\s*?)\=[^=](.*?)$/", $extra_var );
  690.  
  691.             //function associate to variable
  692.             $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
  693.  
  694.             //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
  695.             $temp = preg_split( "/\.|\[|\-\>/", $var );
  696.  
  697.             //variable name
  698.             $var_name = $temp[ 0 ];
  699.  
  700.             //variable path
  701.             $variable_path = substr( $var, strlen( $var_name ) );
  702.  
  703.             //parentesis transform [ e ] in [" e in "]
  704.             $variable_path = str_replace( '[', '["', $variable_path );
  705.             $variable_path = str_replace( ']', '"]', $variable_path );
  706.  
  707.             //transform .$variable in ["$variable"]
  708.             $variable_path = preg_replace('/\.\$(\w+)/', '["$\\1"]', $variable_path );
  709.  
  710.             //transform [variable] in ["variable"]
  711.             $variable_path = preg_replace('/\.(\w+)/', '["\\1"]', $variable_path );
  712.  
  713.             //if there's a function
  714.             if( $function_var ){
  715.                
  716.                 // check if there's a function or a static method and separate, function by parameters
  717.                 $function_var = str_replace("::", "@double_dot@", $function_var );
  718.  
  719.                 // get the position of the first :
  720.                 if( $dot_position = strpos( $function_var, ":" ) ){
  721.  
  722.                     // get the function and the parameters
  723.                     $function = substr( $function_var, 0, $dot_position );
  724.                     $params = substr( $function_var, $dot_position+1 );
  725.  
  726.                 }
  727.                 else{
  728.  
  729.                     //get the function
  730.                     $function = str_replace( "@double_dot@", "::", $function_var );
  731.                     $params = null;
  732.  
  733.                 }
  734.  
  735.                 // replace back the @double_dot@ with ::
  736.                 $function = str_replace( "@double_dot@", "::", $function );
  737.                 $params = str_replace( "@double_dot@", "::", $params );
  738.  
  739.  
  740.             }
  741.             else
  742.                 $function = $params = null;
  743.  
  744.             $php_var = $var_name . $variable_path;
  745.  
  746.             // compile the variable for php
  747.             if( isset( $function ) ){
  748.                 if( $php_var )
  749.                     $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
  750.                 else
  751.                     $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $params ) )" : "$function()" ) . $php_right_delimiter;
  752.             }
  753.             else
  754.                 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
  755.  
  756.             $html = str_replace( $tag, $php_var, $html );
  757.  
  758.         }
  759.  
  760.         return $html;
  761.  
  762.     }
  763.  
  764.  
  765.  
  766.     function var_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
  767.  
  768.         //all variables
  769.         if( preg_match_all( '/' . $tag_left_delimiter . '\$(\w+(?:\.\${0,1}[A-Za-z0-9_]+)*(?:(?:\[\${0,1}[A-Za-z0-9_]+\])|(?:\-\>\${0,1}[A-Za-z0-9_]+))*)(.*?)' . $tag_right_delimiter . '/', $html, $matches ) ){
  770.  
  771.                     for( $parsed=array(), $i=0, $n=count($matches[0]); $i<$n; $i++ )
  772.                         $parsed[$matches[0][$i]] = array('var'=>$matches[1][$i],'extra_var'=>$matches[2][$i]);
  773.  
  774.                     foreach( $parsed as $tag => $array ){
  775.  
  776.                             //variable name ex: news.title
  777.                             $var = $array['var'];
  778.  
  779.                             //function and parameters associate to the variable ex: substr:0,100
  780.                             $extra_var = $array['extra_var'];
  781.  
  782.                             // check if there's any function disabled by black_list
  783.                             $this->function_check( $tag );
  784.  
  785.                             $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
  786.  
  787.                             // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
  788.                             $is_init_variable = preg_match( "/^[a-z_A-Z\.\[\](\-\>)]*=[^=]*$/", $extra_var );
  789.                            
  790.                             //function associate to variable
  791.                             $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
  792.  
  793.                             //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
  794.                             $temp = preg_split( "/\.|\[|\-\>/", $var );
  795.  
  796.                             //variable name
  797.                             $var_name = $temp[ 0 ];
  798.  
  799.                             //variable path
  800.                             $variable_path = substr( $var, strlen( $var_name ) );
  801.  
  802.                             //parentesis transform [ e ] in [" e in "]
  803.                             $variable_path = str_replace( '[', '["', $variable_path );
  804.                             $variable_path = str_replace( ']', '"]', $variable_path );
  805.  
  806.                             //transform .$variable in ["$variable"] and .variable in ["variable"]
  807.                             $variable_path = preg_replace('/\.(\${0,1}\w+)/', '["\\1"]', $variable_path );
  808.                            
  809.                             // if is an assignment also assign the variable to $this->var['value']
  810.                             if( $is_init_variable )
  811.                                 $extra_var = "=\$this->var['{$var_name}']{$variable_path}" . $extra_var;
  812.  
  813.                                
  814.  
  815.                             //if there's a function
  816.                             if( $function_var ){
  817.                                
  818.                                     // check if there's a function or a static method and separate, function by parameters
  819.                                     $function_var = str_replace("::", "@double_dot@", $function_var );
  820.  
  821.  
  822.                                     // get the position of the first :
  823.                                     if( $dot_position = strpos( $function_var, ":" ) ){
  824.  
  825.                                         // get the function and the parameters
  826.                                         $function = substr( $function_var, 0, $dot_position );
  827.                                         $params = substr( $function_var, $dot_position+1 );
  828.  
  829.                                     }
  830.                                     else{
  831.  
  832.                                         //get the function
  833.                                         $function = str_replace( "@double_dot@", "::", $function_var );
  834.                                         $params = null;
  835.  
  836.                                     }
  837.  
  838.                                     // replace back the @double_dot@ with ::
  839.                                     $function = str_replace( "@double_dot@", "::", $function );
  840.                                     $params = str_replace( "@double_dot@", "::", $params );
  841.                             }
  842.                             else
  843.                                     $function = $params = null;
  844.  
  845.                             //if it is inside a loop
  846.                             if( $loop_level ){
  847.                                     //verify the variable name
  848.                                     if( $var_name == 'key' )
  849.                                             $php_var = '$key' . $loop_level;
  850.                                     elseif( $var_name == 'value' )
  851.                                             $php_var = '$value' . $loop_level . $variable_path;
  852.                                     elseif( $var_name == 'counter' )
  853.                                             $php_var = '$counter' . $loop_level;
  854.                                     else
  855.                                             $php_var = '$' . $var_name . $variable_path;
  856.                             }else
  857.                                     $php_var = '$' . $var_name . $variable_path;
  858.                            
  859.                             // compile the variable for php
  860.                             if( isset( $function ) )
  861.                                     $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
  862.                             else
  863.                                     $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
  864.                            
  865.                             $html = str_replace( $tag, $php_var, $html );
  866.  
  867.  
  868.                     }
  869.                 }
  870.  
  871.         return $html;
  872.     }
  873.  
  874.  
  875.  
  876.     /**
  877.      * Check if function is in black list (sandbox)
  878.      *
  879.      * @param string $code
  880.      * @param string $tag
  881.      */
  882.     protected function function_check( $code ){
  883.  
  884.         $preg = '#(\W|\s)' . implode( '(\W|\s)|(\W|\s)', self::$black_list ) . '(\W|\s)#';
  885.  
  886.         // check if the function is in the black list (or not in white list)
  887.         if( count(self::$black_list) && preg_match( $preg, $code, $match ) ){
  888.  
  889.             // find the line of the error
  890.             $line = 0;
  891.             $rows=explode("\n",$this->tpl['source']);
  892.             while( !strpos($rows[$line],$code) )
  893.                 $line++;
  894.  
  895.             // stop the execution of the script
  896.             $e = new RainTpl_SyntaxException('Unallowed syntax in ' . $this->tpl['tpl_filename'] . ' template');
  897.             throw $e->setTemplateFile($this->tpl['tpl_filename'])
  898.                 ->setTag($code)
  899.                 ->setTemplateLine($line);
  900.         }
  901.  
  902.     }
  903.  
  904.     /**
  905.      * Prints debug info about exception or passes it further if debug is disabled.
  906.      *
  907.      * @param RainTpl_Exception $e
  908.      * @return string
  909.      */
  910.     protected function printDebug(RainTpl_Exception $e){
  911.         if (!self::$debug) {
  912.             throw $e;
  913.         }
  914.         $output = sprintf('<h2>Exception: %s</h2><h3>%s</h3><p>template: %s</p>',
  915.             get_class($e),
  916.             $e->getMessage(),
  917.             $e->getTemplateFile()
  918.         );
  919.         if ($e instanceof RainTpl_SyntaxException) {
  920.             if (null != $e->getTemplateLine()) {
  921.                 $output .= '<p>line: ' . $e->getTemplateLine() . '</p>';
  922.             }
  923.             if (null != $e->getTag()) {
  924.                 $output .= '<p>in tag: ' . htmlspecialchars($e->getTag()) . '</p>';
  925.             }
  926.             if (null != $e->getTemplateLine() && null != $e->getTag()) {
  927.                 $rows=explode("\n",  htmlspecialchars($this->tpl['source']));
  928.                 $rows[$e->getTemplateLine()] = '<font color=red>' . $rows[$e->getTemplateLine()] . '</font>';
  929.                 $output .= '<h3>template code</h3>' . implode('<br />', $rows) . '</pre>';
  930.             }
  931.         }
  932.         $output .= sprintf('<h3>trace</h3><p>In %s on line %d</p><pre>%s</pre>',
  933.             $e->getFile(), $e->getLine(),
  934.             nl2br(htmlspecialchars($e->getTraceAsString()))
  935.         );
  936.         return $output;
  937.     }
  938. }
  939.  
  940.  
  941. /**
  942.  * Basic Rain tpl exception.
  943.  */
  944. class RainTpl_Exception extends Exception{
  945.     /**
  946.      * Path of template file with error.
  947.      */
  948.     protected $templateFile = '';
  949.  
  950.     /**
  951.      * Returns path of template file with error.
  952.      *
  953.      * @return string
  954.      */
  955.     public function getTemplateFile()
  956.     {
  957.         return $this->templateFile;
  958.     }
  959.  
  960.     /**
  961.      * Sets path of template file with error.
  962.      *
  963.      * @param string $templateFile
  964.      * @return RainTpl_Exception
  965.      */
  966.     public function setTemplateFile($templateFile)
  967.     {
  968.         $this->templateFile = (string) $templateFile;
  969.         return $this;
  970.     }
  971. }
  972.  
  973. /**
  974.  * Exception thrown when template file does not exists.
  975.  */
  976. class RainTpl_NotFoundException extends RainTpl_Exception{
  977. }
  978.  
  979. /**
  980.  * Exception thrown when syntax error occurs.
  981.  */
  982. class RainTpl_SyntaxException extends RainTpl_Exception{
  983.     /**
  984.      * Line in template file where error has occured.
  985.      *
  986.      * @var int | null
  987.      */
  988.     protected $templateLine = null;
  989.  
  990.     /**
  991.      * Tag which caused an error.
  992.      *
  993.      * @var string | null
  994.      */
  995.     protected $tag = null;
  996.  
  997.     /**
  998.      * Returns line in template file where error has occured
  999.      * or null if line is not defined.
  1000.      *
  1001.      * @return int | null
  1002.      */
  1003.     public function getTemplateLine()
  1004.     {
  1005.         return $this->templateLine;
  1006.     }
  1007.  
  1008.     /**
  1009.      * Sets  line in template file where error has occured.
  1010.      *
  1011.      * @param int $templateLine
  1012.      * @return RainTpl_SyntaxException
  1013.      */
  1014.     public function setTemplateLine($templateLine)
  1015.     {
  1016.         $this->templateLine = (int) $templateLine;
  1017.         return $this;
  1018.     }
  1019.  
  1020.     /**
  1021.      * Returns tag which caused an error.
  1022.      *
  1023.      * @return string
  1024.      */
  1025.     public function getTag()
  1026.     {
  1027.         return $this->tag;
  1028.     }
  1029.  
  1030.     /**
  1031.      * Sets tag which caused an error.
  1032.      *
  1033.      * @param string $tag
  1034.      * @return RainTpl_SyntaxException
  1035.      */
  1036.     public function setTag($tag)
  1037.     {
  1038.         $this->tag = (string) $tag;
  1039.         return $this;
  1040.     }
  1041. }
  1042.  
  1043. // -- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement