Skip to content

Instantly share code, notes, and snippets.

@croxton
Created November 3, 2011 19:22
Show Gist options
  • Save croxton/1337517 to your computer and use it in GitHub Desktop.
Save croxton/1337517 to your computer and use it in GitHub Desktop.
Switchee with adjusted PCRE limit
<?php
$plugin_info = array(
'pi_name' => 'Switchee',
'pi_version' =>'2.0.4a',
'pi_author' =>'Mark Croxton',
'pi_author_url' => 'http://www.hallmark-design.co.uk/',
'pi_description' => 'Switch/case control structure for templates',
'pi_usage' => Switchee::usage()
);
class Switchee {
public $return_data = '';
private $_ph = array();
/**
* 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();
// fetch the tagdata
$tagdata = $this->EE->TMPL->tagdata;
// adjust the pcre backtrack limit in we're parsing a large string
if( strlen($tagdata) > 7000)
{
ini_set('pcre.backtrack_limit','250000');
}
// the variable we want to find
$var = $this->EE->TMPL->fetch_param('variable') ? $this->EE->TMPL->fetch_param('variable') : '';
// 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
// warning: stash will create a new template object, overwriting the current instance
if (strncmp($var, 'stash:', 6) == 0)
{
$var = substr($var, 6);
$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: {$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';
$tagdata = preg_replace_callback($pattern, array(get_class($this), '_placeholders'), $tagdata);
// 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);
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++;
// define the pattern we're searching for in tagdata that encloses the current case content
// make search string safe by replacing any regex in the case values with a marker
$pattern = '/{case_'.$index.'}(.*){\/case}/Usi';
$tagdata = str_replace($key, 'case_'.$index, $tagdata);
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
preg_match($pattern, $tagdata, $matches);
$this->return_data = @$matches[1]; // fail gracefully
break 2;
}
}
if ($case_value == $var)
{
// we've found a match, grab case content and exit loop
preg_match($pattern, $tagdata, $matches);
$this->return_data = @$matches[1];
break 2;
}
}
}
// default value
if(isset($val['default']))
{
if(strtolower($val['default']) == 'yes' || strtolower($val['default']) == 'true' || $val['default'] == '1')
{
// found a default, save matched content and continue loop
preg_match($pattern, $tagdata, $matches);
$this->return_data = @$matches[1];
}
}
}
}
// replace namespaced no_results with the real deal
$this->return_data = str_replace(strtolower(__CLASS__).'_no_results', 'no_results', $this->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);
$this->return_data = str_replace('[_'.__CLASS__.'_'.($index+1).']', $val, $this->return_data);
}
}
/**
* _placeholders
*
* Replaces nested tag content with placeholders
*
* @access private
* @param array
* @return string
*/
private function _placeholders($matches)
{
$this->_ph[] = $matches[0];
return '[_'.__CLASS__.'_'.count($this->_ph).']';
}
// usage instructions
public function usage()
{
ob_start();
?>
-------------------
HOW TO USE
-------------------
{exp:switchee variable = "{variable_to_test}" parse="inward"}
{case value="value1|value2"}
Content to show
{/case}
{case value="value3" default="Yes"}
Content to show
{/case}
{case value="#^P(\d+)$#|''"}
Use regular expressions enclosed by hashes #regex#
Be careful to encode the following reserved characters as follows:
{ = &#123;
| = &#124;
} = &#125;
Use '' to represent an empty string
{/case}
{case value="value4" default="Yes"}
{!-- you can also nest Switchee by leaving off the 'exp:' in nested tags : --}
{switchee variable="{another_variable_to_test}" parse="inward"}
{case value="value1" default="Yes"}
nested content to show
{/case}
{/switchee}
{/case}
{/exp:switchee}
How to support no_result blocks inside wrapped tags:
{if switchee_no_results}
{redirect="channel/noresult"}
{/if}
GET and POST globals can be evaluated by prefixing with get: or post:, e.g.:
{exp:switchee variable = "post:my_var" parse="inward"}
Any global variable can be evaluated by prefixing with global:, e.g.:
{exp:switchee variable = "global:my_var" parse="inward"}
Any Stash module variable can be evaluated by prefixing with stash:, e.g.:
{exp:switchee variable = "stash:my_var" parse="inward"}
Debugging:
To enable logging add the parameter debug="yes", e.g.:
{exp:switchee variable = "my_var" parse="inward" debug="yes"}
<?php
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment