/system/core/core.template.php
PHP | 3422 lines | 3219 code | 99 blank | 104 comment | 47 complexity | 1df5302c798edffb0e6a4a0cd94a61e7 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- <?php
- /*
- =====================================================
- ExpressionEngine - by EllisLab
- -----------------------------------------------------
- http://expressionengine.com/
- -----------------------------------------------------
- Copyright (c) 2003 - 2010 EllisLab, Inc.
- =====================================================
- THIS IS COPYRIGHTED SOFTWARE
- PLEASE READ THE LICENSE AGREEMENT
- http://expressionengine.com/docs/license.html
- =====================================================
- File: core.template.php
- -----------------------------------------------------
- Purpose: Template parsing class.
- =====================================================
- */
- if ( ! defined('EXT'))
- {
- exit('Invalid file request');
- }
- class Template {
-
- var $loop_count = 0; // Main loop counter.
- var $depth = 0; // Sub-template loop depth
- var $in_point = ''; // String position of matched opening tag
- var $template = ''; // The requested template (page)
- var $final_template = ''; // The finalized template
- var $fl_tmpl = ''; // 'Floating' copy of the template. Used as a temporary "work area".
- var $cache_hash = ''; // md5 checksum of the template name. Used as title of cache file.
- var $cache_status = ''; // Status of page cache (NO_CACHE, CURRENT, EXPIRED)
- var $cache_timestamp = '';
- var $template_type = ''; // Type of template (webpage, rss)
- var $embed_type = ''; // Type of template for embedded template
- var $template_hits = 0;
- var $php_parse_location = 'output'; // Where in the chain the PHP gets parsed
- var $template_edit_date = ''; // Template edit date
-
- var $encode_email = TRUE; // Whether to use the email encoder. This is set automatically
- var $hit_lock_override = FALSE; // Set to TRUE if you want hits tracked on sub-templates
- var $hit_lock = FALSE; // Lets us lock the hit counter if sub-templates are contained in a template
- var $parse_php = FALSE; // Whether to parse PHP or not
- var $protect_javascript = TRUE; // Protect javascript in conditionals
-
- var $templates_sofar = ''; // Templates processed so far, subtemplate tracker
- var $tag_data = array(); // Data contained in tags
- var $modules = array(); // List of installed modules
- var $module_data = array(); // Data for modules from exp_weblogs
- var $plugins = array(); // List of installed plug-ins
- var $native_modules = array(); // List of native modules with EE
-
- var $var_single = array(); // "Single" variables
- var $var_cond = array(); // "Conditional" variables
- var $var_pair = array(); // "Paired" variables
- var $global_vars = array(); // This array can be set via the path.php file
- var $embed_vars = array(); // This array can be set via the {embed} tag
- var $segment_vars = array(); // Array of segment variables
-
- var $tagparts = array(); // The parts of the tag: {exp:comment:form}
- var $tagdata = ''; // The chunk between tag pairs. This is what modules will utilize
- var $tagproper = ''; // The full opening tag
- var $no_results = ''; // The contents of the {if no_results}{/if} conditionals
- var $no_results_block = ''; // The {if no_results}{/if} chunk
- var $search_fields = array(); // Tag parameters that begin with 'search:'
-
- var $related_data = array(); // A multi-dimensional array containing any related tags
- var $related_id = ''; // Used temporarily for the related ID number
- var $related_markers = array(); // Used temporarily
-
- var $site_ids = array(); // Site IDs for the Sites Request for a Tag
- var $sites = array(); // Array of sites with site_id as key and site_name as value, used to determine site_ids for tag, above.
- var $site_prefs_cache = array(); // Array of cached site prefs, to allow fetching of another site's template files
- var $reverse_related_data = array(); // A multi-dimensional array containing any reverse related tags
-
- var $t_cache_path = 'tag_cache/'; // Location of the tag cache file
- var $p_cache_path = 'page_cache/'; // Location of the page cache file
- var $disable_caching = FALSE;
-
- var $debugging = FALSE; // Template parser debugging on?
- var $cease_processing = FALSE; // Used with no_results() method.
- var $log = array(); // Log of Template processing
- var $start_microtime = 0; // For Logging (= microtime())
-
- var $strict_urls = FALSE; // Whether to make URLs operate strictly or not. This is set via a template global pref
-
- var $realm = 'ExpressionEngine Template'; // Localize?
- var $marker = '0o93H7pQ09L8X1t49cHY01Z5j4TT91fGfr'; // Temporary marker used as a place-holder for template data
-
- /** -------------------------------------
- /** Constructor
- /** -------------------------------------*/
- function Template()
- {
- global $IN, $PREFS;
-
- $this->native_modules = array('blacklist', 'comment', 'email', 'forum',
- 'gallery', 'mailinglist', 'member', 'query',
- 'referrer', 'rss', 'search', 'stats',
- 'trackback', 'updated_sites', 'weblog',
- 'simple_commerce', 'commerce');
-
- $this->global_vars = $IN->global_vars;
-
- if ($PREFS->ini('multiple_sites_enabled') != 'y')
- {
- $this->sites[$PREFS->ini('site_id')] = $PREFS->ini('site_short_name');
- }
-
- if ($PREFS->ini('template_debugging') === 'y' && $this->start_microtime == 0)
- {
- $this->debugging = TRUE;
-
- if (phpversion() < 5)
- {
- list($usec, $sec) = explode(" ", microtime());
- $this->start_microtime = ((float)$usec + (float)$sec);
- }
- else
- {
- $this->start_microtime = microtime(TRUE);
- }
- }
- }
- /* END */
-
-
-
- /** -------------------------------------
- /** Run the template engine
- /** -------------------------------------*/
- function run_template_engine($template_group = '', $template = '')
- {
- global $OUT, $IN, $FNS, $PREFS;
-
- $this->log_item(" - Begin Template Processing - ");
-
- // Set the name of the cache folder for both tag and page caching
-
- if ($IN->URI != '')
- {
- $this->t_cache_path .= md5($FNS->fetch_site_index().$IN->URI).'/';
- $this->p_cache_path .= md5($FNS->fetch_site_index().$IN->URI).'/';
- }
- else
- {
- $this->t_cache_path .= md5($PREFS->ini('site_url').'index'.$IN->QSTR).'/';
- $this->p_cache_path .= md5($PREFS->ini('site_url').'index'.$IN->QSTR).'/';
- }
-
-
- // We limit the total number of cache files in order to
- // keep some sanity with large sites or ones that get
- // hit by over-ambitious crawlers.
-
- if ($this->disable_caching == FALSE)
- {
- if ($dh = @opendir(PATH_CACHE.'page_cache'))
- {
- $i = 0;
- while (false !== (readdir($dh)))
- {
- $i++;
- }
-
- $max = ( ! $PREFS->ini('max_caches') OR ! is_numeric($PREFS->ini('max_caches')) OR $PREFS->ini('max_caches') > 1000) ? 1000 : $PREFS->ini('max_caches');
-
- if ($i > $max)
- {
- $FNS->clear_caching('page');
- }
- }
- }
-
- $this->log_item("URI: ".$IN->URI);
- $this->log_item("Path.php Template: {$template_group}/{$template}");
- $this->process_template($template_group, $template, FALSE);
-
- $this->log_item(" - End Template Processing - ");
- $this->log_item("Parse Global Variables");
- if ($this->template_type == 'static')
- {
- $this->final_template = $this->restore_xml_declaration($this->final_template);
- }
- else
- {
- $this->final_template = $this->parse_globals($this->final_template);
- }
-
- $this->log_item("Template Parsing Finished");
-
- $OUT->out_type = $this->template_type;
- $OUT->build_queue($this->final_template);
- }
- /* END */
-
-
-
- /** -------------------------------------
- /** Process Template
- /** -------------------------------------*/
- function process_template($template_group = '', $template = '', $sub = FALSE, $site_id = '')
- {
- global $LOC, $PREFS, $REGX, $LANG, $IN, $FNS;
-
- // add this template to our subtemplate tracker
- $this->templates_sofar = $this->templates_sofar.'|'.$site_id.':'.$template_group.'/'.$template.'|';
- /** -------------------------------------
- /** Fetch the requested template
- /** -------------------------------------*/
- // The template can either come from the DB or a cache file
- // Do not use a reference!
-
- $this->cache_status = 'NO_CACHE';
-
- $this->log_item("Retrieving Template");
-
- $this->template = ($template_group != '' AND $template != '') ? $this->fetch_template($template_group, $template, FALSE, $site_id) : $this->parse_template_uri();
-
- $this->log_item("Template Type: ".$this->template_type);
-
- /** -------------------------------------
- /** Static Content, No Parsing
- /** -------------------------------------*/
-
- if ($this->template_type == 'static' OR $this->embed_type == 'static')
- {
- if ($sub == FALSE)
- {
- $this->final_template = $this->template;
- }
-
- return;
- }
-
- /* -------------------------------------
- /* "Smart" Static Parsing
- /*
- /* Performed on embedded webpage templates only that do not have
- /* ExpressionEngine tags or PHP in them.
- /*
- /* Hidden Configuration Variable
- /* - smart_static_parsing => Bypass parsing of templates that could be
- /* of the type 'static' but aren't? (y/n)
- /* -------------------------------------*/
- if ($PREFS->ini('smart_static_parsing') !== 'n' && $this->embed_type == 'webpage' && ! stristr($this->template, LD) && ! stristr($this->template, '<?'))
- {
- $this->log_item("Smart Static Parsing Triggered");
-
- if ($sub == FALSE)
- {
- $this->final_template = $this->template;
- }
-
- return;
- }
-
- /** -------------------------------------
- /** Replace "logged_out" variables
- /** -------------------------------------*/
- // We do this for backward compatibility
- // Note: My plan is to deprecate this, but we need to update
- // every template in an installation with the new syntax.
- // I would have done it for 1.2 but I added this late, after the
- // beta testers already got a copy, so we'll do it in a future update.
-
- $logvars = array('NOT_LOGGED_IN' => 'logged_out', 'not_logged_in' => 'logged_out', 'LOGGED_IN' => 'logged_in');
-
- foreach($logvars as $key => $val)
- {
- $this->template = str_replace(LD.'if '.$key.RD, LD.'if '.$val.RD, $this->template);
- }
-
- /** -------------------------------------
- /** Parse URI segments
- /** -------------------------------------*/
-
- // This code lets admins fetch URI segments which become
- // available as: {segment_1} {segment_2}
-
- for ($i = 1; $i < 10; $i++)
- {
- $this->template = str_replace(LD.'segment_'.$i.RD, $IN->fetch_uri_segment($i), $this->template);
- $this->segment_vars['segment_'.$i] = $IN->fetch_uri_segment($i);
- }
-
- /** -------------------------------------
- /** Parse {embed} tag variables
- /** -------------------------------------*/
-
- if ($sub === TRUE && count($this->embed_vars) > 0)
- {
- $this->log_item("Embed Variables (Keys): ".implode('|', array_keys($this->embed_vars)));
- $this->log_item("Embed Variables (Values): ".trim(implode('|', $this->embed_vars)));
-
- foreach ($this->embed_vars as $key => $val)
- {
- // add 'embed:' to the key for replacement and so these variables work in conditionals
- $this->embed_vars['embed:'.$key] = $val;
- unset($this->embed_vars[$key]);
- $this->template = str_replace(LD.'embed:'.$key.RD, $val, $this->template);
- }
- }
-
- // cleanup of leftover/undeclared embed variables
- // don't worry with undeclared embed: vars in conditionals as the conditionals processor will handle that adequately
- if (strpos($this->template, LD.'embed:') !== FALSE)
- {
- $this->template = preg_replace('/'.LD.'embed:(.+?)'.RD.'/', '', $this->template);
- }
-
- /** --------------------------------------------------
- /** Parse 'Site' variables
- /** --------------------------------------------------*/
- $this->log_item("Parsing Site Variables");
- // load site variables into the global_vars array
- foreach (array('site_id', 'site_label', 'site_short_name') as $site_var)
- {
- $this->global_vars[$site_var] = stripslashes($PREFS->ini($site_var));
- }
-
- /** -------------------------------------
- /** Parse manual variables
- /** -------------------------------------*/
- // These are variables that can be set in the path.php file
-
- if (count($this->global_vars) > 0)
- {
- $this->log_item("Global Path.php Variables (Keys): ".implode('|', array_keys($this->global_vars)));
- $this->log_item("Global Path.php Variables (Values): ".trim(implode('|', $this->global_vars)));
-
- foreach ($this->global_vars as $key => $val)
- {
- $this->template = str_replace(LD.$key.RD, $val, $this->template);
- }
- }
-
- /** -------------------------------------
- /** Parse date format string "constants"
- /** -------------------------------------*/
-
- $date_constants = array('DATE_ATOM' => '%Y-%m-%dT%H:%i:%s%Q',
- 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC',
- 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%O',
- 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O',
- 'DATE_RFC850' => '%l, %d-%M-%y %H:%m:%i UTC',
- 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O',
- 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O',
- 'DATE_RFC2822' => '%D, %d %M %Y %H:%i:%s %O',
- 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O',
- 'DATE_W3C' => '%Y-%m-%dT%H:%i:%s%Q'
- );
- foreach ($date_constants as $key => $val)
- {
- $this->template = str_replace(LD.$key.RD, $val, $this->template);
- }
-
- $this->log_item("Parse Date Format String Constants");
-
- /** --------------------------------------------------
- /** Template's Last Edit time {template_edit_date format="%Y %m %d %H:%i:%s"}
- /** --------------------------------------------------*/
- if (strpos($this->template, LD.'template_edit_date') !== FALSE && preg_match_all("/".LD."template_edit_date\s+format=([\"\'])([^\\1]*?)\\1".RD."/", $this->template, $matches))
- {
- for ($j = 0; $j < count($matches['0']); $j++)
- {
- $this->template = preg_replace("/".$matches['0'][$j]."/", $LOC->decode_date($matches['2'][$j], $this->template_edit_date), $this->template, 1);
- }
- }
- /** --------------------------------------------------
- /** Current time {current_time format="%Y %m %d %H:%i:%s"}
- /** --------------------------------------------------*/
- if (strpos($this->template, LD.'current_time') !== FALSE && preg_match_all("/".LD."current_time\s+format=([\"\'])([^\\1]*?)\\1".RD."/", $this->template, $matches))
- {
- for ($j = 0; $j < count($matches['0']); $j++)
- {
- $this->template = preg_replace("/".preg_quote($matches['0'][$j], '/')."/", $LOC->decode_date($matches['2'][$j], $LOC->now), $this->template, 1);
- }
- }
-
- $this->template = str_replace(LD.'current_time'.RD, $LOC->now, $this->template);
-
- $this->log_item("Parse Current Time Variables");
-
- /** -------------------------------------
- /** Is the main template cached?
- /** -------------------------------------*/
- // If a cache file exists for the primary template
- // there is no reason to go further.
- // However we do need to fetch any subtemplates
- if ($this->cache_status == 'CURRENT' AND $sub == FALSE)
- {
- $this->log_item("Cached Template Used");
-
- $this->template = $this->parse_nocache($this->template);
-
- /** -------------------------------------
- /** Smite Our Enemies: Advanced Conditionals
- /** -------------------------------------*/
-
- if (stristr($this->template, LD.'if'))
- {
- $this->template = $this->advanced_conditionals($this->template);
- }
-
- $this->log_item("Conditionals Parsed, Processing Sub Templates");
-
- $this->final_template = $this->template;
- $this->process_sub_templates($this->template);
- return;
- }
- // Remove whitespace from variables.
- // This helps prevent errors, particularly if PHP is used in a template
- $this->template = preg_replace("/".LD."\s*(\S+)\s*".RD."/U", LD."\\1".RD, $this->template);
- /** -------------------------------------
- /** Parse Input Stage PHP
- /** -------------------------------------*/
-
- if ($this->parse_php == TRUE AND $this->php_parse_location == 'input' AND $this->cache_status != 'CURRENT')
- {
- $this->log_item("Parsing PHP on Input");
- $this->template = $this->parse_template_php($this->template);
- }
-
- /** -------------------------------------
- /** Smite Our Enemies: Conditionals
- /** -------------------------------------*/
-
- $this->log_item("Parsing Segment, Embed, and Global Vars Conditionals");
-
- $this->template = $this->segment_conditionals($this->template);
- $this->template = $this->array_conditionals($this->template, $this->embed_vars);
- $this->template = $this->array_conditionals($this->template, $this->global_vars);
- /** -------------------------------------
- /** Set global variable assignment
- /** -------------------------------------*/
- if (preg_match_all("/".LD."assign_variable:(.+?)=([\"\'])([^\\2]*?)\\2".RD."/i", $this->template, $matches))
- {
- $this->log_item("Processing Assigned Variables: ".trim(implode('|', $matches['1'])));
-
- for ($j = 0; $j < count($matches['0']); $j++)
- {
- $this->template = str_replace($matches['0'][$j], "", $this->template);
- $this->template = str_replace(LD.$matches['1'][$j].RD, $matches['3'][$j], $this->template);
- }
- }
-
- /** -------------------------------------
- /** Process the template
- /** -------------------------------------*/
-
- // Replace forward slashes with entity to prevent preg_replace errors.
- $this->template = str_replace('/', SLASH, $this->template);
- // Fetch installed modules and plugins if needed
- if (count($this->modules) == 0)
- {
- $this->fetch_modules();
- }
-
- if (count($this->plugins) == 0)
- {
- $this->fetch_plugins();
- }
-
- // Parse the template.
-
- $this->log_item(" - Beginning Tag Processing - ");
-
- while (is_int(strpos($this->template, LD.'exp:')))
- {
- // Initialize values between loops
- $this->tag_data = array();
- $this->var_single = array();
- $this->var_cond = array();
- $this->var_pair = array();
- $this->loop_count = 0;
-
- $this->log_item("Parsing Tags in Template");
- // Run the template parser
- $this->parse_template();
-
- $this->log_item("Processing Tags");
-
- // Run the class/method handler
- $this->class_handler();
-
- if ($this->cease_processing === TRUE)
- {
- return;
- }
- }
-
- $this->log_item(" - End Tag Processing - ");
-
- // Decode forward slash entities back to ascii
-
- $this->template = str_replace(SLASH, '/', $this->template);
-
- /** -------------------------------------
- /** Parse Output Stage PHP
- /** -------------------------------------*/
-
- if ($this->parse_php == TRUE AND $this->php_parse_location == 'output' AND $this->cache_status != 'CURRENT')
- {
- $this->log_item("Parsing PHP on Output");
- $this->template = $this->parse_template_php($this->template);
- }
-
- /** -------------------------------------
- /** Write the cache file if needed
- /** -------------------------------------*/
-
- if ($this->cache_status == 'EXPIRED')
- {
- $this->template = $FNS->insert_action_ids($this->template);
- $this->write_cache_file($this->cache_hash, $this->template, 'template');
- }
-
- /** -------------------------------------
- /** Parse Our Uncacheable Forms
- /** -------------------------------------*/
-
- $this->template = $this->parse_nocache($this->template);
-
- /** -------------------------------------
- /** Smite Our Enemies: Advanced Conditionals
- /** -------------------------------------*/
-
- if (stristr($this->template, LD.'if'))
- {
- $this->log_item("Processing Advanced Conditionals");
- $this->template = $this->advanced_conditionals($this->template);
- }
-
- // <?php This fixes a BBEdit bug that makes the list of function not work right. Seems related to the PHP declarations above.
-
-
- /** -------------------------------------
- /** Build finalized template
- /** -------------------------------------*/
-
- // We only do this on the first pass.
- // The sub-template routine will insert embedded
- // templates into the master template
-
- if ($sub == FALSE)
- {
- $this->final_template = $this->template;
- $this->process_sub_templates($this->template);
- }
- }
- /* END */
-
-
-
- /** -------------------------------------
- /** Parse embedded sub-templates
- /** -------------------------------------*/
- function process_sub_templates($template)
- {
- global $REGX, $FNS, $LANG, $PREFS, $DB;
- /** -------------------------------------
- /** Match all {embed=bla/bla} tags
- /** -------------------------------------*/
-
- $matches = array();
-
- if ( ! preg_match_all("/(".LD."embed\s*=)(.*?)".RD."/s", $template, $matches))
- {
- return;
- }
- /** -------------------------------------
- /** Loop until we have parsed all sub-templates
- /** -------------------------------------*/
-
- // For each embedded tag we encounter we'll run the template parsing
- // function - AND - through the beauty of recursive functions we
- // will also call THIS function as well, allowing us to parse
- // infinitely nested sub-templates in one giant loop o' love
-
- $this->log_item(" - Processing Sub Templates (Depth: ".($this->depth+1).") - ");
-
- $i = 0;
- $this->depth++;
-
- $this->log_item("List of Embeds: ".str_replace(array('"', "'"), '', trim(implode(',', $matches['2']))));
- // re-match the full tag of each if necessary before we start processing
- // necessary evil in case template globals are used inside the embed tag,
- // doing this within the processing loop will result in leaving unparsed
- // embed tags e.g. {embed="foo/bar" var="{global_var}/{custom_field}"}
- $temp = $template;
- foreach ($matches[2] as $key => $val)
- {
- if (strpos($val, LD) !== FALSE)
- {
- $matches[0][$key] = $FNS->full_tag($matches[0][$key], $temp);
- $matches[2][$key] = substr(str_replace($matches[1][$key], '', $matches[0][$key]), 0, -1);
- $temp = str_replace($matches[0][$key], '', $temp);
- }
- }
- foreach($matches['2'] as $key => $val)
- {
- $parts = preg_split("/\s+/", $val, 2);
-
- $this->embed_vars = (isset($parts['1'])) ? $FNS->assign_parameters($parts['1']) : array();
-
- if ($this->embed_vars === FALSE)
- {
- $this->embed_vars = array();
- }
-
- $val = $REGX->trim_slashes($REGX->strip_quotes($parts['0']));
- if ( ! stristr($val, '/'))
- {
- continue;
- }
-
- $ex = explode("/", trim($val));
-
- if (count($ex) != 2)
- {
- continue;
- }
-
- /** ----------------------------------
- /** Determine Site
- /** ----------------------------------*/
-
- $site_id = $PREFS->ini('site_id');
-
- if (stristr($ex[0], ':'))
- {
- $name = substr($ex[0], 0, strpos($ex[0], ':'));
-
- if ($PREFS->ini('multiple_sites_enabled') == 'y')
- {
- if (sizeof($this->sites) == 0)
- {
- $sites_query = $DB->query("SELECT site_id, site_name FROM exp_sites");
-
- foreach($sites_query->result as $row)
- {
- $this->sites[$row['site_id']] = $row['site_name'];
- }
- }
-
- $site_id = array_search($name, $this->sites);
-
- if (empty($site_id))
- {
- $site_id = $PREFS->ini('site_id');
- }
- }
-
- $ex[0] = str_replace($name.':', '', $ex[0]);
- }
-
-
- /** ----------------------------------
- /** Loop Prevention
- /** ----------------------------------*/
- /* -------------------------------------------
- /* Hidden Configuration Variable
- /* - template_loop_prevention => 'n'
- Whether or not loop prevention is enabled - y/n
- /* -------------------------------------------*/
-
- if (substr_count($this->templates_sofar, '|'.$site_id.':'.$ex['0'].'/'.$ex['1'].'|') > 1 && $PREFS->ini('template_loop_prevention') != 'n')
- {
- $this->final_template = ($PREFS->ini('debug') >= 1) ? str_replace('%s', $ex['0'].'/'.$ex['1'], $LANG->line('template_loop')) : "";
- return;
- }
-
- /** ----------------------------------
- /** Process Subtemplate
- /** ----------------------------------*/
-
- $this->log_item("Processing Sub Template: ".$ex['0']."/".$ex['1']);
-
- $this->process_template($ex['0'], $ex['1'], TRUE, $site_id);
- $this->final_template = str_replace($matches['0'][$key], $this->template, $this->final_template);
- $this->embed_type = '';
-
- // Here we go again! Wheeeeeee.....
- $this->process_sub_templates($this->template);
-
- // pull the subtemplate tracker back a level to the parent template
- $this->templates_sofar = substr($this->templates_sofar, 0, - strlen('|'.$site_id.':'.$ex[0].'/'.$ex[1].'|'));
- }
- $this->depth--;
- if ($this->depth == 0)
- {
- $this->templates_sofar = '';
- }
-
- }
- /* END */
- /** -------------------------------------
- /** Parse the template
- /** -------------------------------------*/
- function parse_template()
- {
- global $FNS;
-
- while (TRUE)
- {
- // Make a "floating" copy of the template which we'll progressively slice into pieces with each loop
-
- $this->fl_tmpl = $this->template;
-
- // Identify the string position of the first occurence of a matched tag
-
- $this->in_point = strpos($this->fl_tmpl, LD.'exp:');
-
- // If the above variable returns false we are done looking for tags
- // This single conditional keeps the template engine from spiraling
- // out of control in an infinite loop.
-
- if (FALSE === $this->in_point)
- {
- break;
- }
- else
- {
- /** ------------------------------------------
- /** Process the tag data
- /** ------------------------------------------*/
-
- // These REGEXs parse out the various components contained in any given tag.
-
- // Grab the opening portion of the tag: {exp:some:tag param="value" param="value"}
- if ( ! preg_match("/".LD.'exp:'.".*?".RD."/s", $this->fl_tmpl, $matches))
- {
- $this->template = preg_replace("/".LD.'exp:'.".*?$/", '', $this->template);
- break;
- }
-
- $this->log_item("Tag: ".$matches['0']);
-
- // Checking for variables/tags embedded within tags
- // {exp:weblog:entries weblog="{master_weblog_name}"}
- if (stristr(substr($matches['0'], 1), LD) !== false)
- {
- $matches['0'] = $FNS->full_tag($matches['0']);
- }
-
- $raw_tag = preg_replace("/(\r\n)|(\r)|(\n)|(\t)/", ' ', $matches['0']);
-
- $tag_length = strlen($raw_tag);
-
- $data_start = $this->in_point + $tag_length;
- $tag = trim(substr($raw_tag, 1, -1));
- $args = trim((preg_match("/\s+.*/", $tag, $matches))) ? $matches['0'] : '';
- $tag = trim(str_replace($args, '', $tag));
-
- $cur_tag_close = LD.SLASH.$tag.RD;
-
- // -----------------------------------------
-
- // Assign the class name/method name and any parameters
-
- $class = $this->assign_class(substr($tag, strlen('exp') + 1));
- $args = $FNS->assign_parameters($args);
-
- // standardized mechanism for "search" type parameters get some extra lovin'
-
- $search_fields = array();
-
- if ($args !== FALSE)
- {
- foreach ($args as $key => $val)
- {
- if (strncmp($key, 'search:', 7) == 0)
- {
- $search_fields[substr($key, 7)] = str_replace(SLASH, '/', $val);
- }
- }
- }
-
- // Trim the floating template, removing the tag we just parsed.
-
- $this->fl_tmpl = substr($this->fl_tmpl, $this->in_point + $tag_length);
-
- $out_point = strpos($this->fl_tmpl, $cur_tag_close);
-
- // Do we have a tag pair?
-
- if (FALSE !== $out_point)
- {
- // Assign the data contained between the opening/closing tag pair
-
- $this->log_item("Closing Tag Found");
-
- $block = substr($this->template, $data_start, $out_point);
-
- // Fetch the "no_results" data
-
- $no_results = '';
- $no_results_block = '';
-
- if (preg_match("/".LD."if no_results".RD."(.*?)".LD.SLASH."if".RD."/s", $block, $match))
- {
- // Match the entirety of the conditional, dude. Bad Rick!
-
- if (stristr($match['1'], LD.'if'))
- {
- $match['0'] = $FNS->full_tag($match['0'], $block, LD.'if', LD.SLASH."if".RD);
- }
-
- $no_results = substr($match['0'], strlen(LD."if no_results".RD), -strlen(LD.SLASH."if".RD));
-
- $no_results_block = $match['0'];
- }
-
- // Define the entire "chunk" - from the left edge of the opening tag
- // to the right edge of closing tag.
- $out_point = $out_point + $tag_length + strlen($cur_tag_close);
-
- $chunk = substr($this->template, $this->in_point, $out_point);
- }
- else
- {
- // Single tag...
-
- $this->log_item("No Closing Tag");
-
- $block = ''; // Single tags don't contain data blocks
-
- $no_results = '';
- $no_results_block = '';
-
- // Define the entire opening tag as a "chunk"
-
- $chunk = substr($this->template, $this->in_point, $tag_length);
- }
-
- // Strip the "chunk" from the template, replacing it with a unique marker.
-
- if (stristr($raw_tag, 'random'))
- {
- $this->template = preg_replace("|".preg_quote($chunk)."|s", 'M'.$this->loop_count.$this->marker, $this->template, 1);
- }
- else
- {
- $this->template = str_replace($chunk, 'M'.$this->loop_count.$this->marker, $this->template);
- }
-
- $cfile = md5($chunk); // This becomes the name of the cache file
- // Build a multi-dimensional array containing all of the tag data we've assembled
-
- $this->tag_data[$this->loop_count]['tag'] = $raw_tag;
- $this->tag_data[$this->loop_count]['class'] = $class['0'];
- $this->tag_data[$this->loop_count]['method'] = $class['1'];
- $this->tag_data[$this->loop_count]['tagparts'] = $class;
- $this->tag_data[$this->loop_count]['params'] = $args;
- $this->tag_data[$this->loop_count]['chunk'] = $chunk; // Matched data block - including opening/closing tags
- $this->tag_data[$this->loop_count]['block'] = $block; // Matched data block - no tags
- $this->tag_data[$this->loop_count]['cache'] = $this->cache_status($cfile, $args);
- $this->tag_data[$this->loop_count]['cfile'] = $cfile;
- $this->tag_data[$this->loop_count]['no_results'] = $no_results;
- $this->tag_data[$this->loop_count]['no_results_block'] = $no_results_block;
- $this->tag_data[$this->loop_count]['search_fields'] = $search_fields;
-
- } // END IF
- // Increment counter
- $this->loop_count++;
- } // END WHILE
- }
- /* END */
- /** -------------------------------------
- /** Class/Method handler
- /** -------------------------------------*/
- function class_handler()
- {
- global $FNS, $TMPL, $DB, $PREFS;
-
- $classes = array();
-
- // Fill an array with the names of all the classes that we previously extracted from the tags
-
- for ($i = 0; $i < count($this->tag_data); $i++)
- {
- // Should we use the tag cache file?
- if ($this->tag_data[$i]['cache'] == 'CURRENT')
- {
- // If so, replace the marker in the tag with the cache data
-
- $this->log_item("Tag Cached and Cache is Current");
-
- $this->replace_marker($i, $this->get_cache_file($this->tag_data[$i]['cfile']));
- }
- else
- {
- // Is a module or plug-in being requested?
-
- if ( ! in_array($this->tag_data[$i]['class'] , $this->modules))
- {
- if ( ! in_array($this->tag_data[$i]['class'] , $this->plugins))
- {
- global $LANG, $PREFS, $OUT;
-
- $this->log_item("Invalid Tag");
- if ($PREFS->ini('debug') >= 1)
- {
- if ($this->tag_data[$i]['tagparts']['0'] == $this->tag_data[$i]['tagparts']['1'] &&
- ! isset($this->tag_data[$i]['tagparts']['2']))
- {
- unset($this->tag_data[$i]['tagparts']['1']);
- }
-
- $error = $LANG->line('error_tag_syntax');
- $error .= '<br /><br />';
- $error .= htmlspecialchars(LD);
- $error .= 'exp:'.implode(':', $this->tag_data[$i]['tagparts']);
- $error .= htmlspecialchars(RD);
- $error .= '<br /><br />';
- $error .= $LANG->line('error_fix_syntax');
-
- $OUT->fatal_error($error);
- }
- else
- return false;
- }
- else
- {
- $classes[] = 'pi.'.$this->tag_data[$i]['class'];
- $this->log_item("Plugin Tag: ".ucfirst($this->tag_data[$i]['class']).'/'.$this->tag_data[$i]['method']);
- }
- }
- else
- {
- $classes[] = $this->tag_data[$i]['class'];
- $this->log_item("Module Tag: ".ucfirst($this->tag_data[$i]['class']).'/'.$this->tag_data[$i]['method']);
- }
- }
- }
- // Remove duplicate class names and re-order the array
-
- $classes = array_values(array_unique($classes));
-
- // Dynamically require the file that contains each class
-
- $this->log_item("Including Files for Tag and Modules");
-
- for ($i = 0; $i < count($classes); $i++)
- {
- // But before we do, make sure it hasn't already been included...
-
- if ( ! class_exists($classes[$i]))
- {
- if (substr($classes[$i], 0, 3) == 'pi.')
- {
- require_once PATH_PI.$classes[$i].EXT;
- }
- else
- {
- require_once PATH_MOD.$classes[$i].'/mod.'.$classes[$i].EXT;
- }
- }
- }
-
- /** -----------------------------------
- /** Only Retrieve Data if Not Done Before and Modules Being Called
- /** -----------------------------------*/
-
- if (sizeof($this->module_data) == 0 && sizeof(array_intersect($this->modules, $classes)) > 0)
- {
- $query = $DB->query("SELECT module_version, module_name FROM exp_modules");
-
- foreach($query->result as $row)
- {
- $this->module_data[$row['module_name']] = array('version' => $row['module_version']);
- }
- }
-
- // Final data processing
- // Loop through the master array containing our extracted template data
-
- $this->log_item("Beginning Final Tag Data Processing");
-
- reset($this->tag_data);
-
- for ($i = 0; $i < count($this->tag_data); $i++)
- {
- if ($this->tag_data[$i]['cache'] != 'CURRENT')
- {
- $this->log_item("Calling Class/Method: ".ucfirst($this->tag_data[$i]['class'])."/".$this->tag_data[$i]['method']);
-
- /* ---------------------------------
- /* Plugin as Parameter
- /*
- /* - Example: weblog="{exp:some_plugin}"
- /* - A bit of a hidden feature. Has been tested but not quite
- /* ready to say it is ready for prime time as I might want to
- /* move it to earlier in processing so that if there are
- /* multiple plugins being used as parameters it is only called
- /* once instead of for every single parameter. - Paul
- /* ---------------------------------*/
-
- if (substr_count($this->tag_data[$i]['tag'], LD.'exp') > 1 && isset($this->tag_data[$i]['params']['parse']) && $this->tag_data[$i]['params']['parse'] == 'inward')
- {
- foreach($this->tag_data[$i]['params'] as $name => $param)
- {
- if (stristr($this->tag_data[$i]['params'][$name], LD.'exp'))
- {
- $this->log_item("Plugin in Parameter, Processing Plugin First");
-
- $TMPL2 = $FNS->clone_object($this);
-
- while (is_int(strpos($TMPL2->tag_data[$i]['params'][$name], LD.'exp:')))
- {
- $TMPL = new Template();
- $TMPL->start_microtime = $this->start_microtime;
- $TMPL->template = $TMPL2->tag_data[$i]['params'][$name];
- $TMPL->tag_data = array();
- $TMPL->var_single = array();
- $TMPL->var_cond = array();
- $TMPL->var_pair = array();
- $TMPL->plugins = $TMPL2->plugins;
- $TMPL->modules = $TMPL2->modules;
- $TMPL->parse_template();
- $TMPL->class_handler();
- $TMPL->loop_count = 0;
- $TMPL2->tag_data[$i]['params'][$name] = $TMPL->template;
- $TMPL2->log = array_merge($TMPL2->log, $TMPL->log);
- }
-
- foreach (get_object_vars($TMPL2) as $key => $value)
- {
- $this->$key = $value;
- }
-
- unset($TMPL2);
-
- $TMPL = $this;
- }
- }
- }
-
- /** ---------------------------------
- /** Nested Plugins...
- /** ---------------------------------*/
-
- if (in_array($this->tag_data[$i]['class'] , $this->plugins) && strpos($this->tag_data[$i]['block'], LD.'exp:') !== false)
- {
- if ( ! isset($this->tag_data[$i]['params']['parse']) OR $this->tag_data[$i]['params']['parse'] != 'inward')
- {
- $this->log_item("Nested Plugins in Tag, Parsing Outward First");
-
- $TMPL2 = $FNS->clone_object($this);
-
- while (is_int(strpos($TMPL2->tag_data[$i]['block'], LD.'exp:')))
- {
- $TMPL = new Template();
- $TMPL->start_microtime = $this->start_microtime;
- $TMPL->template = $TMPL2->tag_data[$i]['block'];
- $TMPL->tag_data = array();
- $TMPL->var_single = array();
- $TMPL->var_cond = array();
- $TMPL->var_pair = array();
- $TMPL->plugins = $TMPL2->plugins;
- $TMPL->modules = $TMPL2->modules;
- $TMPL->parse_template();
- $TMPL->class_handler();
- $TMPL->loop_count = 0;
- $TMPL2->tag_data[$i]['block'] = $TMPL->template;
- $TMPL2->log = array_merge($TMPL2->log, $TMPL->log);
- }
-
- foreach (get_object_vars($TMPL2) as $key => $value)
- {
- $this->$key = $value;
- }
-
- unset($TMPL2);
-
- $TMPL = $this;
- }
- }
-
- // Assign the data chunk, parameters
-
- // We moved the no_results_block here because of nested tags. The first
- // parsed tag has priority for that conditional.
- $this->tagdata = str_replace($this->tag_data[$i]['no_results_block'], '', $this->tag_data[$i]['block']);
- $this->tagparams = $this->tag_data[$i]['params'];
- $this->tagchunk = $this->tag_data[$i]['chunk'];
- $this->tagproper = $this->tag_data[$i]['tag'];
- $this->tagparts = $this->tag_data[$i]['tagparts'];
- $this->no_results = $this->tag_data[$i]['no_results'];
- $this->search_fields = $this->tag_data[$i]['search_fields'];
-
- /** -------------------------------------
- /** Assign Sites for Tag
- /** -------------------------------------*/
-
- $this->_fetch_site_ids();
-
- /** -------------------------------------
- /** Relationship Data Pulled Out
- /** -------------------------------------*/
-
- // If the weblog:entries tag or search:search_results is being called
- // we need to extract any relationship data that might be present.
- // Note: This needs to happen before extracting the variables
- // in the tag so it doesn't get confused as to which entry the
- // variables belong to.
-
- if (($this->tag_data[$i]['class'] == 'weblog' AND $this->tag_data[$i]['method'] == 'entries')
- OR ($this->tag_data[$i]['class'] == 'search' AND $this->tag_data[$i]['method'] == 'search_results'))
- {
- $this->tagdata = $this->assign_relationship_data($this->tagdata);
- }
- // Fetch the variables for this particular tag
-
- $vars = $FNS->assign_variables($this->tag_data[$i]['block']);
-
- if (count($this->related_markers) > 0)
- {
- foreach ($this->related_markers as $mkr)
- {
- if ( ! isset($vars['var_single'][$mkr]))
- {
- $vars['var_single'][$mkr] = $mkr;
- }
- }
- $this->related_markers = array();
- }
- $this->var_single = $vars['var_single'];
- $this->var_pair = $vars['var_pair'];
- // Redundant see above loop for related_markers - R.S.
- //if ($this->related_id != '')
- //{
- // $this->var_single[$this->related_id] = $this->related_id;
- // $this->related_id = '';
- //}
-
- // Assign Conditional Variables
-
- if ( ! in_array($this->tag_data[$i]['class'],$this->native_modules))
- {
- $this->var_cond = $FNS->assign_conditional_variables($this->tag_data[$i]['block'], SLASH, LD, RD);
- }
-
- // Assign the class name and method name
-
- $class_name = ucfirst($this->tag_data[$i]['class']);
-
- if ($class_name == 'Commerce')
- {
- // The Commerce module is special in that it has its own modules and its
- // constructor handles everything for us
- $meth_name = 'commerce';
- }
- else
- {
- $meth_name = $this->tag_data[$i]['method'];
- }
-
- // Dynamically instantiate the class.
- // If module, only if it is installed...
-
- if (in_array($this->tag_data[$i]['class'], $this->modules) && ! isset($this->module_data[$class_name]))
- {
- $this->log_item("Problem Processing Module: Module Not Installed");
- }
- else
- {
- $this->log_item(" -> Class Called: ".$class_name);
-
- $EE = new $class_name();
- }
-
- /** ----------------------------------
- /** Does method exist? Is This A Module and Is It Installed?
- /** ----------------------------------*/
-
- if ((in_array($this->tag_data[$i]['class'], $this->modules) && ! isset($this->module_data[$class_name])) OR ! method_exists($EE, $meth_name))
- {
- global $LANG, $PREFS, $OUT;
-
- $this->log_item("Tag Not Processed: Method Inexistent or Module Not Installed");
- if ($PREFS->ini('debug') >= 1)
- {
- if ($this->tag_data[$i]['tagparts']['0'] == $this->tag_data[$i]['tagparts']['1'] &&
- ! isset($this->tag_data[$i]['tagparts']['2']))
- {
- unset($this->tag_data[$i]['tagparts']['1']);
- }
-
- $error = $LANG->line('error_tag_module_processing');
- $error .= '<br /><br />';
- $error .= htmlspecialchars(LD);
- $error .= 'exp:'.implode(':', $this->tag_data[$i]['tagparts']);
- $error .= htmlspecialchars(RD);
- $error .= '<br /><br />';
- $error .= str_replace('%x', $this->tag_data[$i]['class'], str_replace('%y', $meth_name, $LANG->line('error_fix_module_processing')));
-
- $OUT->fatal_error($error);
- }
- else
- return;
- }
-
- /*
-
- OK, lets grab the data returned from the class.
-
- First, however, lets determine if the tag has one or two segments.
- If it only has one, we don't want to call the constructor again since
- it was already called during instantiation.
-
- Note: If it only has one segment, only the object constructor will be called.
- Since constructors can't return a value just by initialializing the object
- the output of the class must be assigned to a variable called $this->return_data
-
- */
-
- $this->log_item(" -> Method Called: ".$meth_name);
-
- if (strtolower($class_name) == $meth_name)
- {
- $return_data = (isset($EE->return_data)) ? $EE->return_data : '';
- }
- else
- {
- $return_data = $EE->$meth_name();
- }
-
- /** ----------------------------------
- /** 404 Page Triggered, Cease All Processing of Tags From Now On
- /** ----------------------------------*/
-
- if ($this->cease_processing === TRUE)
- {
- return;
- }
-
- $this->log_item(" -> Data Returned");
-
- // Write cache file if needed
-
- if ($this->tag_data[$i]['cache'] == 'EXPIRED')
- {
- $this->write_cache_file($this->tag_data[$i]['cfile'], $return_data);
- }
-
- // Replace the temporary markers we added earlier with the fully parsed data
-
- $this->replace_marker($i, $return_data);
-
- // Initialize data in case there are susequent loops
-
- $this->var_single = array();
- $this->var_cond = array();
- $this->var_pair = array();
-
- unset($return_data);
- unset($class_name);
- unset($meth_name);
- unset($EE);
- }
- }
- }
- /* END */
- /** -------------------------------------
- /** Assign the related data
- /** -------------------------------------*/
-
- // Weblog entries can have related entries embedded within them.
- // We'll extract the related tag data, stash it away in a…
Large files files are truncated, but you can click here to view the full file