• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • Examples
  • File List

system/classes/hiengine.php

00001 <?php
00002 namespace Habari;
00022 class HiEngine extends RawPHPEngine {
00029   public function __construct()
00030   {
00031     $streams = stream_get_wrappers();
00032     if ( ! in_array( 'hi', $streams ) ) {
00033       stream_wrapper_register( "hi", "\Habari\HiEngineParser" )
00034       or die( _t( "Failed to register HiEngine stream protocol" ) );
00035     }
00036   }
00037 
00044   public function display( $template )
00045   {
00046     extract( $this->engine_vars );
00047     //Utils::debug($this->engine_vars);die();
00048     if ( $this->template_exists( $template ) ) {
00049       $template_file = isset( $this->template_map[$template] ) ? $this->template_map[$template] : null;
00050       $template_file = Plugins::filter( 'include_template_file', $template_file, $template, __CLASS__ );
00051       $template_file = 'hi://' . $template_file;
00052       $fc = file_get_contents( $template_file );
00053       //echo($fc);
00054       eval( 'namespace Habari; ?'.'>' . $fc );
00055       //include $template_file;  // stopped working properly in PHP 5.2.8 
00056     }
00057   }
00058 }
00059 
00063 class HiEngineParser
00064 {
00065   protected $file;
00066   protected $filename;
00067   protected $position;
00068   protected $contexts;
00069   protected $strings = array();
00070 
00080   function stream_open( $path, $mode, $options, &$opened_path )
00081   {
00082     $this->filename = substr( $path, 5 );
00083     $this->file = file_get_contents( $this->filename );
00084 
00085     // This processed value should cache and invalidate if a checksum of the template changes!  :)
00086     $this->file = $this->process( $this->file );
00087 
00088     $this->position = 0;
00089 
00090     return true;
00091   }
00092 
00099   function stream_read( $count )
00100   {
00101     if ( $this->stream_eof() ) {
00102       return false;
00103     }
00104     $ret = substr( $this->file, $this->position, $count );
00105     $this->position += strlen( $ret );
00106     return $ret;
00107   }
00108 
00115   function stream_write( $data )
00116   {
00117     // HiEngineParser streams are read-only
00118     return false;
00119   }
00120 
00126   function stream_tell()
00127   {
00128     return $this->position;
00129   }
00130 
00136   function stream_eof()
00137   {
00138     return $this->position >= strlen( $this->file );
00139   }
00140 
00148   function stream_seek( $offset, $whence )
00149   {
00150     switch ( $whence ) {
00151       case SEEK_SET:
00152         if ( $offset < strlen( $this->file ) && $offset >= 0 ) {
00153           $this->position = $offset;
00154           return true;
00155         }
00156         else {
00157           return false;
00158         }
00159         break;
00160 
00161       case SEEK_CUR:
00162         if ( $offset >= 0 ) {
00163           $this->position += $offset;
00164           return true;
00165         }
00166         else {
00167           return false;
00168         }
00169         break;
00170 
00171       case SEEK_END:
00172         if ( strlen( $this->file ) + $offset >= 0 ) {
00173           $this->position = strlen( $this->file ) + $offset;
00174           return true;
00175         }
00176         else {
00177           return false;
00178         }
00179         break;
00180 
00181       default:
00182         return false;
00183     }
00184     
00185   }
00186 
00191   function stream_stat()
00192   {
00193     return array();
00194   }
00195 
00202   function process( $template )
00203   {
00204     $template = preg_replace_callback( '/\{hi:(".+?")((?:\s*[\w\.]+){0,2})\s*\}/smu', array( $this, 'hi_quote' ), $template );
00205     $template = preg_replace_callback( '%\{hi:([^\?]+?)\}(.+?)\{/hi:\1\}%ism', array( $this, 'hi_loop' ), $template );
00206     $template = preg_replace_callback( '%\{hi:\?\s*(.+?)\}(.+?)\{/hi:\?\}%ismu', array( $this, 'hi_if' ), $template );
00207     $template = preg_replace_callback( '%\{hi:([^:}]+?:.+?)\}%i', array( $this, 'hi_command' ), $template );
00208     $template = preg_replace_callback( '%\{hi:(.+?)\}%i', array( $this, 'hi_var' ), $template );
00209     return $template;
00210   }
00211   
00218   function hi_command( $matches )
00219   {
00220     $cmd = trim( $matches[1] );
00221 
00222     // Catch tags in the format {hi:command:parameter}
00223     if ( preg_match( '/^(\w+):(.+)$/u', $cmd, $cmd_matches ) ) {
00224       switch ( strtolower( $cmd_matches[1] ) ) {
00225         case 'area':
00226           return '<?php echo $theme->area(\'' . $cmd_matches[2] . '\'); ?>';
00227         case 'display':
00228           return '<?php $theme->display(\'' . $cmd_matches[2] . '\'); ?>';
00229         case 'option':
00230         case 'options':
00231           return '<?php Options::out(\'' . $cmd_matches[2] . '\'); ?>';
00232         case 'siteurl':
00233           return '<?php Site::out_url( \'' . $cmd_matches[2] . '\' ); ?>';
00234         case 'url':
00235           return '<?php URL::out( \'' . $cmd_matches[2] . '\' ); ?>';
00236         case 'session':
00237           switch ( $cmd_matches[2] ) {
00238             case 'messages':
00239               return '<?php if (Session::has_messages()){Session::messages_out();} ?>';
00240             case 'errors':
00241               return '<?php if (Session::has_errors()){Session::messages_out();} ?>';
00242           }
00243         // this is an internal match
00244         case 'context':
00245           return $this->hi_to_var( $cmd_matches[2] );
00246         case 'escape':
00247           return '<?php echo Utils::htmlspecialchars( ' . $this->hi_to_var( $cmd_matches[2] ) . ' ); ?>';
00248       }
00249     }
00250     
00251     return $matches[0];
00252   }
00253 
00260   function hi_var( $matches )
00261   {
00262     $cmd = trim( $matches[1] );
00263     $params = array();
00264     $returnval = false;
00265 
00266     if ( preg_match_all( '/(?<=\s)(?P<name>[@a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*)\s*=\s*(?P<value>(?P<quot>["\']).+?\3|[^"\'\s]+)/i', $cmd, $foundparams, PREG_SET_ORDER ) ) {
00267       foreach ( $foundparams as $p ) {
00268         $params[$p['name']] = trim( $p['value'], $p['quot'] );
00269       }
00270     }
00271 
00272     // Straight variable or property output, ala {hi:variable_name} or {hi:post.title}
00273     if ( preg_match( '%(^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*)%i', $cmd, $cmdmatch ) ) {
00274       $cmdmatch = str_replace( '.', '->', $cmdmatch[1] );
00275       if ( count( $this->contexts ) ) {
00276         // Build a conditional that checks for the most specific, then the least
00277         // eg.- $a->b->c->d->x, then $a->b->c->x, down to just $x
00278         $ctx = $this->contexts;
00279         $prefixes = array();
00280         foreach ( $ctx as $void ) {
00281           $prefixes[] = implode( '->', $this->contexts );
00282           array_pop( $ctx );
00283         }
00284         $output = '';
00285         foreach ( $prefixes as $prefix ) {
00286           $output .= '(is_object($' . $prefix . ') && !'.'is_null($' . $prefix . '->' . $cmdmatch . ')) ? $' . $prefix . '->' . $cmdmatch . ' : ';
00287         }
00288         $output .= '$' . $cmdmatch;
00289         $returnval = $output;
00290       }
00291       else {
00292         $returnval = '$'. $cmdmatch;
00293       }
00294     }
00295     if ( $returnval !== false ) {
00296       $returnval = $this->apply_parameters( $returnval, $params );
00297       return '<?php echo '. $returnval . '; ?>';
00298     }
00299 
00300     // Use tags in the format {hi:@foo} as theme functions, ala $theme->foo();
00301     if ( $cmd[0] == '@' ) {
00302       return '<?php $theme->' . substr( $cmd, 1 ) . '(); ?>';
00303     }
00304 
00305     // Didn't match anything we support so far
00306     return $matches[0];
00307   }
00308   
00316   function apply_parameters( $returnval, $params )
00317   {
00318     foreach ( $params as $k => $v ) {
00319       if ( $k[0] == '@' ) {
00320         $returnval = '$theme->' . substr( $k, 1 ) . '(' . $returnval . ", '" . $v . "')";
00321       }
00322       switch ( $k ) {
00323         case 'dateformat':
00324           $returnval = "call_user_func(array(" . $returnval . ", 'format'), '" . addslashes( $v ) . "')";
00325           break; 
00326       }
00327     }
00328     return $returnval;
00329   }
00330 
00337   function hi_loop( $matches )
00338   {
00339     $hivar = $matches[1];
00340     $phpvar = $this->hi_to_var( $hivar );
00341     $iterator = strpos( $hivar, '.' ) ? substr( $hivar, strrpos( $hivar, '.' ) + 1 ) : $hivar;
00342     $output = '<?php foreach(' . $phpvar . ' as $' . $iterator . '_index => $' . $iterator . '_1): ?>';
00343     $this->contexts[] = "{$iterator}_1";
00344     $output .= $this->process( $matches[2] );
00345     $output .= '<?php endforeach; ?>';
00346     array_pop( $this->contexts );
00347     return $output;
00348   }
00349 
00355   function var_replace( $matches )
00356   {
00357     $var = $matches[1];
00358 
00359     if ( is_callable( $var ) ) {
00360       return $var;
00361     }
00362     if ( preg_match( '/true|false|null|isset|empty/i', $var ) ) {
00363       return $var;
00364     }
00365 
00366     $var = $this->hi_to_var( $var );
00367 
00368     return $var;
00369   }
00370 
00371   function hi_to_var( $hisyntax )
00372   {
00373     $var = str_replace( '.', '->', $hisyntax );
00374     if ( count( $this->contexts ) ) {
00375       // Build a conditional that checks for the most specific, then the least
00376       // eg.- $a->b->c->d->x, then $a->b->c->x, down to just $x
00377       $ctx = $this->contexts;
00378       $prefixes = array();
00379       foreach ( $ctx as $void ) {
00380         $prefixes[] = implode( '->', $this->contexts );
00381         array_pop( $ctx );
00382       }
00383       $output = '';
00384       foreach ( $prefixes as $prefix ) {
00385         $output .= '(!is_null($' . $prefix . '->' . $var . ') ? $' . $prefix . '->' . $var . ' : ';
00386       }
00387       $output .= '$' . $var . ')';
00388       return $output;
00389     }
00390     else {
00391       return '$'. $var;
00392     }
00393   }
00394 
00400   function string_stack( $matches )
00401   {
00402     $key = chr( 0 ) . count( $this->strings ) . chr( 1 );
00403     $this->strings[$key] = $matches[0];
00404     return $key;
00405   }
00406 
00413   function hi_if ( $matches )
00414   {
00415     list( $void, $eval, $context ) = $matches;
00416 
00417     $eval = preg_replace_callback( '/([\'"]).*?(?<!\\\\)\1/i', array( $this, 'string_stack' ), $eval );
00418     $eval = preg_replace_callback( '/\b((?<!::)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*(?!::))\b/i', array( $this, 'var_replace' ), $eval );
00419     $eval = preg_replace( '/(?<!=)=(?!=)/i', '==', $eval );
00420     $eval = str_replace( array_keys( $this->strings ), $this->strings, $eval );
00421 
00422     $context = preg_replace( '/\{hi:\?else\?\}/i', '<?php else: ?>', $context );
00423 
00424     $output = '<?php if (' . $eval . '): ?>';
00425     $output .= $this->process( $context );
00426     $output .= '<?php endif; ?>';
00427     return $output;
00428   }
00429 
00435   function hi_quote( $matches )
00436   {
00437     $args = preg_split( '/\s+/u', trim( $matches[2] ) );
00438 
00439     preg_match_all( '/"(.+?)(?<!\\\\)"/', $matches[1], $quotes );
00440     $count = 0;
00441     $all_vars = array();
00442     foreach ( $quotes[1] as $index => $quote ) {
00443       preg_match_all( '/{hi:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*)}/', $quote, $vars, PREG_SET_ORDER );
00444       foreach ( $vars as $var ) {
00445         $count++;
00446         $quote = str_replace( $var[0], '%'.$count.'\$s', $quote );
00447         $all_vars[] = '{hi:context:' . $var[1] . '}';  //$this->hi_to_var($var[1]);
00448       }
00449       $quotes[1][$index] = $quote;
00450     }
00451     if ( count( $quotes[1] ) > 1 ) {
00452       $output = '<?php printf(_n("'.$quotes[1][0].'", "'.$quotes[1][1].'", {hi:context:'.$args[0].'})';
00453       // Add vars
00454       if ( count( $all_vars ) > 0 ) {
00455         $output .= ', ' . implode( ', ', $all_vars );
00456       }
00457       array_shift( $args );
00458     }
00459     else {
00460       $output = '<?php _e("'.$quotes[1][0].'"';
00461       // Add vars
00462       if ( count( $all_vars ) > 0 ) {
00463         $output .= ', array(' . implode( ', ', $all_vars ) . ')';
00464       }
00465     }
00466 
00467     // Add the domain, if any
00468     if ( isset( $args[0] ) ) {
00469       $output .= ', "'.$args[0].'"';
00470     }
00471 
00472     // Close the tag
00473     $output .= '); ?>';
00474 
00475     return $output;
00476   }
00477 }
00478 
00479 ?>

Generated on Sun Aug 4 2013 12:51:43 for Habari by  doxygen 1.7.1