/system/classes/hiengine.php
PHP | 478 lines | 289 code | 44 blank | 145 comment | 33 complexity | d304b500286d0c69509acfd80b970fde MD5 | raw file
Possible License(s): Apache-2.0
- <?php
- /**
- * @package Habari
- *
- */
- /**
- *
- * Habari HiEngine class
- *
- * The HiEngine is a subclass of the RawPHPEngine class
- * which is intended for those theme designers who want to use
- * simple {hi:*} tags four output instead of PHP
- *
- * To use this engine, specify "hiengine" in the theme.xml of a theme.
- *
- * This engine behaves exactly like RawPHPEngine after the template tags are
- * processed, so if an existing RawPHPEngine template is switched to HiEngine,
- * it should still run without issue.
- */
- class HiEngine extends RawPHPEngine {
- /**
- *
- * Constructor for HiEngine
- *
- * Sets up the stream protocol handler
- */
- public function __construct()
- {
- $streams = stream_get_wrappers();
- if ( ! in_array( 'hi', $streams ) ) {
- stream_wrapper_register( "hi", "HiEngineParser" )
- or die( _t( "Failed to register HiEngine stream protocol" ) );
- }
- }
- /**
- *
- * A function which outputs the result of a transposed
- * template to the output stream
- * @param template $ Name of template to display
- */
- public function display( $template )
- {
- extract( $this->engine_vars );
- //Utils::debug($this->engine_vars);die();
- if ( $this->template_exists( $template ) ) {
- $template_file = isset( $this->template_map[$template] ) ? $this->template_map[$template] : null;
- $template_file = Plugins::filter( 'include_template_file', $template_file, $template, __CLASS__ );
- $template_file = 'hi://' . $template_file;
- $fc = file_get_contents( $template_file );
- //echo($fc);
- eval( '?'.'>' . $fc );
- //include $template_file; // stopped working properly in PHP 5.2.8
- }
- }
- }
- /**
- * HiEngineParser - A stream filtering class for the HiEngine
- */
- class HiEngineParser
- {
- protected $file;
- protected $filename;
- protected $position;
- protected $contexts;
- protected $strings = array();
- /**
- * Open a HiEngineParser stream
- *
- * @param string $path Path of the opened resource, including the protocol specifier
- * @param string $mode Mode used to open the file
- * @param integer $options Bitmask options for opening this stream
- * @param string $opened_path The actual path opened if using relative path, by reference
- * @return boolean true on success
- */
- function stream_open( $path, $mode, $options, &$opened_path )
- {
- $this->filename = substr( $path, 5 );
- $this->file = file_get_contents( $this->filename );
- // This processed value should cache and invalidate if a checksum of the template changes! :)
- $this->file = $this->process( $this->file );
- $this->position = 0;
- return true;
- }
- /**
- * Read data from a HiEngineParser stream
- *
- * @param integer $count Number of characters to read from the current position
- * @return string Characters read from the stream
- */
- function stream_read( $count )
- {
- if ( $this->stream_eof() ) {
- return false;
- }
- $ret = substr( $this->file, $this->position, $count );
- $this->position += strlen( $ret );
- return $ret;
- }
- /**
- * Srite data to a HiEngineParser stream
- *
- * @param string $data Data to write
- * @return boolean false, since this stream type is read-only
- */
- function stream_write( $data )
- {
- // HiEngineParser streams are read-only
- return false;
- }
- /**
- * Report the position in the stream
- *
- * @return integer the position in the stream
- */
- function stream_tell()
- {
- return $this->position;
- }
- /**
- * Report whether the stream is at the end of the file
- *
- * @return boolean true if the file pointer is at or beyond the end of the file
- */
- function stream_eof()
- {
- return $this->position >= strlen( $this->file );
- }
- /**
- * Seek to a specific position within the stream
- *
- * @param integer $offset The offset from the specified position
- * @param integer $whence The position to seek from
- * @return boolean true if seek was successful
- */
- function stream_seek( $offset, $whence )
- {
- switch ( $whence ) {
- case SEEK_SET:
- if ( $offset < strlen( $this->file ) && $offset >= 0 ) {
- $this->position = $offset;
- return true;
- }
- else {
- return false;
- }
- break;
- case SEEK_CUR:
- if ( $offset >= 0 ) {
- $this->position += $offset;
- return true;
- }
- else {
- return false;
- }
- break;
- case SEEK_END:
- if ( strlen( $this->file ) + $offset >= 0 ) {
- $this->position = strlen( $this->file ) + $offset;
- return true;
- }
- else {
- return false;
- }
- break;
- default:
- return false;
- }
-
- }
- /**
- * Return fstat() info as required when calling stats on the stream
- * @return array An array of stat info
- */
- function stream_stat()
- {
- return array();
- }
- /**
- * Process the template file for template tags
- *
- * @param string $template The template file contents
- * @return string The processed template
- */
- function process( $template )
- {
- $template = preg_replace_callback( '/\{hi:(".+?")((?:\s*[\w\.]+){0,2})\s*\}/smu', array( $this, 'hi_quote' ), $template );
- $template = preg_replace_callback( '%\{hi:([^\?]+?)\}(.+?)\{/hi:\1\}%ism', array( $this, 'hi_loop' ), $template );
- $template = preg_replace_callback( '%\{hi:\?\s*(.+?)\}(.+?)\{/hi:\?\}%ismu', array( $this, 'hi_if' ), $template );
- $template = preg_replace_callback( '%\{hi:([^:}]+?:.+?)\}%i', array( $this, 'hi_command' ), $template );
- $template = preg_replace_callback( '%\{hi:(.+?)\}%i', array( $this, 'hi_var' ), $template );
- return $template;
- }
-
- /**
- * Replace a single function template tag with its PHP counterpart
- *
- * @param array $matches The match array found in HiEngineParser::process()
- * @return string The PHP replacement for the function template tag
- */
- function hi_command( $matches )
- {
- $cmd = trim( $matches[1] );
- // Catch tags in the format {hi:command:parameter}
- if ( preg_match( '/^(\w+):(.+)$/u', $cmd, $cmd_matches ) ) {
- switch ( strtolower( $cmd_matches[1] ) ) {
- case 'area':
- return '<?php $theme->area(\'' . $cmd_matches[2] . '\'); ?>';
- case 'display':
- return '<?php $theme->display(\'' . $cmd_matches[2] . '\'); ?>';
- case 'option':
- case 'options':
- return '<?php Options::out(\'' . $cmd_matches[2] . '\'); ?>';
- case 'siteurl':
- return '<?php Site::out_url( \'' . $cmd_matches[2] . '\' ); ?>';
- case 'url':
- return '<?php URL::out( \'' . $cmd_matches[2] . '\' ); ?>';
- case 'session':
- switch ( $cmd_matches[2] ) {
- case 'messages':
- return '<?php if (Session::has_messages()){Session::messages_out();} ?>';
- case 'errors':
- return '<?php if (Session::has_errors()){Session::messages_out();} ?>';
- }
- // this is an internal match
- case 'context':
- return $this->hi_to_var( $cmd_matches[2] );
- case 'escape':
- return '<?php echo Utils::htmlspecialchars( ' . $this->hi_to_var( $cmd_matches[2] ) . ' ); ?>';
- }
- }
-
- return $matches[0];
- }
- /**
- * Replace a single template tag with its PHP counterpart
- *
- * @param array $matches The match array found in HiEngineParser::process()
- * @return string The PHP replacement for the template tag
- */
- function hi_var( $matches )
- {
- $cmd = trim( $matches[1] );
- $params = array();
- $returnval = false;
- 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 ) ) {
- foreach ( $foundparams as $p ) {
- $params[$p['name']] = trim( $p['value'], $p['quot'] );
- }
- }
- // Straight variable or property output, ala {hi:variable_name} or {hi:post.title}
- if ( preg_match( '%(^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*)%i', $cmd, $cmdmatch ) ) {
- $cmdmatch = str_replace( '.', '->', $cmdmatch[1] );
- if ( count( $this->contexts ) ) {
- // Build a conditional that checks for the most specific, then the least
- // eg.- $a->b->c->d->x, then $a->b->c->x, down to just $x
- $ctx = $this->contexts;
- $prefixes = array();
- foreach ( $ctx as $void ) {
- $prefixes[] = implode( '->', $this->contexts );
- array_pop( $ctx );
- }
- $output = '';
- foreach ( $prefixes as $prefix ) {
- $output .= '(is_object($' . $prefix . ') && !'.'is_null($' . $prefix . '->' . $cmdmatch . ')) ? $' . $prefix . '->' . $cmdmatch . ' : ';
- }
- $output .= '$' . $cmdmatch;
- $returnval = $output;
- }
- else {
- $returnval = '$'. $cmdmatch;
- }
- }
- if ( $returnval !== false ) {
- $returnval = $this->apply_parameters( $returnval, $params );
- return '<?php echo '. $returnval . '; ?>';
- }
- // Use tags in the format {hi:@foo} as theme functions, ala $theme->foo();
- if ( $cmd[0] == '@' ) {
- return '<?php $theme->' . substr( $cmd, 1 ) . '(); ?>';
- }
- // Didn't match anything we support so far
- return $matches[0];
- }
-
- /**
- * Take the found paramters on a variable tag and apply them to the output
- *
- * @param array $returnval The expression to be output
- * @param array $params An associative array of parameters
- * @return string The PHP expression with the paramters applied
- */
- function apply_parameters( $returnval, $params )
- {
- foreach ( $params as $k => $v ) {
- if ( $k[0] == '@' ) {
- $returnval = '$theme->' . substr( $cmd, 1 ) . '(' . $returnval . ", '" . $v . "')";
- }
- switch ( $k ) {
- case 'dateformat':
- $returnval = "call_user_func(array(" . $returnval . ", 'format'), '" . addslashes( $v ) . "')";
- break;
- }
- }
- return $returnval;
- }
- /**
- * Replace a loop tag section with its PHP counterpart, and add the context to the stack
- *
- * @param array $matches The match array found in HiEngineParser::process()
- * @return string The PHP replacement for the template tag
- */
- function hi_loop( $matches )
- {
- $hivar = $matches[1];
- $phpvar = $this->hi_to_var( $hivar );
- $iterator = strpos( $hivar, '.' ) ? substr( $hivar, strrpos( $hivar, '.' ) + 1 ) : $hivar;
- $output = '<?php foreach(' . $phpvar . ' as $' . $iterator . '_index => $' . $iterator . '_1): ?>';
- $this->contexts[] = "{$iterator}_1";
- $output .= $this->process( $matches[2] );
- $output .= '<?php endforeach; ?>';
- array_pop( $this->contexts );
- return $output;
- }
- /**
- * Replace variables in the hiengine syntax with PHP varaibles
- * @param array $matches The match array found in hi_if ()
- * @returns string A PHP variable string to use as the replacement
- */
- function var_replace( $matches )
- {
- $var = $matches[1];
- if ( is_callable( $var ) ) {
- return $var;
- }
- if ( preg_match( '/true|false|null|isset|empty/i', $var ) ) {
- return $var;
- }
- $var = $this->hi_to_var( $var );
- return $var;
- }
- function hi_to_var( $hisyntax )
- {
- $var = str_replace( '.', '->', $hisyntax );
- if ( count( $this->contexts ) ) {
- // Build a conditional that checks for the most specific, then the least
- // eg.- $a->b->c->d->x, then $a->b->c->x, down to just $x
- $ctx = $this->contexts;
- $prefixes = array();
- foreach ( $ctx as $void ) {
- $prefixes[] = implode( '->', $this->contexts );
- array_pop( $ctx );
- }
- $output = '';
- foreach ( $prefixes as $prefix ) {
- $output .= '(!is_null($' . $prefix . '->' . $var . ') ? $' . $prefix . '->' . $var . ' : ';
- }
- $output .= '$' . $var . ')';
- return $output;
- }
- else {
- return '$'. $var;
- }
- }
- /**
- * Creates a table of static strings in hiengine expressions to be replaced in later
- * @param array $matches The match found in hi_if ()
- * @returns string An uncommon string index for the stored static string.
- */
- function string_stack( $matches )
- {
- $key = chr( 0 ) . count( $this->strings ) . chr( 1 );
- $this->strings[$key] = $matches[0];
- return $key;
- }
- /**
- * Replace an if tag section with its PHP counterpart
- *
- * @param array $matches The match array found in HiEngineParser::process()
- * @return string The PHP replacement for the template tag
- */
- function hi_if ( $matches )
- {
- list( $void, $eval, $context ) = $matches;
- $eval = preg_replace_callback( '/([\'"]).*?(?<!\\\\)\1/i', array( $this, 'string_stack' ), $eval );
- $eval = preg_replace_callback( '/\b((?<!::)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*(?!::))\b/i', array( $this, 'var_replace' ), $eval );
- $eval = preg_replace( '/(?<!=)=(?!=)/i', '==', $eval );
- $eval = str_replace( array_keys( $this->strings ), $this->strings, $eval );
- $context = preg_replace( '/\{hi:\?else\?\}/i', '<?php else: ?>', $context );
- $output = '<?php if (' . $eval . '): ?>';
- $output .= $this->process( $context );
- $output .= '<?php endif; ?>';
- return $output;
- }
- /**
- * Prepare strings for translation
- * @param array $matches Matches in HiEngineParser::process()
- * @param string The PHP replacement for the template tag
- */
- function hi_quote( $matches )
- {
- $args = preg_split( '/\s+/u', trim( $matches[2] ) );
- preg_match_all( '/"(.+?)(?<!\\\\)"/', $matches[1], $quotes );
- $count = 0;
- $all_vars = array();
- foreach ( $quotes[1] as $index => $quote ) {
- preg_match_all( '/{hi:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*)}/', $quote, $vars, PREG_SET_ORDER );
- foreach ( $vars as $var ) {
- $count++;
- $quote = str_replace( $var[0], '%'.$count.'\$s', $quote );
- $all_vars[] = '{hi:context:' . $var[1] . '}'; //$this->hi_to_var($var[1]);
- }
- $quotes[1][$index] = $quote;
- }
- if ( count( $quotes[1] ) > 1 ) {
- $output = '<?php printf(_n("'.$quotes[1][0].'", "'.$quotes[1][1].'", {hi:context:'.$args[0].'})';
- // Add vars
- if ( count( $all_vars ) > 0 ) {
- $output .= ', ' . implode( ', ', $all_vars );
- }
- array_shift( $args );
- }
- else {
- $output = '<?php _e("'.$quotes[1][0].'"';
- // Add vars
- if ( count( $all_vars ) > 0 ) {
- $output .= ', array(' . implode( ', ', $all_vars ) . ')';
- }
- }
- // Add the domain, if any
- if ( isset( $args[0] ) ) {
- $output .= ', "'.$args[0].'"';
- }
- // Close the tag
- $output .= '); ?>';
- return $output;
- }
- }
- ?>