-
-
Save og-shawn-crigger/3533027 to your computer and use it in GitHub Desktop.
Parser for Bonfire
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* The Lex Autoloader (this is case-sensative) | |
*/ | |
class Lex_Autoloader | |
{ | |
protected static $load_path = './'; | |
/** | |
* Registers the Autoloader | |
* | |
* @return void | |
*/ | |
public static function register() | |
{ | |
self::$load_path = dirname(dirname(__FILE__)).'/'; | |
ini_set('unserialize_callback_func', 'spl_autoload_call'); | |
spl_autoload_register('Lex_Autoloader::load'); | |
} | |
/** | |
* Autoloads the Lex classes, if it is not a Lex class it simply | |
* returns. | |
* | |
* @param string $class class name | |
* @return bool | |
*/ | |
static public function load($class) | |
{ | |
if (strpos($class, 'Lex') !== 0) | |
{ | |
return false; | |
} | |
$file = self::$load_path.str_replace(array('_', "\0"), array('/', ''), $class).'.php'; | |
$file = str_replace('/', DIRECTORY_SEPARATOR, $file); | |
if (is_file($file)) | |
{ | |
require $file; | |
return true; | |
} | |
return false; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php defined('BASEPATH') OR exit('No direct script access allowed'); | |
/** | |
* CodeIgniter Dwoo Parser Class | |
* | |
* @package Bonfire\Core\Libraries | |
* @category Parser | |
* @author Avinash Kundaliya | |
* @link http://cibonfire.com | |
*/ | |
class MY_Parser extends CI_Parser | |
{ | |
/** | |
* An instance of the CI super object. | |
* | |
* @access private | |
* | |
* @var object | |
*/ | |
private $ci; | |
// ------------------------------------------------------------------------ | |
/** | |
* Load dependacies and sets CI major object. | |
* | |
* @return void | |
*/ | |
function __construct() | |
{ | |
//include the lex parser class | |
if ( ! class_exists('Lex_Autoloader')) | |
{ | |
include APPPATH.'/libraries/Lex/Autoloader.php'; | |
} | |
$this->ci =& get_instance(); | |
} | |
// ------------------------------------------------------------------------ | |
/** | |
* Parses Template or String and does the Mojo! | |
* | |
* @param string $template View File or String to Parse | |
* @param array $data Array of Data to be Parsed | |
* @param boolean $return | |
* @param boolean $load_view | |
* @return mixed | |
*/ | |
public function parse($template = '', $data = array(), $return = FALSE, $load_view = TRUE) | |
{ | |
// Ready Set Go! | |
$this->ci->benchmark->mark('parse_start'); | |
// Convert from object to array | |
is_array($data) or $data = (array) $data; | |
$data = array_merge($data, $this->ci->load->_ci_cached_vars); | |
//if load_view is false, we parse the string | |
$parseString = $template; | |
//else load the view to parse | |
if($load_view) | |
{ | |
$parseString = $this->ci->load->view($template, $data, TRUE); | |
} | |
Lex_Autoloader::register(); | |
$parser = new Lex_Parser(); | |
$parser->scope_glue(':'); | |
$parsed = $parser->parse($parseString, $data, array($this, 'parser_callback')); | |
// Time's Up! | |
$this->ci->benchmark->mark('parse_end'); | |
if ( ! $return) | |
{ | |
$this->ci->output->append_output($parsed); | |
return; | |
} | |
return $parsed; | |
} | |
// ------------------------------------------------------------------------ | |
/** | |
* Parser Callback | |
* | |
* @param string $module | |
* @param string $attribute | |
* @param string $content | |
* | |
* @return mixed | |
*/ | |
public function parser_callback($module, $attribute, $content) | |
{ | |
$return_view = NULL; | |
$parsed_return = ''; | |
$output = self::get_view($module,$attribute); | |
$return_view = $output; | |
//loop it up, if its array no use in the template, gotta work it here. | |
if(is_array($output)) | |
{ | |
// Need to make sure we have a array and no objects inside the array too. | |
$parser = new Lex_Parser(); | |
$parser->scope_glue(':'); | |
foreach($output as $result) | |
{ | |
$parsed_return .= $parser->parse($content, $result, array($this, 'parser_callback')); | |
} | |
unset($parser); | |
$return_view = $parsed_return; | |
} | |
return $return_view; | |
} | |
// ------------------------------------------------------------------------ | |
/** | |
* Runs module or library callback methods. | |
* | |
* @access private | |
* | |
* @param string $module Module Class Name | |
* @param array $attribute Attributes to run Method with | |
* @param string $method Method to call. | |
* | |
* @return mixed | |
*/ | |
private function get_view($module = '', $attribute = array(), $method = 'index') | |
{ | |
$return_view = false; | |
// Get the required module | |
$module = str_replace(':','/',$module); | |
if(($pos = strrpos($module, '/')) != FALSE) | |
{ | |
$method = substr($module, $pos + 1); | |
$module = substr($module, 0, $pos); | |
} | |
if($class = $this->ci->load->module($module)) | |
{ | |
//if the method is callable | |
if (method_exists($class, $method)) | |
{ | |
ob_start(); | |
$output = call_user_func_array(array($class, $method), $attribute); | |
$buffer = ob_get_clean(); | |
$output = ($output !== NULL) ? $output : $buffer; | |
$return_view = $output; | |
} | |
} | |
//maybe it is a library | |
else if(!$return_view && strpos($module,'/') === FALSE) | |
{ | |
if(class_exists($module)) | |
{ | |
ob_start(); | |
$output = call_user_func_array(array($module, $method), $attribute); | |
$buffer = ob_get_clean(); | |
$output = ($output !== NULL) ? $output : $buffer; | |
$return_view = $output; | |
} | |
} | |
return $return_view; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Part of the Lex Template Parser. | |
* | |
* @author Dan Horrigan | |
* @license MIT License | |
* @copyright 2011 Dan Horrigan | |
*/ | |
class LexParsingException extends Exception { } | |
class Lex_Parser | |
{ | |
protected $allow_php = false; | |
protected $regex_setup = false; | |
protected $scope_glue = '.'; | |
protected $tag_regex = ''; | |
protected $cumulative_noparse = false; | |
protected $in_condition = false; | |
protected $variable_regex = ''; | |
protected $variable_loop_regex = ''; | |
protected $variable_tag_regex = ''; | |
protected $callback_tag_regex = ''; | |
protected $callback_loop_tag_regex = ''; | |
protected $noparse_regex = ''; | |
protected $conditional_regex = ''; | |
protected $conditional_else_regex = ''; | |
protected $conditional_end_regex = ''; | |
protected $conditional_data = array(); | |
protected static $extractions = array( | |
'noparse' => array(), | |
); | |
protected static $data = null; | |
protected static $callback_data = array(); | |
/** | |
* The main Lex parser method. Essentially acts as dispatcher to | |
* all of the helper parser methods. | |
* | |
* @param string $text Text to parse | |
* @param array|object $data Array or object to use | |
* @param mixed $callback Callback to use for Callback Tags | |
* @return string | |
*/ | |
public function parse($text, $data = array(), $callback = false, $allow_php = false) | |
{ | |
$this->setup_regex(); | |
$this->allow_php = $allow_php; | |
// Is this the first time parse() is called? | |
if (Lex_Parser::$data === null) | |
{ | |
// Let's store the local data array for later use. | |
Lex_Parser::$data = $data; | |
} | |
else | |
{ | |
// Let's merge the current data array with the local scope variables | |
// So you can call local variables from within blocks. | |
$data = array_merge(Lex_Parser::$data, $data); | |
// Since this is not the first time parse() is called, it's most definately a callback, | |
// let's store the current callback data with the the local data | |
// so we can use it straight after a callback is called. | |
Lex_Parser::$callback_data = $data; | |
} | |
// The parse_conditionals method executes any PHP in the text, so clean it up. | |
if ( ! $allow_php) | |
{ | |
$text = str_replace(array('<?', '?>'), array('<?', '?>'), $text); | |
} | |
$text = $this->parse_comments($text); | |
$text = $this->extract_noparse($text); | |
$text = $this->extract_looped_tags($text, $data, $callback); | |
// Order is important here. We parse conditionals first as to avoid | |
// unnecessary code from being parsed and executed. | |
$text = $this->parse_conditionals($text, $data, $callback); | |
$text = $this->inject_extractions($text, 'looped_tags'); | |
$text = $this->parse_variables($text, $data, $callback); | |
$text = $this->inject_extractions($text, 'callback_blocks'); | |
if ($callback) | |
{ | |
$text = $this->parse_callback_tags($text, $data, $callback); | |
} | |
// To ensure that {{ noparse }} is never parsed even during consecutive parse calls | |
// set $cumulative_noparse to true and use Lex_Parser::inject_noparse($text); immediately | |
// before the final output is sent to the browser | |
if ( ! $this->cumulative_noparse) | |
{ | |
$text = $this->inject_extractions($text); | |
} | |
return $text; | |
} | |
/** | |
* Removes all of the comments from the text. | |
* | |
* @param string $text Text to remove comments from | |
* @return string | |
*/ | |
public function parse_comments($text) | |
{ | |
$this->setup_regex(); | |
return preg_replace('/\{\{#.*?#\}\}/s', '', $text); | |
} | |
/** | |
* Recursivly parses all of the variables in the given text and | |
* returns the parsed text. | |
* | |
* @param string $text Text to parse | |
* @param array|object $data Array or object to use | |
* @return string | |
*/ | |
public function parse_variables($text, $data, $callback = null) | |
{ | |
$this->setup_regex(); | |
/** | |
* $data_matches[][0][0] is the raw data loop tag | |
* $data_matches[][0][1] is the offset of raw data loop tag | |
* $data_matches[][1][0] is the data variable (dot notated) | |
* $data_matches[][1][1] is the offset of data variable | |
* $data_matches[][2][0] is the content to be looped over | |
* $data_matches[][2][1] is the offset of content to be looped over | |
*/ | |
if (preg_match_all($this->variable_loop_regex, $text, $data_matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE)) | |
{ | |
foreach ($data_matches as $index => $match) | |
{ | |
if ($loop_data = $this->get_variable($match[1][0], $data)) | |
{ | |
$looped_text = ''; | |
foreach ($loop_data as $item_data) | |
{ | |
$str = $this->parse_conditionals($match[2][0], $item_data, $callback); | |
$str = $this->parse_variables($str, $item_data, $callback); | |
if ($callback !== null) | |
{ | |
$str = $this->parse_callback_tags($str, $item_data, $callback); | |
} | |
$looped_text .= $str; | |
} | |
$text = preg_replace('/'.preg_quote($match[0][0], '/').'/m', addcslashes($looped_text, '\\$'), $text, 1); | |
} | |
else // It's a callback block. | |
{ | |
// Let's extract it so it doesn't conflict | |
// with the local scope variables in the next step. | |
$text = $this->create_extraction('callback_blocks', $match[0][0], $match[0][0], $text); | |
} | |
} | |
} | |
/** | |
* $data_matches[0] is the raw data tag | |
* $data_matches[1] is the data variable (dot notated) | |
*/ | |
if (preg_match_all($this->variable_tag_regex, $text, $data_matches)) | |
{ | |
foreach ($data_matches[1] as $index => $var) | |
{ | |
if (($val = $this->get_variable($var, $data, '__lex_no_value__')) !== '__lex_no_value__') | |
{ | |
$text = str_replace($data_matches[0][$index], $val, $text); | |
} | |
} | |
} | |
return $text; | |
} | |
/** | |
* Parses all Callback tags, and sends them through the given $callback. | |
* | |
* @param string $text Text to parse | |
* @param mixed $callback Callback to apply to each tag | |
* @param bool $in_conditional Whether we are in a conditional tag | |
* @return string | |
*/ | |
public function parse_callback_tags($text, $data, $callback) | |
{ | |
$this->setup_regex(); | |
$in_condition = $this->in_condition; | |
if ($in_condition) | |
{ | |
$regex = '/\{\s*('.$this->variable_regex.')(\s+.*?)?\s*\}/ms'; | |
} | |
else | |
{ | |
$regex = '/\{\{\s*('.$this->variable_regex.')(\s+.*?)?\s*\}\}/ms'; | |
} | |
/** | |
* $match[0][0] is the raw tag | |
* $match[0][1] is the offset of raw tag | |
* $match[1][0] is the callback name | |
* $match[1][1] is the offset of callback name | |
* $match[2][0] is the parameters | |
* $match[2][1] is the offset of parameters | |
*/ | |
while (preg_match($regex, $text, $match, PREG_OFFSET_CAPTURE)) | |
{ | |
$parameters = array(); | |
$tag = $match[0][0]; | |
$start = $match[0][1]; | |
$name = $match[1][0]; | |
if (isset($match[2])) | |
{ | |
$cb_data = $data; | |
if ( !empty(Lex_Parser::$callback_data)) | |
{ | |
$cb_data = array_merge(Lex_Parser::$callback_data, (array) $data); | |
} | |
$raw_params = $this->inject_extractions($match[2][0], '__cond_str'); | |
$parameters = $this->parse_parameters($raw_params, $cb_data, $callback); | |
} | |
$content = ''; | |
$temp_text = substr($text, $start + strlen($tag)); | |
if (preg_match('/\{\{\s*\/'.preg_quote($name, '/').'\s*\}\}/m', $temp_text, $match, PREG_OFFSET_CAPTURE)) | |
{ | |
$content = substr($temp_text, 0, $match[0][1]); | |
$tag .= $content.$match[0][0]; | |
// Is there a nested block under this one existing with the same name? | |
$nested_regex = '/\{\{\s*('.preg_quote($name, '/').')(\s.*?)\}\}(.*?)\{\{\s*\/\1\s*\}\}/ms'; | |
if (preg_match($nested_regex, $content.$match[0][0], $nested_matches)) | |
{ | |
$nested_content = preg_replace('/\{\{\s*\/'.preg_quote($name, '/').'\s*\}\}/m', '', $nested_matches[0]); | |
$content = $this->create_extraction('nested_looped_tags', $nested_content, $nested_content, $content); | |
} | |
} | |
$replacement = call_user_func_array($callback, array($name, $parameters, $content)); | |
$replacement = $this->parse_recursives($replacement, $content, $callback); | |
if ($in_condition) | |
{ | |
$replacement = $this->value_to_literal($replacement); | |
} | |
$text = preg_replace('/'.preg_quote($tag, '/').'/m', addcslashes($replacement, '\\$'), $text, 1); | |
$text = $this->inject_extractions($text, 'nested_looped_tags'); | |
} | |
return $text; | |
} | |
/** | |
* Parses all conditionals, then executes the conditionals. | |
* | |
* @param string $text Text to parse | |
* @param mixed $data Data to use when executing conditionals | |
* @param mixed $callback The callback to be used for tags | |
* @return string | |
*/ | |
public function parse_conditionals($text, $data, $callback) | |
{ | |
$this->setup_regex(); | |
preg_match_all($this->conditional_regex, $text, $matches, PREG_SET_ORDER); | |
$this->conditional_data = $data; | |
/** | |
* $matches[][0] = Full Match | |
* $matches[][1] = Either 'if', 'unless', 'elseif', 'unlessif' | |
* $matches[][2] = Condition | |
*/ | |
foreach ($matches as $match) | |
{ | |
$this->in_condition = true; | |
$condition = $match[2]; | |
// Extract all literal string in the conditional to make it easier | |
if (preg_match_all('/(["\']).*?(?<!\\\\)\1/', $condition, $str_matches)) | |
{ | |
foreach ($str_matches[0] as $m) | |
{ | |
$condition = $this->create_extraction('__cond_str', $m, $m, $condition); | |
} | |
} | |
$condition = preg_replace_callback('/\b('.$this->variable_regex.')\b/', array($this, 'process_condition_var'), $condition); | |
if ($callback) | |
{ | |
$condition = preg_replace('/\b(?!\{\s*)('.$this->callback_name_regex.')(?!\s+.*?\s*\})\b/', '{$1}', $condition); | |
$condition = $this->parse_callback_tags($condition, $data, $callback); | |
} | |
// Re-inject any strings we extracted | |
$condition = $this->inject_extractions($condition, '__cond_str'); | |
$conditional = '<?php '.$match[1].' ('.$condition.'): ?>'; | |
$text = preg_replace('/'.preg_quote($match[0], '/').'/m', addcslashes($conditional, '\\$'), $text, 1); | |
} | |
$text = preg_replace($this->conditional_else_regex, '<?php else: ?>', $text); | |
$text = preg_replace($this->conditional_end_regex, '<?php endif; ?>', $text); | |
$text = $this->parse_php($text); | |
$this->in_condition = false; | |
return $text; | |
} | |
/** | |
* Goes recursively through a callback tag with a passed child array. | |
* | |
* @param string $text - The replaced text after a callback. | |
* @param string $orig_text - The original text, before a callback is called. | |
* @param mixed $callback | |
* @return string $text | |
*/ | |
public function parse_recursives($text, $orig_text, $callback) | |
{ | |
// Is there a {{ *recursive [array_key]* }} tag here, let's loop through it. | |
if (preg_match($this->recursive_regex, $text, $match)) | |
{ | |
$array_key = $match[1]; | |
$tag = $match[0]; | |
$next_tag = null; | |
$children = Lex_Parser::$callback_data[$array_key]; | |
$child_count = count($children); | |
$count = 1; | |
// Is the array not multi-dimensional? Let's make it multi-dimensional. | |
if ($child_count == count($children, COUNT_RECURSIVE)) | |
{ | |
$children = array($children); | |
$child_count = 1; | |
} | |
foreach ($children as $child) | |
{ | |
$has_children = true; | |
// If this is a object let's convert it to an array. | |
is_array($child) OR $child = (array) $child; | |
// Does this child not contain any children? | |
// Let's set it as empty then to avoid any errors. | |
if ( ! array_key_exists($array_key, $child)) | |
{ | |
$child[$array_key] = array(); | |
$has_children = false; | |
} | |
$replacement = $this->parse($orig_text, $child, $callback, $this->allow_php); | |
// If this is the first loop we'll use $tag as reference, if not | |
// we'll use the previous tag ($next_tag) | |
$current_tag = ($next_tag !== null) ? $next_tag : $tag; | |
// If this is the last loop set the next tag to be empty | |
// otherwise hash it. | |
$next_tag = ($count == $child_count) ? '' : md5($tag.$replacement); | |
$text = str_replace($current_tag, $replacement.$next_tag, $text); | |
if ($has_children) | |
{ | |
$text = $this->parse_recursives($text, $orig_text, $callback); | |
} | |
$count++; | |
} | |
} | |
return $text; | |
} | |
/** | |
* Gets or sets the Scope Glue | |
* | |
* @param string|null $glue The Scope Glue | |
* @return string | |
*/ | |
public function scope_glue($glue = null) | |
{ | |
if ($glue !== null) | |
{ | |
$this->regex_setup = false; | |
$this->scope_glue = $glue; | |
} | |
return $glue; | |
} | |
/** | |
* Sets the noparse style. Immediate or cumulative. | |
* | |
* @param bool $mode | |
* @return void | |
*/ | |
public function cumulative_noparse($mode) | |
{ | |
$this->cumulative_noparse = $mode; | |
} | |
/** | |
* Injects noparse extractions. | |
* | |
* This is so that multiple parses can store noparse | |
* extractions and all noparse can then be injected right | |
* before data is displayed. | |
* | |
* @param string $text Text to inject into | |
* @return string | |
*/ | |
public function inject_noparse($text) | |
{ | |
if (isset(Lex_Parser::$extractions['noparse'])) | |
{ | |
foreach (Lex_Parser::$extractions['noparse'] AS $hash => $replacement) | |
{ | |
if (strpos($text, "noparse_{$hash}") !== FALSE) | |
{ | |
$text = str_replace("noparse_{$hash}", $replacement, $text); | |
} | |
} | |
} | |
return $text; | |
} | |
/** | |
* This is used as a callback for the conditional parser. It takes a variable | |
* and returns the value of it, properly formatted. | |
* | |
* @param array $match A match from preg_replace_callback | |
* @return string | |
*/ | |
protected function process_condition_var($match) | |
{ | |
$var = is_array($match) ? $match[0] : $match; | |
if (in_array(strtolower($var), array('true', 'false', 'null', 'or', 'and')) or | |
strpos($var, '__cond_str') === 0 or | |
is_numeric($var)) | |
{ | |
return $var; | |
} | |
$value = $this->get_variable($var, $this->conditional_data, '__process_condition_var__'); | |
if ($value === '__process_condition_var__') | |
{ | |
return $this->in_condition ? $var : 'null'; | |
} | |
return $this->value_to_literal($value); | |
} | |
/** | |
* This is used as a callback for the conditional parser. It takes a variable | |
* and returns the value of it, properly formatted. | |
* | |
* @param array $match A match from preg_replace_callback | |
* @return string | |
*/ | |
protected function process_param_var($match) | |
{ | |
return $match[1].$this->process_condition_var($match[2]); | |
} | |
/** | |
* Takes a value and returns the literal value for it for use in a tag. | |
* | |
* @param string $value Value to convert | |
* @return string | |
*/ | |
protected function value_to_literal($value) | |
{ | |
if ($value === null) | |
{ | |
return "null"; | |
} | |
elseif ($value === true) | |
{ | |
return "true"; | |
} | |
elseif ($value === false) | |
{ | |
return "false"; | |
} | |
elseif (is_numeric($value)) | |
{ | |
return '"'.$value.'"'; | |
} | |
elseif (is_string($value)) | |
{ | |
return '"'.addslashes($value).'"'; | |
} | |
elseif (is_object($value) and is_callable(array($value, '__toString'))) | |
{ | |
return '"'.addslashes((string) $value).'"'; | |
} | |
elseif (is_array($value)) | |
{ | |
return !empty($value) ? "true" : "false"; | |
} | |
else | |
{ | |
return $value; | |
} | |
} | |
/** | |
* Sets up all the global regex to use the correct Scope Glue. | |
* | |
* @return void | |
*/ | |
protected function setup_regex() | |
{ | |
if ($this->regex_setup) | |
{ | |
return; | |
} | |
$glue = preg_quote($this->scope_glue, '/'); | |
$this->variable_regex = $glue === '\\.' ? '[a-zA-Z0-9_'.$glue.']+' : '[a-zA-Z0-9_\.'.$glue.']+'; | |
$this->callback_name_regex = $this->variable_regex.$glue.$this->variable_regex; | |
$this->variable_loop_regex = '/\{\{\s*('.$this->variable_regex.')\s*\}\}(.*?)\{\{\s*\/\1\s*\}\}/ms'; | |
$this->variable_tag_regex = '/\{\{\s*('.$this->variable_regex.')\s*\}\}/m'; | |
$this->callback_block_regex = '/\{\{\s*('.$this->variable_regex.')(\s.*?)\}\}(.*?)\{\{\s*\/\1\s*\}\}/ms'; | |
$this->recursive_regex = '/\{\{\s*\*recursive\s*('.$this->variable_regex.')\*\s*\}\}/ms'; | |
$this->noparse_regex = '/\{\{\s*noparse\s*\}\}(.*?)\{\{\s*\/noparse\s*\}\}/ms'; | |
$this->conditional_regex = '/\{\{\s*(if|elseif)\s*((?:\()?(.*?)(?:\))?)\s*\}\}/ms'; | |
$this->conditional_else_regex = '/\{\{\s*else\s*\}\}/ms'; | |
$this->conditional_end_regex = '/\{\{\s*(\/if|endif)\s*\}\}/ms'; | |
$this->regex_setup = true; | |
} | |
/** | |
* Extracts the noparse text so that it is not parsed. | |
* | |
* @param string $text The text to extract from | |
* @return string | |
*/ | |
protected function extract_noparse($text) | |
{ | |
/** | |
* $matches[][0] is the raw noparse match | |
* $matches[][1] is the noparse contents | |
*/ | |
if (preg_match_all($this->noparse_regex, $text, $matches, PREG_SET_ORDER)) | |
{ | |
foreach ($matches as $match) | |
{ | |
$text = $this->create_extraction('noparse', $match[0], $match[1], $text); | |
} | |
} | |
return $text; | |
} | |
/** | |
* Extracts the looped tags so that we can parse conditionals then re-inject. | |
* | |
* @param string $text The text to extract from | |
* @return string | |
*/ | |
protected function extract_looped_tags($text, $data = array(), $callback = null) | |
{ | |
/** | |
* $matches[][0] is the raw match | |
*/ | |
if (preg_match_all($this->callback_block_regex, $text, $matches, PREG_SET_ORDER)) | |
{ | |
foreach ($matches as $match) | |
{ | |
// Does this callback block contain parameters? | |
if ($this->parse_parameters($match[2], $data, $callback)) | |
{ | |
// Let's extract it so it doesn't conflict with local variables when | |
// parse_variables() is called. | |
$text = $this->create_extraction('callback_blocks', $match[0], $match[0], $text); | |
} | |
else | |
{ | |
$text = $this->create_extraction('looped_tags', $match[0], $match[0], $text); | |
} | |
} | |
} | |
return $text; | |
} | |
/** | |
* Extracts text out of the given text and replaces it with a hash which | |
* can be used to inject the extractions replacement later. | |
* | |
* @param string $type Type of extraction | |
* @param string $extraction The text to extract | |
* @param string $replacement Text that will replace the extraction when re-injected | |
* @param string $text Text to extract out of | |
* @return string | |
*/ | |
protected function create_extraction($type, $extraction, $replacement, $text) | |
{ | |
$hash = md5($replacement); | |
Lex_Parser::$extractions[$type][$hash] = $replacement; | |
return str_replace($extraction, "{$type}_{$hash}", $text); | |
} | |
/** | |
* Injects all of the extractions. | |
* | |
* @param string $text Text to inject into | |
* @return string | |
*/ | |
protected function inject_extractions($text, $type = null) | |
{ | |
if ($type === null) | |
{ | |
foreach (Lex_Parser::$extractions as $type => $extractions) | |
{ | |
foreach ($extractions as $hash => $replacement) | |
{ | |
if (strpos($text, "{$type}_{$hash}") !== false) | |
{ | |
$text = str_replace("{$type}_{$hash}", $replacement, $text); | |
unset(Lex_Parser::$extractions[$type][$hash]); | |
} | |
} | |
} | |
} | |
else | |
{ | |
if ( ! isset(Lex_Parser::$extractions[$type])) | |
{ | |
return $text; | |
} | |
foreach (Lex_Parser::$extractions[$type] as $hash => $replacement) | |
{ | |
if (strpos($text, "{$type}_{$hash}") !== false) | |
{ | |
$text = str_replace("{$type}_{$hash}", $replacement, $text); | |
unset(Lex_Parser::$extractions[$type][$hash]); | |
} | |
} | |
} | |
return $text; | |
} | |
/** | |
* Takes a dot-notated key and finds the value for it in the given | |
* array or object. | |
* | |
* @param string $key Dot-notated key to find | |
* @param array|object $data Array or object to search | |
* @param mixed $default Default value to use if not found | |
* @return mixed | |
*/ | |
protected function get_variable($key, $data, $default = null) | |
{ | |
if (strpos($key, $this->scope_glue) === false) | |
{ | |
$parts = explode('.', $key); | |
} | |
else | |
{ | |
$parts = explode($this->scope_glue, $key); | |
} | |
foreach ($parts as $key_part) | |
{ | |
if (is_array($data)) | |
{ | |
if ( ! array_key_exists($key_part, $data)) | |
{ | |
return $default; | |
} | |
$data = $data[$key_part]; | |
} | |
elseif (is_object($data)) | |
{ | |
if ( ! isset($data->{$key_part})) | |
{ | |
return $default; | |
} | |
$data = $data->{$key_part}; | |
} | |
else | |
{ | |
return $default; | |
} | |
} | |
return $data; | |
} | |
/** | |
* Evaluates the PHP in the given string. | |
* | |
* @param string $text Text to evaluate | |
* @return string | |
*/ | |
protected function parse_php($text) | |
{ | |
ob_start(); | |
$result = eval('?>'.$text.'<?php '); | |
if (($result === false) and (ENVIRONMENT === PYRO_DEVELOPMENT)) | |
{ | |
echo '<br />You have a syntax error in your Lex tags. The snippet of text that contains the error has been output below:<br />'; | |
exit(str_replace(array('?>', '<?php '), '', $text)); | |
} | |
elseif ($result === false) | |
{ | |
log_message('error', str_replace(array('?>', '<?php '), '', $text)); | |
echo '<br />You have a syntax error in your Lex tags: The snippet of text that contains the error has been output to your application\'s log file.<br />'; | |
} | |
return ob_get_clean(); | |
} | |
/** | |
* Parses a parameter string into an array | |
* | |
* @param string The string of parameters | |
* @return array | |
*/ | |
protected function parse_parameters($parameters, $data, $callback) | |
{ | |
$this->conditional_data = $data; | |
$this->in_condition = true; | |
// Extract all literal string in the conditional to make it easier | |
if (preg_match_all('/(["\']).*?(?<!\\\\)\1/', $parameters, $str_matches)) | |
{ | |
foreach ($str_matches[0] as $m) | |
{ | |
$parameters = $this->create_extraction('__param_str', $m, $m, $parameters); | |
} | |
} | |
$parameters = preg_replace_callback( | |
'/(.*?\s*=\s*(?!__))('.$this->variable_regex.')/is', | |
array($this, 'process_param_var'), | |
$parameters | |
); | |
if ($callback) | |
{ | |
$parameters = preg_replace('/(.*?\s*=\s*(?!\{\s*)(?!__))('.$this->callback_name_regex.')(?!\s*\})\b/', '$1{$2}', $parameters); | |
$parameters = $this->parse_callback_tags($parameters, $data, $callback); | |
} | |
// Re-inject any strings we extracted | |
$parameters = $this->inject_extractions($parameters, '__param_str'); | |
$this->in_condition = false; | |
if (preg_match_all('/(.*?)\s*=\s*(\'|"|&#?\w+;)(.*?)(?<!\\\\)\2/s', trim($parameters), $matches)) | |
{ | |
$return = array(); | |
foreach ($matches[1] as $i => $attr) | |
{ | |
$return[trim($matches[1][$i])] = stripslashes($matches[3][$i]); | |
} | |
return $return; | |
} | |
return array(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment