/system/expressionengine/third_party/stash/mod.stash.php
PHP | 5113 lines | 3338 code | 619 blank | 1156 comment | 455 complexity | 659280961a436c13b17490d1a9b9a167 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
- require_once PATH_THIRD . 'stash/config.php';
- /**
- * Set and get template variables, EE snippets and persistent variables.
- *
- * @package Stash
- * @author Mark Croxton (mcroxton@hallmark-design.co.uk)
- * @copyright Copyright (c) 2014 Hallmark Design
- * @license http://creativecommons.org/licenses/by-nc-sa/3.0/
- * @link http://hallmark-design.co.uk
- */
- class Stash {
- public $EE;
- public $version = STASH_VER;
- public $site_id;
- public $path;
- public $file_sync;
- public $stash_cookie;
- public $stash_cookie_expire;
- public $default_scope;
- public $limit_bots;
- public static $context = NULL;
-
- protected $xss_clean;
- protected $replace;
- protected $type;
- protected $parse_tags = FALSE;
- protected $parse_vars = NULL;
- protected $parse_conditionals = FALSE;
- protected $parse_depth = 1;
- protected $parse_complete = FALSE;
- protected $bundle_id = 1;
- protected $process = 'inline';
- protected $priority = 1;
- protected static $bundles = array();
-
- private $_update = FALSE;
- private $_append = TRUE;
- private $_stash;
- private $_session_id;
- private $_ph = array();
- private $_list_delimiter = '|+|';
- private $_list_row_delimiter = '|&|';
- private $_list_row_glue = '|=|';
- private $_list_null = '__NULL__';
- private $_embed_nested = FALSE;
- private $_nocache_suffix = ':nocache';
- private static $_nocache = TRUE;
- private static $_nocache_prefixes = array('stash');
- private static $_is_human = TRUE;
- private static $_cache;
- /*
- * Constructor
- */
- public function __construct($calling_from_hook = FALSE)
- {
- $this->EE =& get_instance();
-
- // load dependencies - make sure the package path is available in case the class is being called statically
- $this->EE->load->add_package_path(PATH_THIRD.'stash/', TRUE);
- $this->EE->lang->loadfile('stash');
- $this->EE->load->model('stash_model');
- // default site id
- $this->site_id = $this->EE->config->item('site_id');
-
- // config defaults
- $this->path = $this->EE->config->item('stash_file_basepath') ? $this->EE->config->item('stash_file_basepath') : APPPATH . 'stash/';
- $this->file_sync = $this->_get_boolean_config_item('stash_file_sync', FALSE); // default = FALSE
- $this->stash_cookie = $this->EE->config->item('stash_cookie') ? $this->EE->config->item('stash_cookie') : 'stashid';
- $this->stash_cookie_expire = $this->EE->config->item('stash_cookie_expire') ? $this->EE->config->item('stash_cookie_expire') : 0;
- $this->stash_cookie_enabled = $this->_get_boolean_config_item('stash_cookie_enabled'); // default = TRUE
- $this->default_scope = $this->EE->config->item('stash_default_scope') ? $this->EE->config->item('stash_default_scope') : 'user';
- $this->default_refresh = $this->EE->config->item('stash_default_refresh') ? $this->EE->config->item('stash_default_refresh') : 0; // minutes
- $this->limit_bots = $this->_get_boolean_config_item('stash_limit_bots', FALSE); // default = FALSE
- // cache pruning can cache stampede mitigation defaults
- $this->prune = $this->_get_boolean_config_item('stash_prune_enabled'); // default = TRUE
- $this->prune_probability = $this->EE->config->item('stash_prune_probability') ? $this->EE->config->item('stash_prune_probability') : .4; // percent
- $this->invalidation_period = $this->EE->config->item('stash_invalidation_period') ? $this->EE->config->item('stash_invalidation_period') : 0; // seconds
- // permitted file extensions for Stash embeds
- $this->file_extensions = $this->EE->config->item('stash_file_extensions')
- ? (array) $this->EE->config->item('stash_file_extensions')
- : array('html', 'md', 'css', 'js', 'rss', 'xml');
- // Support {if var1 IN (var2) }...{/if} style conditionals in Stash templates / tagdata?
- $this->parse_if_in = $this->EE->config->item('stash_parse_if_in') ? $this->EE->config->item('stash_parse_if_in') : FALSE;
- // include query string when using the @URI context (full page caching)?
- $this->include_query_str = $this->EE->config->item('stash_query_strings') ? $this->EE->config->item('stash_query_strings') : FALSE;
-
- // initialise tag parameters
- if (FALSE === $calling_from_hook)
- {
- $this->init();
- }
- // fetch the stash session id
- if ($this->stash_cookie_enabled)
- {
- if ( ! isset($this->EE->session->cache['stash']['_session_id']) )
- {
- // do we have a stash cookie?
- if ($cookie_data = $this->_get_stash_cookie())
- {
- // YES - restore session
- $this->EE->session->cache['stash']['_session_id'] = $cookie_data['id'];
- // shall we prune expired variables?
- if ($this->prune)
- {
- // probability that pruning occurs
- $prune_chance = 100/$this->prune_probability;
- // trigger pruning every 1 chance out of $prune_chance
- if (mt_rand(0, ($prune_chance-1)) === 0)
- {
- // prune variables with expiry date older than right now
- $this->EE->stash_model->prune_keys();
- }
- }
- }
- else
- {
- if ($this->limit_bots)
- {
- // Is the user a human? Legitimate bots don't set cookies so will end up here every page load
- // Humans who accept cookies only get checked when the cookie is first set
- self::$_is_human = ($this->_is_bot() ? FALSE : TRUE);
- }
-
- // NO - let's generate a unique id
- $unique_id = $this->EE->functions->random();
-
- // add to stash array
- $this->EE->session->cache['stash']['_session_id'] = $unique_id;
-
- // create a cookie; store the creation date in the cookie itself
- $this->_set_stash_cookie($unique_id);
- }
- }
-
- // create a reference to the session id
- $this->_session_id =& $this->EE->session->cache['stash']['_session_id'];
- }
- else
- {
- $this->_session_id = '_global';
- }
- }
-
- // ---------------------------------------------------------
-
- /**
- * Initialise tag parameters
- *
- * @access public
- * @param bool $calling_from_hook Is method being called by an extension hook?
- * @return void
- */
- public function init($calling_from_hook = FALSE)
- {
- // make sure we have a Template object to work with, in case Stash is being invoked outside of a template
- if ( ! class_exists('EE_Template'))
- {
- $this->_load_EE_TMPL();
- }
-
- // initialise internal flags
- $this->parse_complete = FALSE;
- $this->_update = FALSE;
- $this->_append = TRUE;
- $this->_embed_nested = FALSE;
- $this->process = 'inline';
-
- // postpone the parsing of the called stash tag?
- if (FALSE === $calling_from_hook)
- {
- /* process stage:
- start = called prior to template parsing in the current template
- inline = process as a normal tag within the natural parse order of the template
- end = called after all tag parsing has completed
- */
- $this->process = $this->EE->TMPL->fetch_param('process', 'inline'); // start | inline | end
- $this->priority = $this->EE->TMPL->fetch_param('priority', '1'); // ensure a priority is set
- }
-
- // legacy: make 'final' the same as 'end'
- if ($this->process == "final")
- {
- $this->process = "end";
- }
-
- // tags can't be processed on start, only stash embeds
- if ($this->process == "start")
- {
- $this->process = "inline";
- }
-
- // allow the site_id to be overridden, for e.g. shared variables across mutliple sites
- $this->site_id = (integer) $this->EE->TMPL->fetch_param('site_id', $this->site_id);
- // selected bundle
- $bundle = $this->EE->TMPL->fetch_param('bundle', 'default');
- // lookup the id of an existing bundle, or map to one of the preset bundles
- if ( ! $this->bundle_id = $this->EE->stash_model->get_bundle_by_name($bundle))
- {
- // not found, fallback to the default
- $this->bundle_id = 1;
- }
-
- // xss scripting protection
- $this->xss_clean = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('xss_clean'));
-
- // if the variable is already set, do we want to replace it's value? Default = yes
- $this->replace = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('replace', 'yes'));
- // parse="yes"?
- $this->set_parse_params();
-
- // do we want to parse any tags and variables inside tagdata? Default = no
- $this->parse_tags = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('parse_tags'));
- $this->parse_vars = $this->EE->TMPL->fetch_param('parse_vars', NULL);
-
- // legacy behaviour: if parse_vars is null but parse tags is true, we should make sure vars are parsed too
- if ($this->parse_tags && $this->parse_vars == NULL)
- {
- $this->parse_vars = TRUE;
- }
- else
- {
- $this->parse_vars = (bool) preg_match('/1|on|yes|y/i', $this->parse_vars);
- }
-
- // parsing: how many passes of the template should we make? (more passes = more overhead). Default = 1
- $this->parse_depth = preg_replace('/[^0-9]/', '', $this->EE->TMPL->fetch_param('parse_depth', 1));
-
- // parsing: parse advanced conditionals. Default = no
- $this->parse_conditionals = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('parse_conditionals'));
-
- // stash type, default to 'variable'
- $this->type = strtolower( $this->EE->TMPL->fetch_param('type', 'variable') );
-
- // create a stash array in the session if we don't have one
- if ( ! array_key_exists('stash', $this->EE->session->cache) )
- {
- $this->EE->session->cache['stash'] = array();
- }
-
- // determine the memory storage location
- if ($this->type === 'variable')
- {
- // we're setting/getting a 'native' stash variable
- $this->_stash =& $this->EE->session->cache['stash'];
- }
- elseif ($this->type === 'snippet' || $this->type === 'global')
- {
- // we're setting/getting a global variable {snippet}
- $this->_stash =& $this->EE->config->_global_vars;
- }
- else
- {
- $this->EE->output->show_user_error('general', $this->EE->lang->line('unknown_stash_type') . $this->type);
- }
- }
-
- // ---------------------------------------------------------
-
- /**
- * Load the EE Template class and register the Stash module
- * Used when Stash is instantiated outside of an EE template
- *
- * @access private
- * @return void
- */
- private function _load_EE_TMPL()
- {
- // -------------------------------------
- // 'stash_load_template_class' hook
- // -------------------------------------
- if ($this->EE->extensions->active_hook('stash_load_template_class') === TRUE)
- {
- $this->EE->TMPL = $this->EE->extensions->call('stash_load_template_class');
- }
- else
- {
- require_once APPPATH.'libraries/Template.php';
- $this->EE->TMPL = new EE_Template();
- $this->EE->TMPL->modules = array('stash');
- }
- }
-
- /*
- ================================================================
- Template tags
- ================================================================
- */
-
- /**
- * Shortcut to stash:get or stash:set
- *
- * @param string $name The method name being called or context if third tagpart
- * @param array $arguments The method call arguments
- *
- * @return void
- */
- public function __call($name, $arguments)
- {
- /* Sample use
- ---------------------------------------------------------
- {exp:stash:foo}
-
- is equivalent to:
-
- {exp:stash:get name="foo"}
- ---------------------------------------------------------
- {exp:stash:foo}
- CONTENT
- {/exp:stash:foo}
-
- is equivalent to:
-
- {exp:stash:set name="foo"}
- CONTENT
- {/exp:stash:set}
- ---------------------------------------------------------
- {exp:stash:bar:foo}
-
- is equivalent to:
-
- {exp:stash:get name="bar:foo"}
- and
- {exp:stash:get context="bar" name="foo"}
- ---------------------------------------------------------
- {exp:stash:bar:foo}
- CONTENT
- {/exp:stash:bar:foo}
-
- is equivalent to:
- {exp:stash:set context="bar" name="foo"}
- CONTENT
- {/exp:stash:set}
- and
- {exp:stash:set name="bar:foo"}
- CONTENT
- {/exp:stash:set}
- --------------------------------------------------------- */
-
- switch($name)
- {
- case 'unset' :
- // make 'unset' - a reserved word - an alias of destroy()
- return call_user_func_array(array($this, 'destroy'), $arguments);
- break;
- case 'static' :
- // make 'static' - a reserved word - an alias of static_cache()
- return call_user_func_array(array($this, 'static_cache'), $arguments);
- break;
- default :
-
- // if there is an extra tagpart, then we have a context and a name
- if (isset($this->EE->TMPL->tagparts[2]))
- {
- $this->EE->TMPL->tagparams['context'] = $name;
- $this->EE->TMPL->tagparams['name'] = $this->EE->TMPL->tagparts[2];
- }
- else
- {
- $this->EE->TMPL->tagparams['name'] = $name;
- }
- return $this->EE->TMPL->tagdata ? $this->set() : $this->get();
- }
- }
-
- /**
- * Set content in the current session, optionally save to the database
- *
- * @access public
- * @param mixed $params The name of the variable to retrieve, or an array of key => value pairs
- * @param string $value The value of the variable
- * @param string $type The type of variable
- * @param string $scope The scope of the variable
- * @return void
- */
- public function set($params=array(), $value='', $type='variable', $scope='user')
- {
- /* Sample use
- ---------------------------------------------------------
- {exp:stash:set name="title" type="snippet"}A title{/exp:stash:set}
-
- OR static call within PHP enabled templates or other add-on:
- <?php stash::set('title', 'My title') ?>
- --------------------------------------------------------- */
-
- // is this method being called directly?
- if ( func_num_args() > 0)
- {
- if ( !(isset($this) && get_class($this) == __CLASS__))
- {
- return self::_api_static_call(__FUNCTION__, $params, $type, $scope, $value);
- }
- else
- {
- return $this->_api_call(__FUNCTION__, $params, $type, $scope, $value);
- }
- }
-
- // do we want to set the variable?
- $set = TRUE;
-
- // var name
- $name = $this->EE->TMPL->fetch_param('name', FALSE);
-
- // context handling
- $context = $this->EE->TMPL->fetch_param('context', NULL);
-
- if ( !! $name)
- {
- if ($context !== NULL && count( explode(':', $name) == 1 ) )
- {
- $name = $context . ':' . $name;
- $this->EE->TMPL->tagparams['context'] = NULL;
- }
- }
-
- // replace '@' placeholders with the current context
- $stash_key = $this->_parse_context($name);
-
- // scope
- $scope = strtolower($this->EE->TMPL->fetch_param('scope', $this->default_scope)); // local|user|site
-
- // do we want this tag to return it's tagdata? (default: no)
- $output = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('output'));
- // do we want to parse early global variables in variables retrieved from the cache
-
- // append or prepend passed as parameters?
- if (preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('prepend')))
- {
- $this->_update = TRUE;
- $this->_append = FALSE;
- }
- elseif (preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('append')))
- {
- $this->_update = TRUE;
- $this->_append = TRUE;
- }
-
- // do we want to save this variable in a bundle?
- $bundle = $this->EE->TMPL->fetch_param('bundle', NULL); // save in a bundle?
-
- // do we want to replace an existing variable?
- if ( !! $name && ! $this->replace && ! $this->_update)
- {
- // try to get existing value
- $existing_value = FALSE;
-
- if ( array_key_exists($stash_key, $this->_stash))
- {
- $existing_value = $this->_stash[$name];
- }
- elseif ($scope !== 'local')
- {
- // narrow the scope to user?
- $session_id = $scope === 'user' ? $this->_session_id : '_global';
-
- $existing_value = $this->EE->stash_model->get_key(
- $stash_key,
- $this->bundle_id,
- $session_id,
- $this->site_id
- );
- }
- if ( $existing_value !== FALSE)
- {
- // yes, it's already been stashed
- $this->EE->TMPL->tagdata = $this->_stash[$name] = $existing_value;
- // don't overwrite existing value
- $set = FALSE;
- }
- unset($existing_value);
- }
-
- // do we want to ignore empty tagdata values?
- if ( $not_empty = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('not_empty')) )
- {
- if ( ! $this->not_empty())
- {
- $set = FALSE;
- }
- }
-
- if ($set)
- {
- // support for deprecated no_results_prefix parameter
- $no_results_prefix = $this->EE->TMPL->fetch_param('no_results_prefix');
- // check for an unprefix parameter to avoid variable name conflicts in nested tags
- if($unprefix = $this->EE->TMPL->fetch_param('unprefix', $no_results_prefix))
- {
- $this->EE->TMPL->tagdata = $this->_un_prefix($unprefix, $this->EE->TMPL->tagdata);
- }
-
- if ( ($this->parse_tags || $this->parse_vars || $this->parse_conditionals) && ! $this->parse_complete)
- {
- $this->_parse_sub_template($this->parse_tags, $this->parse_vars, $this->parse_conditionals, $this->parse_depth);
- $this->parse_complete = TRUE; // don't run again
- }
-
- // apply any string manipulations
- $this->EE->TMPL->tagdata = $this->_clean_string($this->EE->TMPL->tagdata);
- if ( !! $name )
- {
- // get params
- $label = $this->EE->TMPL->fetch_param('label', $name);
- $save = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('save'));
- $match = $this->EE->TMPL->fetch_param('match', NULL); // regular expression to test value against
- $against = $this->EE->TMPL->fetch_param('against', $this->EE->TMPL->tagdata); // text to apply test against
- $filter = $this->EE->TMPL->fetch_param('filter', NULL); // regex pattern to search for
- $default = $this->EE->TMPL->fetch_param('default', NULL); // default value
- $delimiter = $this->EE->TMPL->fetch_param('delimiter', '|'); // implode arrays using this delimiter
- // cache refresh time
- $refresh = (int) $this->EE->TMPL->fetch_param('refresh', $this->default_refresh);
-
- // do we want to set a placeholder somewhere in this template ?
- $set_placeholder = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('set_placeholder'));
-
- // make sure we have a value to fallback to for output in current template
- if ($set_placeholder && is_null($default))
- {
- $default = '';
- }
- // set refresh
- if ($refresh > 0)
- {
- $refresh = $this->EE->localize->now + ($refresh * 60);
- }
-
- // regex match
- if ( $match !== NULL && preg_match('/^#(.*)#$/', $match))
- {
- $is_match = $this->_matches($match, $against);
-
- // did it fail to match the filter?
- if ( ! $is_match )
- {
- // if a default has been specified fallback to it
- if (! is_null($default))
- {
- $this->EE->TMPL->tagdata = $default;
- }
- else
- {
- return;
- }
- }
- }
- // regex filter
- if ( $filter !== NULL && ! is_array($this->EE->TMPL->tagdata))
- {
- preg_match($filter, $this->EE->TMPL->tagdata, $found);
- if (isset($found[1]))
- {
- $this->EE->TMPL->tagdata = $found[1];
- }
- }
-
- // make sure we're working with a string
- // if we're setting a variable from a global ($_POST, $_GET etc), it could be an array
- if ( is_array($this->EE->TMPL->tagdata))
- {
- $this->EE->TMPL->tagdata = array_filter($this->EE->TMPL->tagdata, 'strlen');
- $this->EE->TMPL->tagdata = implode($delimiter, $this->EE->TMPL->tagdata);
- }
-
- if ( $this->_update )
- {
- // We're updating a variable, so lets see if it's in the session or db
- if ( ! array_key_exists($name, $this->_stash))
- {
- $this->_stash[$name] = $this->_run_tag('get', array('name', 'type', 'scope', 'context'));
- }
-
- // Append or prepend?
- if ( $this->_append )
- {
- $this->_stash[$name] .= $this->EE->TMPL->tagdata;
- }
- else
- {
- $this->_stash[$name] = $this->EE->TMPL->tagdata.$this->_stash[$name];
- }
- }
- else
- {
- $this->_stash[$name] = $this->EE->TMPL->tagdata;
- }
-
- // replace value into a {placeholder} anywhere in the current template?
- if ($set_placeholder)
- {
- $this->EE->TMPL->template = $this->EE->functions->var_swap(
- $this->EE->TMPL->template,
- array($name => $this->_stash[$name])
- );
- }
-
- // allow user- and site- scoped variables to be saved to the db
- // stop bots saving data to reduce unnecessary load on the server
- if ($save && $scope !== 'local' && self::$_is_human)
- {
- // optionally clean data before inserting
- $parameters = $this->_stash[$name];
-
- if ($this->xss_clean)
- {
- $this->EE->security->xss_clean($parameters);
- }
- // what's the intended variable scope?
- if ($scope === 'site')
- {
- $session_filter = '_global';
- }
- else
- {
- $session_filter =& $this->_session_id;
- }
-
- // let's check if there is an existing record
- $result = $this->EE->stash_model->get_key($stash_key, $this->bundle_id, $session_filter, $this->site_id);
- if ( $result !== FALSE)
- {
- // yes record exists, but do we want to update it?
- $update_key = FALSE;
- // is the new variable value identical to the value in the cache?
- // allow append/prepend if the stash key has been created *in this page load*
- $cache_key = $stash_key. '_'. $this->bundle_id .'_' .$this->site_id . '_' . $session_filter;
-
- if ( $result !== $parameters && ($this->replace || ($this->_update && $this->EE->stash_model->is_inserted_key($cache_key)) ))
- {
- $update_key = TRUE;
- }
- if ($update_key)
- {
- // update
- $this->EE->stash_model->update_key(
- $stash_key,
- $this->bundle_id,
- $session_filter,
- $this->site_id,
- $refresh,
- $parameters
- );
- }
- }
- else
- {
- // no record - insert one
-
- // Don't save if this template has a 404 header set from a redirect
- if ( $this->EE->output->out_type !== "404")
- {
- $this->EE->stash_model->insert_key(
- $stash_key,
- $this->bundle_id,
- $session_filter,
- $this->site_id,
- $refresh,
- $parameters,
- $label
- );
- }
- }
- }
- }
- else
- {
- // no name supplied, so let's assume we want to set sections of content within tag pairs
- // {stash:my_variable}...{/stash:my_variable}
- $vars = array();
- $tagdata = $this->EE->TMPL->tagdata;
-
- // context handling
- if ( $context !== NULL )
- {
- $prefix = $context . ':';
- $this->EE->TMPL->tagparams['context'] = NULL;
- }
- else
- {
- $prefix = '';
- }
-
- // if the tagdata has been parsed, we need to generate a new array of tag pairs
- // this permits dynamic tag pairs, e.g. {stash:{key}}{/stash:{key}}
- if ($this->parse_complete)
- {
- $tag_vars = $this->EE->functions->assign_variables($this->EE->TMPL->tagdata);
- $tag_pairs = $tag_vars['var_pair'];
- }
- else
- {
- $tag_pairs =& $this->EE->TMPL->var_pair;
- }
-
- foreach($tag_pairs as $key => $val)
- {
- if (strncmp($key, 'stash:', 6) == 0)
- {
- $pattern = '/'.LD.$key.RD.'(.*)'.LD.'\/'.$key.RD.'/Usi';
- preg_match($pattern, $tagdata, $matches);
- if (!empty($matches))
- {
- // set the variable, but cleanup first in case there are any nested tags
- $this->EE->TMPL->tagparams['name'] = $prefix . str_replace('stash:', '', $key);
- $this->EE->TMPL->tagdata = preg_replace('/'.LD.'stash:[a-zA-Z0-9\-_]+'.RD.'(.*)'.LD.'\/stash:[a-zA-Z0-9\-_]+'.RD.'/Usi', '', $matches[1]);
- $this->parse_complete = TRUE; // don't allow tagdata to be parsed
- $this->set();
- }
- }
- }
-
- // reset tagdata to original value
- $this->EE->TMPL->tagdata = $tagdata;
- unset($tagdata);
- }
- }
-
- if ( !! $name)
- {
- if ( $bundle !== NULL)
- {
- if ( ! isset(self::$bundles[$bundle]))
- {
- self::$bundles[$bundle] = array();
- }
- self::$bundles[$bundle][$name] = $this->_stash[$name];
- }
- $this->EE->TMPL->log_item('Stash: SET '. $name . ' to value ' . $this->_stash[$name]);
- }
-
- if ($output)
- {
- return $this->EE->TMPL->tagdata;
- }
- }
-
- // ---------------------------------------------------------
-
- /**
- * Get content from session, database, $_POST/$_GET superglobals or file
- *
- * @access public
- * @param mixed $params The name of the variable to retrieve, or an array of key => value pairs
- * @param string $type The type of variable
- * @param string $scope The scope of the variable
- * @return string
- */
- public function get($params='', $type='variable', $scope='user')
- {
- /* Sample use
- ---------------------------------------------------------
- {exp:stash:get name="title"}
-
- OR static call within PHP enabled templates or other add-on:
- <?php echo stash::get('title') ?>
- --------------------------------------------------------- */
- // is this method being called directly?
- if ( func_num_args() > 0)
- {
- if ( !(isset($this) && get_class($this) == __CLASS__))
- {
- return self::_api_static_call(__FUNCTION__, $params, $type, $scope);
- }
- else
- {
- return $this->_api_call(__FUNCTION__, $params, $type, $scope);
- }
- }
-
- if ( $this->process !== 'inline')
- {
- if ($out = $this->_post_parse(__FUNCTION__)) return $out;
- }
- $name = $this->EE->TMPL->fetch_param('name');
- $default = $this->EE->TMPL->fetch_param('default', NULL); // default value
- $dynamic = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('dynamic'));
- $save = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('save'));
- $scope = strtolower($this->EE->TMPL->fetch_param('scope', $this->default_scope)); // local|user|site
- $bundle = $this->EE->TMPL->fetch_param('bundle', NULL); // save in a bundle?
- $match = $this->EE->TMPL->fetch_param('match', NULL); // regular expression to test value against
- $filter = $this->EE->TMPL->fetch_param('filter', NULL); // regex pattern to search for
- // do we want this tag to return the value, or just set the variable quietly in the background?
- $output = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('output', 'yes'));
-
- // parse any vars in the $name parameter?
- if ($this->parse_vars)
- {
- $name = $this->_parse_template_vars($name);
- }
-
- // low search support - do we have a query string?
- $low_query = $this->EE->TMPL->fetch_param('low_query', NULL);
- // context handling
- $context = $this->EE->TMPL->fetch_param('context', NULL);
- $global_name = $name;
-
- if ($context !== NULL && count( explode(':', $name) == 1 ) )
- {
- $name = $context . ':' . $name;
- $this->EE->TMPL->tagparams['context'] = NULL;
- }
-
- // parse '@' context pointers
- $name_in_context = $this->_parse_context($name);
-
- // read from file?
- $file = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('file'));
- $file_name = $this->EE->TMPL->fetch_param('file_name', FALSE); // default value
-
- // when to parse the variable if reading from a file and saving:
- // before we save it to database (set) or when we retrieve it (get), or on set and get (both)
- $parse_stage = strtolower($this->EE->TMPL->fetch_param('parse_stage', 'set')); // set|get|both
-
- if ( !! $file_name)
- {
- $file = TRUE;
- }
- else
- {
- $file_name = $name;
- }
-
- // the variable value
- $value = NULL;
-
- // do we want to set the variable?
- $set = FALSE;
- // is it a segment? We need to support these in stash template files
- if (strncmp($name, 'segment_', 8) === 0)
- {
- $seg_index = substr($name, 8);
- $value = $this->EE->uri->segment($seg_index);
- }
- // let's see if it's been stashed before in this page load
- elseif ( is_string($name) && array_key_exists($name, $this->_stash))
- {
- $value = $this->_stash[$name];
- }
-
- // let's see if it exists in the current context
- elseif ( is_string($name_in_context) && array_key_exists($name_in_context, $this->_stash))
- {
- $value = $this->_stash[$name_in_context];
- $name = $name_in_context;
- }
-
- // not found in memory
- else
- {
- // has it been bundled?
- if ( ! is_null($bundle) && isset(self::$bundles[$bundle][$name]))
- {
- $value = $this->_stash[$name] = self::$bundles[$bundle][$name];
- }
- elseif ( ! $this->_update && ! ($dynamic && ! $save) && $scope !== 'local')
- {
- // let's look in the database table cache, but only if if we're not
- // appending/prepending or trying to register a global without saving it
-
- // narrow the scope to user?
- $session_id = $scope === 'user' ? $this->_session_id : '_global';
-
- // replace '@' placeholders with the current context
- $stash_key = $this->_parse_context($name);
-
- // look for our key
- if ( $parameters = $this->EE->stash_model->get_key(
- $stash_key,
- $this->bundle_id,
- $session_id,
- $this->site_id
- ))
- {
- // save to session
- $value = $this->_stash[$name] = $parameters;
- }
- }
- // Are we looking for a superglobal or uri segment?
- if ( ($dynamic && $value === NULL) || ($dynamic && $this->replace) )
- {
- $from_global = FALSE;
-
- // low search support
- if ($low_query !== NULL)
- {
- // has the query string been supplied or is it in POST?
- if (strncmp($low_query, 'stash:', 6) == 0)
- {
- $low_query = substr($low_query, 6);
- $low_query = $this->_stash[$low_query];
- }
- $low_query = @unserialize(base64_decode(str_replace('_', '/', $low_query)));
- if (isset( $low_query[$global_name] ))
- {
- $from_global = $low_query[$global_name];
- unset($low_query);
- }
- else
- {
- // set to empty value
- $from_global = '';
- }
- }
-
- // or is it in the $_POST or $_GET superglobals ( run through xss_clean() )?
- if ($from_global === FALSE)
- {
- $from_global = $this->EE->input->get_post($global_name, TRUE);
- }
-
- if ($from_global === FALSE)
- {
- // no, so let's check the uri segments
- $segs = $this->EE->uri->segment_array();
- foreach ( $segs as $index => $segment )
- {
- if ( $segment == $global_name && array_key_exists( ($index+1), $segs) )
- {
- $from_global = $segs[($index+1)];
- break;
- }
- }
- }
-
- if ($from_global !== FALSE)
- {
- // save to stash, and optionally to database, if save="yes"
- $value = $from_global;
- $set = TRUE;
- }
- }
-
- // Are we reading a file?
- if ( ($file && $value === NULL) || ($file && $this->replace) || ($file && $this->file_sync) )
- {
- // extract and remove the file extension, if provided
- $ext = 'html'; // default extension
- # PHP 5.3+ only
- # $file_ext = preg_filter('/^.*\./', '', $file_name);
- $file_ext = NULL;
- if ( preg_match('/^.*\./', $file_name) )
- {
- $file_ext = preg_replace('/^.*\./', '', $file_name);
- }
- // make sure the extension is allowed
- if ( ! is_null($file_ext))
- {
- if ( in_array($file_ext, $this->file_extensions))
- {
- $ext = $file_ext;
- }
- }
-
- // strip file ext (if any) and make sure we have a safe url encoded file path
- $file_path = preg_replace('/\.[^.]*$/', '', $file_name);
- #$file_path = explode(':', $file_path);
- $file_path = preg_split("/[:\/]+/", $file_path);
- foreach($file_path as &$part)
- {
- // make sure it's a valid url title
- $part = str_replace('.', '', $part);
- // insist upon alphanumeric characters and - or _
- $part = trim(preg_replace('/[^a-z0-9\-\_]+/', '-', strtolower($part)), '-');
- }
- unset($part); // remove reference
- // remove any empty url parts
- $file_path = array_filter($file_path);
-
- $file_path = $this->path . implode('/', $file_path) . '.' . $ext;
- if ( file_exists($file_path))
- {
- $this->EE->TMPL->log_item("Stash: reading file " . $file_path);
- $value = str_replace("\r\n", "\n", file_get_contents($file_path));
- $set = TRUE;
-
- // disable tag parsing on set when parse_stage is 'get'
- if ($parse_stage == 'get')
- {
- $this->parse_complete = TRUE;
- }
- }
- else
- {
- $this->EE->output->show_user_error('general', sprintf($this->EE->lang->line('stash_file_not_found'), $file_path));
- return;
- }
- }
- }
-
- // set to default value if it NULL or empty string (this permits '0' to be a valid value)
- if ( ($value === NULL || $value === '') && ! is_null($default))
- {
- $value = $default;
- $set = TRUE;
- }
-
- // create/update value of variable if required
- // note: don't save if we're updating a variable (to avoid recursion)
- if ( $set && ! $this->_update)
- {
- $this->EE->TMPL->tagparams['name'] = $name;
- $this->EE->TMPL->tagparams['output'] = 'yes';
- $this->EE->TMPL->tagdata = $value;
- $this->replace = TRUE;
- $value = $this->set();
- }
-
- $this->EE->TMPL->log_item('Stash: RETRIEVED '. $name . ' with value ' . $value);
-
- // save to bundle
- if ($bundle !== NULL)
- {
- if ( ! isset(self::$bundles[$bundle]))
- {
- self::$bundles[$bundle] = array();
- }
- self::$bundles[$bundle][$name] = $value;
- }
-
- // are we outputting the variable?
- if ($output)
- {
- if ( ! $file )
- {
- $value = $this->_parse_output($value, $match, $filter, $default);
- }
- else
- {
- // If this is a variable loaded originally from a file, parse if the desired parse stage is on retrieval (parse_stage="get|both")
- if ( ($parse_stage == 'get' || $parse_stage == 'both'))
- {
- $this->parse_complete = FALSE; // enable parsing
- $this->parse_vars = TRUE; // ensure early global and stash vars are always fully parsed
- $value = $this->_parse_output($value, $match, $filter, $default);
- }
- else
- {
- // ensure early global vars are always parsed
- $value = $this->_parse_template_vars($value);
- }
- }
- return $value;
- }
- }
- // ---------------------------------------------------------
-
- /**
- * Define default/fallback content for a stash variable from enclosed tagdata
- *
- * @access public
- * @return string
- */
- public function block()
- {
- /* Sample use
- ---------------------------------------------------------
- {exp:stash:block name="page_content"}
- default content
- {/exp:stash:block}
- {exp:stash:block:page_content}
- default content
- {/exp:stash:block:page_content}
- --------------------------------------------------------- */
- $tag_parts = $this->EE->TMPL->tagparts;
- if ( is_array( $tag_parts ) && isset( $tag_parts[2] ) )
- {
- if (isset($tag_parts[3]))
- {
- $this->EE->TMPL->tagparams['context'] = $this->EE->TMPL->fetch_param('context', $tag_parts[2]);
- $this->EE->TMPL->tagparams['name'] = $this->EE->TMPL->fetch_param('name', $tag_parts[3]);
- }
- else
- {
- // no context or name provided?
- if ( ! isset($this->EE->TMPL->tagparams['name']) AND ! isset($this->EE->TMPL->tagparams['context']))
- {
- $this->EE->TMPL->tagparams['context'] = 'block';
- }
- $this->EE->TMPL->tagparams['name'] = $this->EE->TMPL->fetch_param('name', $tag_parts[2]);
- }
- }
- // is this block dependent on one or more other stash variables *being set*?
- if ($requires = $this->EE->TMPL->fetch_param('requires', FALSE))
- {
- $requires = explode('|', $requires);
- foreach ($requires as $var)
- {
- if ( ! isset($this->_stash[$var]))
- {
- return '';
- }
- }
- }
- $this->EE->TMPL->tagparams['default'] = $this->EE->TMPL->tagdata;
- $this->EE->TMPL->tagdata = FALSE;
- return $this->get();
- }
- // ---------------------------------------------------------
-
- /**
- * Inject a stash embed into a variable or block
- *
- * @access public
- * @return string|void
- */
- public function extend()
- {
- /* Sample use
- ---------------------------------------------------------
- {exp:stash:extend name="content" with="views:my_embed" stash:my_var="value"}
-
- Or as a tag pair with an arbitrary 4th tagpart:
- {exp:stash:extend:block name="content" with="views:my_embed"}
- {stash:my_var}value{/stash:my_var}
- {/exp:stash:extend:block}
-
- --------------------------------------------------------- */
- if ( FALSE !== $with = $this->EE->TMPL->fetch_param('with', FALSE))
- {
- $embed_vars = array();
- unset($this->EE->TMPL->tagparams['with']);
- // embed vars passed as params
- foreach ($this->EE->TMPL->tagparams as $key => $val)
- {
- if (strncmp($key, 'stash:', 6) == 0)
- {
- $embed_vars[substr($key, 6)] = $val;
- unset($this->EE->TMPL->tagparams[$key]);
- }
- }
- // if this is a tag pair, capture data enclosed by {stash:...} pairs
- if ($this->EE->TMPL->tagdata)
- {
- foreach($this->EE->TMPL->var_pair as $key => $val)
- {
- if (strncmp($key, 'stash:', 6) == 0)
- {
- $pattern = '/'.LD.$key.RD.'(.*)'.LD.'\/'.$key.RD.'/Usi';
- preg_match($pattern, $this->EE->TMPL->tagdata, $matches);
- if ( ! empty($matches))
- {
- $embed_vars[substr($key, 6)] = $matches[1];
- }
- }
- }
- }
-
- // add embed vars directly to the stash session cache
- #$this->EE->session->cache['stash'] = array_merge($this->EE->session->cache['stash'], $embed_vars);
- // add embed vars to the static cache for parsing at the point the extended embed is included into the page
- if (count($embed_vars) > 0)
- {
- if ( ! isset(self::$_cache['embed_vars']))
- {
- self::$_cache['embed_vars'] = array();
- }
- // generate a unique id to identify this embed instance
- $id = $this->EE->functions->random();
- // cache the variables for later
- self::$_cache['embed_vars'][$id] = $embed_vars;
- // add id as a parameter
- $with .= ' id="' . $id .'"';
- }
- // now inject the embed into the named block/variable
- $this->EE->TMPL->tagdata = LD . 'exp:stash:embed:' . $with . RD;
- return $this->set();
- }
- }
- // ---------------------------------------------------------
-
- /**
- * Clone a variable / list
- *
- * @access public
- * @return string
- */
- public function copy()
- {
- /* Sample use
- ---------------------------------------------------------
- {exp:stash:copy
- name="original_name"
- context="original_context"
- scope="original_scope"
- type="original_type"
- copy_name="copy_name"
- copy_context="copy_context"
- copy_scope="copy_scope"
- copy_type="copy_type"
- }
- --------------------------------------------------------- */
- // get the original variable value, restricting which params are passed to a minimum
- $this->EE->TMPL->tagdata = $this->_run_tag('get', array('name', 'type', 'scope', 'context'));
- // prep the tagparams with the values for the clone
- $this->EE->TMPL->tagparams['name'] …
Large files files are truncated, but you can click here to view the full file