/** * Constructor * * Evaluates case values and extracts the content of the * first case that matches the variable parameter * * @access public * @return void */ public function Switchee() { $this->EE =& get_instance(); // reduce the PCRE default recursion limit to a safe level to prevent a server crash // (segmentation fault) when the available stack is exhausted before recursion limit reached // Apache *nix executable stack size is 8Mb, so safe size is 16777 // Apache Win32 executable stack size is 256Kb, so safe size is 524 ini_set('pcre.recursion_limit', '16777'); // PCRE default backtrack limit is low on PHP <5.3.6 // Increase it to the default value in newer versions of PHP ini_set('pcre.backtrack_limit', '1000000'); // fetch the tagdata $tagdata = $this->EE->TMPL->tagdata; // the variable we want to find $var = $this->EE->TMPL->fetch_param('variable') ? $this->EE->TMPL->fetch_param('variable') : ''; $match_all = $this->EE->TMPL->fetch_param('match') == 'all'; // debug? $debug = (bool) preg_match('/1|on|yes|y/i', $this->EE->TMPL->fetch_param('debug')); // register POST and GET values if (strncmp($var, 'get:', 4) == 0) { $var = filter_var($this->EE->input->get(substr($var, 4)), FILTER_SANITIZE_STRING); } if (strncmp($var, 'post:', 5) == 0) { $var = filter_var($this->EE->input->post(substr($var, 5)), FILTER_SANITIZE_STRING); } // register variables created by Stash if (strncmp($var, 'stash:', 6) == 0) { $var = substr($var, 6); if (isset($this->EE->session->cache['stash']) && isset($this->EE->session->cache['stash'][$var])) { // first look in the native stash variables array, for speed's sake $var = $this->EE->session->cache['stash'][$var]; } else { // we'll need to invoke Stash itself if (!class_exists('Stash')) { include_once PATH_THIRD . 'stash/mod.stash.php'; } $var = Stash::get($var); } } // register global vars if (strncmp($var, 'global:', 7) == 0) { $var = substr($var, 7); if (array_key_exists($var, $this->EE->config->_global_vars)) { $var = $this->EE->config->_global_vars[$var]; } else { // global has not been parsed yet, so we'll do it the hard way (this adds some overhead) $var = $this->EE->TMPL->parse_globals(LD . $var . RD); } } // log if ($debug) { $this->EE->TMPL->log_item("Switchee: evaluating variable {$var}"); } // replace content inside nested tags with indexed placeholders, storing it in an array for later // here's the tricky bit - we only match outer tags /* $pattern = '/{switchee(?>(?!{\/?switchee).|(?R))*{\/switchee/si'; */ // more memory efficient version of the above... $pattern = '#{switchee(?>(?:[^{]++|{(?!\\/?switchee[^}]*}))+|(?R))*{\\/switchee#si'; $tagdata = preg_replace_callback($pattern, array(get_class($this), '_placeholders'), $tagdata); // returns NULL on PCRE error if ($tagdata === NULL && $debug) { $this->_pcre_error(); } // loop through case parameters and find a case pair value that matches our variable $index = 0; // now we need to generate a new array of tag pairs for our tagdata $tag_vars = $this->EE->functions->assign_variables($tagdata); $has_match = false; $temp_return_data = ''; $default = ''; foreach ($tag_vars['var_pair'] as $key => $val) { // is this tag pair a case? if (preg_match('/^case/', $key)) { // index of the case tag pair we're looking at $index++; // replace any regex in the case values with a marker $tagdata = str_replace($key, 'case_' . $index, $tagdata); // get the position of the content inside the case being evaluated $starts_at = strpos($tagdata, "{case_" . $index . "}") + strlen("{case_" . $index . "}"); $ends_at = strpos($tagdata, "{/case}", $starts_at); if (isset($val['value'])) { $val_array = array(); if (stristr($val['value'], '|')) { $val_array = explode('|', $val['value']); } else { $val_array[] = $val['value']; } // loop through each value and look for a match foreach ($val_array as $case_index => $case_value) { // convert '' and "" to an actual empty string if ($case_value == "''" || $case_value == '""') { $case_value = ''; } // decode any encoded characters $case_value = $this->EE->security->entity_decode($case_value); $var = $this->EE->security->entity_decode($var); // is the case value a regular expression? // check for a string contained within hashes #regex# if (preg_match('/^#(.*)#$/', $case_value)) { if (preg_match($case_value, $var)) { // we've found a match, grab case content and exit loop $temp_return_data .= substr($tagdata, $starts_at, $ends_at - $starts_at); // log if ($debug) { $this->EE->TMPL->log_item("Switchee: regex match: case '{$case_value}' matched variable '{$var}'"); } $has_match = true; if ($match_all) { break; } else { break 2; } } } if ($case_value == $var) { // we've found a match, grab case content and exit loop $temp_return_data .= substr($tagdata, $starts_at, $ends_at - $starts_at); // log if ($debug) { $this->EE->TMPL->log_item("Switchee: string match: case '{$case_value}' matched variable '{$var}'"); } $has_match = true; if ($match_all) { break; } else { break 2; } } } } // default value if (!$has_match && isset($val['default'])) { $default_param = strtolower($val['default']); if ($default_param == 'yes' || $default_param == 'y' || $default_param == 'true' || $default_param == '1') { // found a default, save matched content but keep search for a match (continue loop) $default = substr($tagdata, $starts_at, $ends_at - $starts_at); // log if ($debug) { $this->EE->TMPL->log_item("Switchee: default case found for variable '{$var}'. This will be returned if no match is found."); } } } } } // fallback to default value if no matches if (!$has_match) { $temp_return_data = $default; } // replace namespaced no_results with the real deal $temp_return_data = str_replace(strtolower(__CLASS__) . '_no_results', 'no_results', $temp_return_data); // restore original content inside nested tags foreach ($this->_ph as $index => $val) { // convert the outer shell of {switchee} tag pairs to plugin tags {exp:switchee} // now we can do this all over again... $val = preg_replace(array('/^{switchee/i', '/{\\/switchee$/i'), array('{exp:switchee', '{/exp:switchee'), $val); $temp_return_data = str_replace('{[_' . __CLASS__ . '_' . ($index + 1) . ']', $val, $temp_return_data); } $this->return_data = $temp_return_data; }
/** * Method for template_fetch_template hook * * Inject early stash embeds into the template * * @access public * @param array * @return array */ public function template_fetch_template($row) { // get the latest version of $row if (isset($this->EE->extensions->last_call) && $this->EE->extensions->last_call) { $row = $this->EE->extensions->last_call; } // do we have any stash embeds? {stash:embed name=""} or {stash:embed:name} $matches = array(); if (!preg_match_all("/(" . LD . "stash:embed)([\\s|:].*?)" . RD . "/s", $row['template_data'], $matches)) { return $row; } // deal with any unparsed {vars} inside parameters $temp = $row['template_data']; foreach ($matches[2] as $key => $val) { if (strpos($val, LD) !== FALSE) { $matches[0][$key] = $this->EE->functions->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); } } // match up embed params with tags $embeds = array(); foreach ($matches[2] as $key => $val) { $parts = preg_split("/\\s+/", $val, 2); $embed_params = isset($parts[1]) ? $this->EE->functions->assign_parameters($parts[1]) : array(); if ($embed_params === FALSE) { $embed_params = array(); } // support {stash:embed:context:var} if (!empty($parts[0])) { $embed_params['name'] = trim($parts[0], ':'); } $embeds[trim($matches[0][$key], LD . RD)] = $embed_params; } if (count($embeds) > 0) { if (!class_exists('Stash')) { include_once PATH_THIRD . 'stash/mod.stash.php'; } foreach ($embeds as $tag => $param) { $out = ''; if (!empty($param)) { // process early? if (isset($param['process']) && $param['process'] == 'start') { // mandatory parameters $param['scope'] = 'site'; $param['file'] = 'yes'; $param['save'] = 'yes'; $param['bundle'] = 'template'; #$param['process'] = 'inline'; // set default parse parameters $param['parse_tags'] = isset($param['parse_tags']) ? $param['parse_tags'] : 'yes'; $param['parse_vars'] = isset($param['parse_vars']) ? $param['parse_vars'] : 'yes'; $param['parse_conditionals'] = isset($param['parse_conditionals']) ? $param['parse_conditionals'] : 'yes'; $param['parse_depth'] = isset($param['parse_depth']) ? $param['parse_depth'] : 4; $param['parse_stage'] = isset($param['parse_stage']) ? $param['parse_stage'] : 'get'; $param['replace'] = isset($param['replace']) ? $param['replace'] : 'no'; // We need to load modules/plugins, which hasn't been done by the template class at this stage in the parse order. // This only runs once - the template class won't run it again if modules[] array is populated. if (count($this->EE->TMPL->modules) == 0) { $this->EE->TMPL->fetch_addons(); } // parse stash embed vars passed as parameters in the form stash:my_var $embed_vars = array(); foreach ($param as $key => $val) { if (strncmp($key, 'stash:', 6) == 0) { $embed_vars[substr($key, 6)] = $val; } } // merge embed variables into the session cache in case they are used in another nested (or later-parsed) stash template if (!empty($embed_vars)) { // 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(); } $this->EE->session->cache['stash'] = array_merge($this->EE->session->cache['stash'], $embed_vars); } // get the file $out = Stash::get($param); // minimal replace of embed vars if we're not using Stash to parse the template variables if ($param['parse_vars'] == 'no') { foreach ($embed_vars as $key => $val) { $out = str_replace(LD . 'stash:' . $key . RD, $val, $out); } } // convert any nested {stash:embed} into {exp:stash:embed} tags $out = str_replace(LD . 'stash:embed', LD . 'exp:stash:embed', $out); } else { // convert it into a normal tag... $out = LD . 'exp:' . $tag . RD; } } // Set as a global variable so it gets replaced into the template early by the Template class // $row is a copy not a reference, so this is the only way to change stuff in the actual template! // Make sure the stash embeds are prepended to beginning of the globals array so that any global vars // passed as parameters get parsed later by EE $this->EE->config->_global_vars = array($tag => $out) + $this->EE->config->_global_vars; } } return $row; }