Skip to content

Instantly share code, notes, and snippets.

@og-shawn-crigger
Created September 28, 2012 23:56
Show Gist options
  • Save og-shawn-crigger/3802666 to your computer and use it in GitHub Desktop.
Save og-shawn-crigger/3802666 to your computer and use it in GitHub Desktop.
Lex Parser for Bonfire
<?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;
}
}
<?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;
}
}
<?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('&lt;?', '?&gt;'), $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();
}
}
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* Bonfire
*
* An open source project to allow developers get a jumpstart their development of CodeIgniter applications
*
* @package Bonfire
* @author Bonfire Dev Team
* @copyright Copyright (c) 2011 - 2012, Bonfire Dev Team
* @license http://guides.cibonfire.com/license.html
* @link http://cibonfire.com
* @since Version 1.0
* @filesource
*/
// ------------------------------------------------------------------------
/**
* Template
*
* The Template class makes the creation of consistently themed web pages across your
* entire site simple and as automatic as possible.
*
* It supports parent/child themes, controller-named automatic overrides, and more.
*
* @package Bonfire
* @subpackage Libraries
* @category Libraries
* @author Bonfire Dev Team
* @version 3.0
* @link http://cibonfire.com/docs/guides/views.html
*
*/
class Template
{
/**
* Set the debug mode on the template to output messages
*
* @access private
* @static
*
* @var bool
*/
private static $debug = false;
/**
* Stores the name of the active theme (folder) with a trailing slash.
*
* @access protected
* @static
*
* @var string
*/
protected static $active_theme = '';
/**
* Stores the default theme from the config file for a slight performance increase.
*
* @access protected
* @static
*
* @var string
*/
protected static $default_theme = '';
/**
* The view to load. Normally not set unless you need to bypass the automagic.
*
* @access protected
* @static
*
* @var string
*/
protected static $current_view;
/**
* The layout to render the views into.
*
* @access public
* @static
*
* @var string
*/
public static $layout;
/**
* If TRUE, CodeIgniter's Template Parser will be used to
* parse the view. If FALSE, the view is displayed with
* no parsing. Used by the yield() and block()
*
* @access public
* @static
*
* @var bool
*/
public static $parse_views = FALSE;
/**
* The data to be passed into the views. The keys are the names of the variables
* and the values are the values.
*
* @access protected
* @static
*
* @var array
*/
protected static $data = array();
/**
* An array of blocks. The key is the name to reference it by, and the value is the file.
* The class will loop through these, parse them, and push them into the layout.
*
* @access public
* @static
*
* @var array
*/
public static $blocks = array();
/**
* Holds a simple array to store the status Message
* that gets displayed using the message() function.
*
* @access protected
* @static
*
* @var string
*/
protected static $message;
/**
* An array of paths to look for themes.
*
* @access protected
* @static
*
* @var array
*/
protected static $theme_paths = array();
/**
* The full server path to the site root.
*
* @access public
* @static
*
* @var string
*/
public static $site_path;
/**
* Stores CI's default view path.
*
* @access protected
* @static
*
* @var string
*/
protected static $orig_view_path;
/**
* An instance of the CI super object.
*
* @access private
* @static
*
* @var object
*/
private static $ci;
//--------------------------------------------------------------------
/**
* This constructor is here purely for CI's benefit, as this is a static class.
*
* @return void
*/
public function __construct()
{
self::$ci =& get_instance();
self::init();
}//end __construct()
//--------------------------------------------------------------------
/**
* Grabs an instance of the CI superobject, loads the Ocular config
* file, and sets our default layout.
*
* @access public
* @static
*
* @return void
*/
public static function init()
{
// If the application config file hasn't been loaded, do it now
if (!self::$ci->config->item('template.theme_paths'))
{
self::$ci->config->load('application');
}
// Store our settings
self::$site_path = self::$ci->config->item('template.site_path');
self::$theme_paths = self::$ci->config->item('template.theme_paths');
self::$layout = self::$ci->config->item('template.default_layout');
self::$default_theme = self::$ci->config->item('template.default_theme');
self::$parse_views = self::$ci->config->item('template.parse_views');
// Store our orig view path, so we can reset it
//self::$orig_view_path = self::$ci->load->_ci_view_path;
log_message('debug', 'Template library loaded');
}//end init()
//--------------------------------------------------------------------
/**
* Renders out the specified layout, which starts the process
* of rendering the page content. Also determines the correct
* view to use based on the current controller/method.
*
* @access public
* @static
*
* @global object $OUT Core CodeIgniter Output object
* @param string $layout The name of the a layout to use. This overrides any current or default layouts set.
*
* @return void
*/
public static function render($layout=NULL)
{
$output = '';
$controller = self::$ci->router->class;
// We need to know which layout to render
$layout = empty($layout) ? self::$layout : $layout;
// Is it in an AJAX call? If so, override the layout
if (self::$ci->input->is_ajax_request())
{
$layout = self::$ci->config->item('template.ajax_layout');
self::$ci->output->set_header("Cache-Control: no-store, no-cache, must-revalidate");
self::$ci->output->set_header("Cache-Control: post-check=0, pre-check=0");
self::$ci->output->set_header("Pragma: no-cache");
self::$ci->output->set_header('Content-Type: text/html');
$controller = NULL;
}
// Grab our current view name, based on controller/method
// which routes to views/controller/method.
if (empty(self::$current_view))
{
self::$current_view = self::$ci->router->class . '/' . self::$ci->router->method;
}
//
// Time to render the layout
//
self::load_view($layout, self::$data, $controller, TRUE, $output);
if (empty($output)) { show_error('Unable to find theme layout: '. $layout); }
Events::trigger('after_layout_render', $output);
global $OUT;
$OUT->set_output($output);
// Reset the original view path
//self::$ci->load->_ci_view_path = self::$orig_view_path;
}//end render()
//--------------------------------------------------------------------
/**
* Renders the current page into the layout.
*
* Uses a view based on the controller/function being run. (See __constructor).
*
* @access public
* @static
*
* @return string A string containing the output of the render process.
*/
public static function yield()
{
$output = '';
if (self::$debug) { echo 'Current View = '. self::$current_view; }
self::load_view(self::$current_view, NULL, self::$ci->router->class .'/'. self::$ci->router->method, FALSE, $output);
Events::trigger('after_page_render', $output);
return $output;
}//end yield()
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// !BLOCKS
//--------------------------------------------------------------------
/**
* Stores the block named $name in the blocks array for later rendering.
* The $current_view variable is the name of an existing view. If it is empty,
* your script should still function as normal.
*
* @access public
* @static
*
* @param string $block_name The name of the block. Must match the name in the block() method.
* @param string $view_name The name of the view file to render.
*
* @return void
*/
public static function set_block($block_name='', $view_name='')
{
if (!empty($block_name))
{
self::$blocks[$block_name] = $view_name;
}
}//end set_block()
//--------------------------------------------------------------------
/**
* Renders a "block" to the view.
*
* A block is a partial view contained in a view file in the
* application/views folder. It can be used for sidebars,
* headers, footers, or any other recurring element within
* a site. It is recommended to set a default when calling
* this function within a layout. The default will be rendered
* if no methods override the view (using the set_block() method).
*
* @access public
* @static
*
* @param string $block_name The name of the block to render.
* @param string $default_view The view to render if no other view has been set with the set_block() method.
* @param array $data An array of data to pass to the view.
* @param bool $themed Whether we should look in the themes or standard view locations.
*
* @return void
*/
public static function block($block_name='', $default_view='', $data=array(), $themed=FALSE)
{
if (empty($block_name))
{
logit('[Template] No block name provided.');
return;
}
if (empty($block_name) && empty($default_view))
{
logit('[Template] No default block provided for `' . $block_name . '`');
return;
}
// If a block has been set previously use that
if (isset(self::$blocks[$block_name]))
{
$block_name = self::$blocks[$block_name];
}
// Otherwise, use the default view.
else
{
$block_name = $default_view;
}
if (self::$debug) { echo "Looking for block: <b>{$block_name}</b>."; }
self::load_view($block_name, $data, FALSE, $themed, $output);
$block_data = array('block'=>$block_name, 'output'=>$output);
Events::trigger('after_block_render', $block_data );
echo $output;
}//end block()
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// !THEME PATHS
//--------------------------------------------------------------------
/**
* Theme paths allow you to have multiple locations for themes to be
* stored. This might be used for separating themes for different sub-
* applications, or a core theme and user-submitted themes.
*
* @access public
* @static
*
* @param string $path A new path where themes can be found.
*
* @return bool
*/
public static function add_theme_path($path=NULL)
{
if (empty($path) || !is_string($path))
{
return FALSE;
}
// Make sure the path has a '/' at the end.
if (substr($path, -1) != '/')
{
$path .= '/';
}
// If the path already exists, we're done here.
if (isset(self::$theme_paths[$path]))
{
return TRUE;
}
// Make sure the folder actually exists
if (is_dir(FCPATH . $path))
{
array_push(self::$theme_paths, $path);
return FALSE;
}
else
{
logit("[Template] Cannot add theme folder: $path does not exist");
return FALSE;
}
}//end add_theme_path()
//--------------------------------------------------------------------
/**
* Remove the theme path
*
* @access public
* @static
*
* @param string $path The path to remove from the theme paths.
*
* @return void
*/
public static function remove_theme_path($path=NULL)
{
if (empty($path) || !is_string($path))
{
return;
}
if (isset(self::$theme_paths[$path]))
{
unset(self::$theme_paths[$path]);
}
}//end remove_theme_path()
//--------------------------------------------------------------------
/**
* Stores the name of the active theme to use. This theme should be
* relative to one of the 'template.theme_paths' folders.
*
* @access public
* @static
*
* @param string $theme The name of the active theme.
* @param string $default_theme (Optional) The name of the desired default theme.
*
* @return void
*/
public static function set_theme($theme=NULL, $default_theme=NULL)
{
if (empty($theme) || !is_string($theme))
{
return;
}
// Make sure a trailing slash is there
if (substr($theme, -1) !== '/')
{
$theme .= '/';
}
self::$active_theme = $theme;
// Default theme?
if (!empty($default_theme) && is_string($default_theme))
{
self::set_default_theme($default_theme);
}
}//end set_theme()
//--------------------------------------------------------------------
/**
* Stores the name of the default theme to use. This theme should be
* relative to one of the template.theme_paths folders.
*
* @access public
* @static
*
* @param string $theme The name of the desired default theme to use.
*
* @return void
*/
public static function set_default_theme($theme=NULL)
{
if (empty($theme) || !is_string($theme))
{
return;
}
// Make sure a trailing slash is there
if (substr($theme, -1) !== '/')
{
$theme .= '/';
}
self::$default_theme = $theme;
}//end set_default_theme()
//--------------------------------------------------------------------
/**
* Returns the active theme.
*
* @access public
* @static
*
* @return string The name of the active theme.
*/
public static function theme()
{
return ( ! empty(self::$active_theme)) ? self::$active_theme : self::$default_theme;
}//end theme()
//--------------------------------------------------------------------
/**
* Returns the full url to a file in the currently active theme.
*
* @access public
* @static
*
* @param string $resource Path to a resource in the theme
*
* @return string The full url (including http://) to the resource.
*/
public static function theme_url($resource='')
{
$url = base_url();
// Add theme path
$url .= self::$theme_paths[0] .'/';
// Add theme
$url .= empty(self::$active_theme) ? self::$default_theme : self::$active_theme;
// Cleanup, just to be safe
$url = str_replace('//', '/', $url);
$url = str_replace(':/', '://', $url);
return $url . $resource;
}//end theme_url()
//--------------------------------------------------------------------
/**
* Set the current view to render.
*
* @access public
* @static
*
* @param string $view The name of the view file to render as content.
*
* @return void
*/
public static function set_view($view=NULL)
{
if (empty($view) || !is_string($view))
{
return;
}
self::$current_view = $view;
}//end set_view()
//--------------------------------------------------------------------
/**
* Makes it easy to save information to be rendered within the views.
* As of 3.0, can also set any of the class properties.
*
* @param string $var_name The name of the variable to set
* @param mixed $value The value to set it to.
*
* @return void
*/
public static function set($var_name='', $value='')
{
// Added by dkenzik
// 20101001
// Easier migration when $data is scaterred all over your project
//
if(is_array($var_name) && $value=='')
{
foreach($var_name as $key => $value)
{
self::$data[$key] = $value;
}
}
else
{
// Is it a class property?
if (isset(self::$$var_name))
{
self::$$var_name = $value;
}
else
{
self::$data[$var_name] = $value;
}
}//end if
}//end set()
//--------------------------------------------------------------------
/**
* Returns a variable that has been previously set, or FALSE if not exists.
* As of 3.0, will also return class properties.
*
* @access public
* @static
*
* @param string $var_name The name of the data item to return.
*
* @return mixed The value of the class property or view data.
*/
public static function get($var_name=NULL)
{
if (empty($var_name))
{
return FALSE;
}
// First, is it a class property?
if (isset(self::$$var_name))
{
return self::$$var_name;
}
else if (isset(self::$data[$var_name]))
{
return self::$data[$var_name];
}
return FALSE;
}//end get()
//--------------------------------------------------------------------
/**
* Set whether or not the views will be passed through CI's parser.
*
* @access public
*
* @param bool $parse Boolean value. Should we parse views?
*/
public function parse_views($parse = FALSE)
{
self::$parse_views = (bool) $parse;
}//end parse_views()
//--------------------------------------------------------------------
/**
* Sets a status message (for displaying small success/error messages).
* This function is used in place of the session->flashdata function,
* because you don't always want to have to refresh the page to get the
* message to show up.
*
* @access public
* @static
*
* @param string $message A string with the message to save.
* @param string $type A string to be included as the CSS class of the containing div.
*
* @return void
*/
public static function set_message($message='', $type='info')
{
if (!empty($message))
{
if (isset(self::$ci->session))
{
self::$ci->session->set_flashdata('message', $type.'::'.$message);
}
self::$message = array('type'=>$type, 'message'=>$message);
}
}//end set_message()
//---------------------------------------------------------------
/**
* Displays a status message (small success/error messages).
* If data exists in 'message' session flashdata, that will
* override any other messages. The renders the message based
* on the template provided in the config file ('OCU_message_template').
*
* @access public
* @static
*
* @param string $message A string to be the message. (Optional) If included, will override any other messages in the system.
* @param string $type The class to attached to the div. (i.e. 'information', 'attention', 'error', 'success')
*
* @return string A string with the results of inserting the message into the message template.
*/
public static function message($message='', $type='information')
{
// Does session data exist?
if (empty($message) && class_exists('CI_Session'))
{
$message = self::$ci->session->flashdata('message');
if (!empty($message))
{
// Split out our message parts
$temp_message = explode('::', $message);
$type = $temp_message[0];
$message = $temp_message[1];
unset($temp_message);
}
}//end if
// If message is empty, we need to check our own storage.
if (empty($message))
{
if (empty(self::$message['message']))
{
return '';
}
$message = self::$message['message'];
$type = self::$message['type'];
}
// Grab out message template and replace the placeholders
$template = str_replace('{type}', $type, self::$ci->config->item('template.message_template'));
$template = str_replace('{message}', $message, $template);
// Clear our session data so we don't get extra messages.
// (This was a very rare occurence, but clearing should resolve the problem.
if (class_exists('CI_Session'))
{
self::$ci->session->set_flashdata('message', '');
}
return $template;
}//end message()
//---------------------------------------------------------------
/**
* Returns a javascript solution for page redirection. This is especially
* handy when you want to redirect out of an ajax request to a standard
* http request.
*
* @access public
* @static
*
* @param string $url The url to redirect to. If not a full url, will wrap it in site_url().
*
* @return void
*/
public static function redirect($url=NULL)
{
$url = strpos($url, 'http') === FALSE ? site_url($url) : $url;
echo "<script>window.location='{$url}'</script>";
exit();
}//end redirect()
//--------------------------------------------------------------------
/**
* Loads a view based on the current themes.
*
* @access public
* @static
*
* @param string $view The view to load.
* @param array $data An array of data elements to be made available to the views
* @param string $override The name of a view to check for first (used for controller-based layouts)
* @param bool $is_themed Whether it should check in the theme folder first.
* @param object $output A pointer to the variable to store the output of the loaded view into.
*
* @return void
*/
public static function load_view($view=NULL, $data=NULL, $override='', $is_themed=TRUE, &$output)
{
if (empty($view)) return '';
// If no active theme is present, use the default theme.
$theme = empty(self::$active_theme) ? self::$default_theme : self::$active_theme;
if ($is_themed)
{
// First check for the overriden file...
$output = self::find_file($override, $data, $theme);
// If we didn't find it, try the standard view
if (empty($output))
{
$output = self::find_file($view, $data, $theme);
}
}
// Just a normal view (possibly from a module, though.)
else
{
// First check within our themes...
$output = self::find_file($view, $data, $theme);
// if $output is empty, no view was overriden, so go for the default
if (empty($output))
{
self::$ci->load->_ci_view_path = self::$orig_view_path;
if (self::$parse_views === TRUE)
{
if (!class_exists('CI_Parser'))
{
self::$ci->load->library('parser');
}
// $output = self::$ci->load->_ci_load(array('_ci_path' => $view.'.php','_ci_vars' => $data,'_ci_return' => TRUE));
$data = self::$ci->load->_ci_cached_vars;
if (count($data) > 0)
{
$temp = array();
foreach($data as $key => $value)
{
if (is_object($value) && count($value) > 0)
// if (count($value) > 0)
{
$value = (array) $value;
}
$temp[$key] = $value;
}
$data = array();
$data = $temp;
unset($temp);
}
// $output = self::$ci->load->view($view, $data, TRUE);
$output = self::$ci->parser->parse($view, $data, TRUE, TRUE);
}
else
{
$output = self::$ci->load->view($view, $data, TRUE);
}
}
self::$ci->load->_ci_view_path = self::$orig_view_path;
}//end if
// Put our ci view path back to normal
//self::$ci->load->_ci_view_path = self::$orig_view_path;
unset($theme, $orig_view_path);
}//end load_view()
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// !PRIVATE METHODS
//--------------------------------------------------------------------
/**
* Searches through the the active theme and the default theme to try to find
* a view file. If found, it returns the rendered view.
*
* @access private
*
* @param string $view The name of the view to find.
* @param array $data An array of key/value pairs to pass to the views.
*
* @return string The content of the file, if found, else empty.
*/
private function find_file($view=NULL, $data=NULL)
{
if (empty($view))
{
return FALSE;
}
$output = ''; // Stores the final output
$view_path = ''; // Used to store the location of the file.
if (!empty($data))
{
$data = (array)$data;
}
// If there are multiple theme locations, we need to search through all of them.
foreach (self::$theme_paths as $path)
{
/*
First, check the active theme
*/
if (self::$debug) { echo "[Find File] Looking for view in active theme: <b>". self::$site_path . $path .'/'. self::$active_theme . $view .'.php</b><br/>'; }
if (!empty(self::$active_theme) && is_file(self::$site_path . $path .'/'. self::$active_theme . $view .'.php'))
{
if (self::$debug) { echo 'Found <b>'. $view .'</b> in Active Theme.<br/>'; }
$view_path = self::$site_path . $path .'/'. self::$active_theme;
}
/*
If not in the active theme, then try the default theme
*/
if (self::$debug) { echo "[Find File] Looking for view in default theme: <b>". self::$site_path . $path .'/'. self::$default_theme . $view .'.php</b><br/>'; }
if (empty($view_path) && is_file(self::$site_path . $path .'/'. self::$default_theme . $view .'.php'))
{
if (self::$debug) { echo 'Found <b>'. $view .'</b> in Default Theme.<br/>'; }
$view_path = self::$site_path . $path .'/'. self::$default_theme;
}
}
// If the view was found, it's path is stored in the $view_path var. So parse or render it
// based on user settings.
if (!empty($view_path))
{
$view_path = str_replace('//', '/', $view_path);
// Set CI's view path to point to the right location.
//self::$ci->load->_ci_view_path = $view_path;
if (self::$debug) { echo '[Find File] Rendering file at: '. $view_path . $view .'.php<br/><br/>'; }
// Grab the output of the view.
if (self::$parse_views === TRUE)
{
$data = array_merge((array)$data,self::$ci->load->_ci_cached_vars);
$output = self::$ci->load->_ci_load(array('_ci_path' => $view_path . $view .'.php', '_ci_vars' => $data, '_ci_return' => TRUE));
//This caused Parsing to die if parsing entire template file.
//$output = self::$ci->parser->parse($view_path.$view, $data, TRUE, TRUE);
} else
{
$output = self::$ci->load->_ci_load(array('_ci_path' => $view_path . $view .'.php', '_ci_vars' => $data, '_ci_return' => TRUE));
}
// Put CI's view path back to the original
//self::$ci->load->_ci_view_path = self::$orig_view_path;
}//end if
return $output;
}//end find_file()
//--------------------------------------------------------------------
}//end class
//--------------------------------------------------------------------
/**
* A shorthand method that allows views (from the current/default themes)
* to be included in any other view.
*
* This function also allows for a very simple form of mobile templates. If being
* viewed from a mobile site, it will attempt to load a file whose name is prefixed
* with 'mobile_'. If that file is not found it will load the regular view.
*
* @access public
* @example Rendering a view named 'index', the mobile version would be 'mobile_index'.
*
* @param string $view The name of the view to render.
* @param array $data An array of data to pass to the view.
* @param bool $ignore_mobile If TRUE, will not change the view name based on mobile viewing. If FALSE, will attempt to load a file prefixed with 'mobile_'
*
* @return string
*/
function theme_view($view=NULL, $data=NULL, $ignore_mobile=FALSE)
{
if (empty($view)) return '';
$ci =& get_instance();
$output ='';
// If we're allowed, try to load the mobile version
// of the file.
if (!$ignore_mobile)
{
$ci->load->library('user_agent');
if ($ci->agent->is_mobile())
{
Template::load_view('mobile_'. $view, $data, NULL, TRUE, $output);
}
}
// If output is empty, then either no mobile file was found
// or we weren't looking for one to begin with.
if (empty($output))
{
Template::load_view($view, $data, NULL, TRUE, $output);
}
return $output;
}//end theme_view()
//--------------------------------------------------------------------
/**
* A simple helper method for checking menu items against the current
* class that is running.
*
* <code>
* <a href="<?php echo site_url(SITE_AREA . '/content'); ?>" <?php echo check_class(SITE_AREA . '/content'); ?> >
* Admin Home
* </a>
*
* </code>
* @access public
*
* @param string $item The name of the class to check against.
* @param bool $class_only If TRUE, will only return 'active'. If FALSE, will return 'class="active"'.
*
* @return string Either <b>class="active"</b> or an empty string.
*/
function check_class($item='', $class_only=FALSE)
{
$ci =& get_instance();
if (strtolower($ci->router->fetch_class()) == strtolower($item))
{
return $class_only ? 'active' : 'class="active"';
}
return '';
}//end check_class()
//--------------------------------------------------------------------
/**
* A simple helper method for checking menu items against the current
* class' method that is being executed (as far as the Router knows.)
*
* @access public
*
* @param string $item The name of the method to check against. Can be an array of names.
*
* @return string Either <b>class="active"</b> or an empty string.
*/
function check_method($item)
{
$ci =& get_instance();
$items = array();
if (!is_array($item))
{
$items[] = $item;
}
else
{
$items = $item;
}
if (in_array($ci->router->fetch_method(), $items))
{
return 'class="active"';
}
return '';
}//end check_method()
//--------------------------------------------------------------------
/**
* Will create a breadcrumb from either the uri->segments or
* from a key/value paired array passed into it.
*
* @access public
*
* @param array $my_segments (optional) Array of Key/Value to make Breadcrumbs from
* @param bool $wrap (boolean) Set to TRUE to wrap in un-ordered list
* @param bool $echo (boolean) Set to TRUE to echo the output, set to FALSE to return it.
*
* @return string A Breadcrumb of your page structure.
*/
function breadcrumb($my_segments=NULL, $wrap=FALSE, $echo=TRUE)
{
$ci =& get_instance();
$output = '';
if (!class_exists('CI_URI'))
{
$ci->load->library('uri');
}
if ( $ci->config->item('template.breadcrumb_symbol') == '' )
{
$seperator = '/';
}
else
{
$seperator = $ci->config->item('template.breadcrumb_symbol');
}
if ($wrap === TRUE)
{
$seperator = '<span class="divider">' . $seperator . '</span>' . PHP_EOL;
}
if (empty($my_segments) || !is_array($my_segments))
{
$segments = $ci->uri->segment_array();
$total = $ci->uri->total_segments();
}
else
{
$segments = $my_segments;
$total = count($my_segments);
}
$in_admin = (bool) (is_array($segments) && in_array(SITE_AREA, $segments));
if ( $in_admin == TRUE )
{
$home_link = site_url(SITE_AREA);
}
else
{
$home_link = site_url();
}
if ($wrap === TRUE)
{
$output = '<ul class="breadcrumb">' . PHP_EOL;
$output .= '<li><a href="'.$home_link.'"><i class="icon-home">&nbsp;</i></a> '.$seperator.'</li>' . PHP_EOL;
}
else
{
$output = '<a href="'.$home_link.'">home</a> '.$seperator;
}
$url = '';
$count = 0;
// URI BASED BREADCRUMB
if (empty($my_segments) || !is_array($my_segments))
{
foreach ($segments as $segment)
{
$url .= '/'. $segment;
$count += 1;
if ($count == $total)
{
if ($wrap === TRUE)
{
$output .= '<li class="active">' . ucfirst(str_replace('_', ' ', $segment)) . '</li>' . PHP_EOL;
}
else
{
$output .= ucfirst(str_replace('_', ' ', $segment)) . PHP_EOL;
}
}
else
{
if ($wrap === TRUE)
{
$output .= '<li><a href="'. $url .'">'. str_replace('_', ' ', ucfirst(mb_strtolower($segment))) .'</a>' . $seperator . '</li>' . PHP_EOL;
}
else
{
$output .= '<a href="'. $url .'">'. str_replace('_', ' ', ucfirst(mb_strtolower($segment))) .'</a>' . $seperator . PHP_EOL;
}
}
}
}
else
{
// USER-SUPPLIED BREADCRUMB
foreach ($my_segments as $title => $uri)
{
$url .= '/'. $uri;
$count += 1;
if ($count == $total)
{
if ($wrap === TRUE)
{
$output .= '<li class="active">' . str_replace('_', ' ', $title) . '</li>' . PHP_EOL;
}
else
{
$output .= str_replace('_', ' ', $title);
}
}
else
{
if ($wrap === TRUE)
{
$output .= '<li><a href="'. $url .'">'. str_replace('_', ' ', ucfirst(mb_strtolower($title))) .'</a>' . $seperator . '</li>' . PHP_EOL;
}
else
{
$output .= '<a href="'. $url .'">'. str_replace('_', ' ', ucfirst(mb_strtolower($title))) .'</a>' . $seperator . PHP_EOL;
}
}
}
}
if ($wrap === TRUE)
{
$output .= PHP_EOL . '</ul>' . PHP_EOL;
}
unset($in_admin, $seperator, $url, $wrap);
if ($echo === TRUE)
{
echo $output;
unset ($output);
}
else
{
return $output;
}
}//end breadcrumb()
//---------------------------------------------------------------
/* End of file template.php */
/* Location: ./application/libraries/template.php */
@og-shawn-crigger
Copy link
Author

Lex Parser for Bonfire Template library.

Add MY_Parser.php and template.php into bonfire/application/libraries/

Create a directory inside of libraries named Lex and place the other 2 files there.

Turn Parsing on with

Template::$parse_views = TRUE;

Enjoy

Credits goto @hardfire and the @pyrocms team a lot of this was borrowed from both.

Enjoy and Cheers 🍺

@dot-mike
Copy link

Thanks 🍺

@dot-mike
Copy link

Thanks 🍺

@pierresilva
Copy link

Hello, this is a awesome work, just one thing. when I load the view from the themes folder is not parsed.
I want my view from themes> mytheme> pages> view.php

When the view is loaded from modules> pages> views> view.php works well.

How to fix this?

thanks for you help...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment