/assets/snippets/jot/includes/phx.parser.class.inc.php
PHP | 414 lines | 356 code | 16 blank | 42 comment | 12 complexity | d7857fdeac6465c2f6377042a2c6434e MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MIT, BSD-2-Clause, Apache-2.0, BSD-3-Clause
- <?php
- /*####
- #
- # Name: PHx (Placeholders Xtended)
- # Version: 2.1.2
- # Author: Armand "bS" Pondman (apondman@zerobarrier.nl)
- # Date: Feb 13, 2007 8:52 CET
- #
- ####*/
- class PHxParser {
- var $placeholders = array();
-
- function PHxParser($debug=0,$maxpass=50) {
- global $modx;
- $this->name = "PHx";
- $this->version = "2.1.2";
- $this->user["mgrid"] = intval($_SESSION['mgrInternalKey']);
- $this->user["usrid"] = intval($_SESSION['webInternalKey']);
- $this->user["id"] = ($this->user["usrid"] > 0 ) ? (-$this->user["usrid"]) : $this->user["mgrid"];
- $this->cache["cm"] = array();
- $this->cache["ui"] = array();
- $this->cache["mo"] = array();
- $this->safetags[0][0] = '~(?<![\[]|^\^)\[(?=[^\+\*\(\[]|$)~s';
- $this->safetags[0][1] = '~(?<=[^\+\*\)\]]|^)\](?=[^\]]|$)~s';
- $this->safetags[1][0] = '&_PHX_INTERNAL_091_&';
- $this->safetags[1][1] = '&_PHX_INTERNAL_093_&';
- $this->safetags[2][0] = '[';
- $this->safetags[2][1] = ']';
- $this->console = array();
- $this->debug = ($debug!='') ? $debug : 0;
- $this->debugLog = false;
- $this->curPass = 0;
- $this->maxPasses = ($maxpass!='') ? $maxpass : 50;
- $this->swapSnippetCache = array();
- $modx->setPlaceholder("phx", "&_PHX_INTERNAL_&");
- }
-
- // Plugin event hook for MODX
- function OnParseDocument() {
- global $modx;
- // Get document output from MODX
- $template = $modx->documentOutput;
- // To the parse cave .. let's go! *insert batman tune here*
- $template = $this->Parse($template);
- // Set processed document output in MODX
- $modx->documentOutput = $template;
- }
-
- // Parser: Preparation, cleaning and checkup
- function Parse($template='') {
- global $modx;
- // If we already reached max passes don't get at it again.
- if ($this->curPass == $this->maxPasses) return $template;
- // Set template pre-process hash
- $st = md5($template);
- // Replace non-call characters in the template: [, ]
- $template = preg_replace($this->safetags[0],$this->safetags[1],$template);
- // To the parse mobile.. let's go! *insert batman tune here*
- $template = $this->ParseValues($template);
- // clean up unused placeholders that have modifiers attached (MODX can't clean them)
- preg_match_all('~\[(\+|\*|\()([^:\+\[\]]+)([^\[\]]*?)(\1|\))\]~s', $template, $matches);
- if ($matches[0]) {
- $template = str_replace($matches[0], '', $template);
- $this->Log("Cleaning unsolved tags: \n" . implode("\n",$matches[2]) );
- }
- // Restore non-call characters in the template: [, ]
- $template = str_replace($this->safetags[1],$this->safetags[2],$template);
- // Set template post-process hash
- $et = md5($template);
- // If template has changed, parse it once more...
- if ($st!=$et) $template = $this->Parse($template);
- // Write an event log if debugging is enabled and there is something to log
- if ($this->debug && $this->debugLog) {
- $modx->logEvent($this->curPass,1,$this->createEventLog(), $this->name.' '.$this->version);
- $this->debugLog = false;
- }
- // Return the processed template
- return $template;
- }
-
- // Parser: Tag detection and replacements
- function ParseValues($template='') {
- global $modx;
-
- $this->curPass = $this->curPass + 1;
- $st = md5($template);
- //$this->LogSource($template);
- $this->LogPass();
-
- // MODX Chunks
- $this->Log("MODX Chunks -> Merging all chunk tags");
- $template = $modx->mergeChunkContent($template);
-
- // MODX Snippets
- //if ( preg_match_all('~\[(\[|!)([^\[]*?)(!|\])\]~s',$template, $matches)) {
- if ( preg_match_all('~\[(\[)([^\[]*?)(\])\]~s',$template, $matches)) {
- $count = count($matches[0]);
- $var_search = array();
- $var_replace = array();
-
- // for each detected snippet
- for($i=0; $i<$count; $i++) {
- $snippet = $matches[2][$i]; // snippet call
- $this->Log("MODX Snippet -> ".$snippet);
-
- // Let MODX evaluate snippet
- $replace = $modx->evalSnippets("[[".$snippet."]]");
- $this->LogSnippet($replace);
-
- // Replace values
- $var_search[] = $matches[0][$i];
- $var_replace[] = $replace;
- }
- $template = str_replace($var_search, $var_replace, $template);
- }
-
- // PHx / MODX Tags
- if ( preg_match_all('~\[(\+|\*|\()([^:\+\[\]]+)([^\[\]]*?)(\1|\))\]~s',$template, $matches)) {
- //$matches[0] // Complete string that's need to be replaced
- //$matches[1] // Type
- //$matches[2] // The placeholder(s)
- //$matches[3] // The modifiers
- //$matches[4] // Type (end character)
-
- $count = count($matches[0]);
- $var_search = array();
- $var_replace = array();
- for($i=0; $i<$count; $i++) {
- $replace = NULL;
- $match = $matches[0][$i];
- $type = $matches[1][$i];
- $type_end = $matches[4][$i];
- $input = $matches[2][$i];
- $modifiers = $matches[3][$i];
- $var_search[] = $match;
- switch($type) {
- // Document / Template Variable eXtended
- case "*":
- $this->Log("MODX TV/DV: " . $input);
- $input = $modx->mergeDocumentContent("[*".$input."*]");
- $replace = $this->Filter($input,$modifiers);
- break;
- // MODX Setting eXtended
- case "(":
- $this->Log("MODX Setting variable: " . $input);
- $input = $modx->mergeSettingsContent("[(".$input.")]");
- $replace = $this->Filter($input,$modifiers);
- break;
- // MODX Placeholder eXtended
- default:
- $this->Log("MODX / PHx placeholder variable: " . $input);
- // Check if placeholder is set
- if ( !array_key_exists($input, $this->placeholders) && !array_key_exists($input, $modx->placeholders) ) {
- // not set so try again later.
- $replace = $match;
- $this->Log(" |--- Skipping - hasn't been set yet.");
- }
- else {
- // is set, get value and run filter
- $input = $this->getPHxVariable($input);
- $replace = $this->Filter($input,$modifiers);
- }
- break;
- }
- $var_replace[] = $replace;
- }
- $template = str_replace($var_search, $var_replace, $template);
- }
- $et = md5($template); // Post-process template hash
-
- // Log an event if this was the maximum pass
- if ($this->curPass == $this->maxPasses) $this->Log("Max passes reached. infinite loop protection so exiting.\n If you need the extra passes set the max passes to the highest count of nested tags in your template.");
- // If this pass is not at maximum passes and the template hash is not the same, get at it again.
- if (($this->curPass < $this->maxPasses) && ($st!=$et)) $template = $this->ParseValues($template);
- return $template;
- }
-
- // Parser: modifier detection and eXtended processing if needed
- function Filter($input, $modifiers) {
- global $modx;
- $output = $input;
- $this->Log(" |--- Input = '". $output ."'");
- if (preg_match_all('~:([^:=]+)(?:=`(.*?)`(?=:[^:=]+|$))?~s',$modifiers, $matches)) {
- $modifier_cmd = $matches[1]; // modifier command
- $modifier_value = $matches[2]; // modifier value
- $count = count($modifier_cmd);
- $condition = array();
- for($i=0; $i<$count; $i++) {
- $output = trim($output);
- $this->Log(" |--- Modifier = '". $modifier_cmd[$i] ."'");
- if ($modifier_value[$i] != '') $this->Log(" |--- Options = '". $modifier_value[$i] ."'");
- switch ($modifier_cmd[$i]) {
- ##### Conditional Modifiers
- case "input": case "if": $output = $modifier_value[$i]; break;
- case "equals": case "is": case "eq": $condition[] = intval(($output==$modifier_value[$i])); break;
- case "notequals": case "isnot": case "isnt": case "ne":$condition[] = intval(($output!=$modifier_value[$i]));break;
- case "isgreaterthan": case "isgt": case "eg": $condition[] = intval(($output>=$modifier_value[$i]));break;
- case "islowerthan": case "islt": case "el": $condition[] = intval(($output<=$modifier_value[$i]));break;
- case "greaterthan": case "gt": $condition[] = intval(($output>$modifier_value[$i]));break;
- case "lowerthan": case "lt":$condition[] = intval(($output<$modifier_value[$i]));break;
- case "isinrole": case "ir": case "memberof": case "mo": // Is Member Of (same as inrole but this one can be stringed as a conditional)
- if ($output == "&_PHX_INTERNAL_&") $output = $this->user["id"];
- $grps = (strlen($modifier_value) > 0 ) ? array_filter(array_map('trim', explode(',', $modifier_value[$i]))) :array();
- $condition[] = intval($this->isMemberOfWebGroupByUserId($output,$grps));
- break;
- case "or":$condition[] = "||";break;
- case "and": $condition[] = "&&";break;
- case "show":
- $conditional = implode(' ',$condition);
- $isvalid = intval(eval("return (". $conditional. ");"));
- if (!$isvalid) { $output = NULL;}
- case "then":
- $conditional = implode(' ',$condition);
- $isvalid = intval(eval("return (". $conditional. ");"));
- if ($isvalid) { $output = $modifier_value[$i]; }
- else { $output = NULL; }
- break;
- case "else":
- $conditional = implode(' ',$condition);
- $isvalid = intval(eval("return (". $conditional. ");"));
- if (!$isvalid) { $output = $modifier_value[$i]; }
- break;
- case "select":
- $raw = explode("&",$modifier_value[$i]);
- $map = array();
- for($m=0; $m<(count($raw)); $m++) {
- $mi = explode("=",$raw[$m]);
- $map[$mi[0]] = $mi[1];
- }
- $output = $map[$output];
- break;
- ##### End of Conditional Modifiers
-
- ##### String Modifiers
- case "lcase": $output = strtolower($output); break;
- case "ucase": $output = strtoupper($output); break;
- case "ucfirst": $output = ucfirst($output); break;
- case "htmlent": $output = htmlentities($output,ENT_QUOTES,$modx->config['etomite_charset']); break;
- case "esc":
- $output = preg_replace("/&(#[0-9]+|[a-z]+);/i", "&$1;", htmlspecialchars($output));
- $output = str_replace(array("[","]","`"),array("[","]","`"),$output);
- break;
- case "strip": $output = preg_replace("~([\n\r\t\s]+)~"," ",$output); break;
- case "notags": $output = strip_tags($output); break;
- case "length": case "len": $output = strlen($output); break;
- case "reverse": $output = strrev($output); break;
- case "wordwrap": // default: 70
- $wrapat = intval($modifier_value[$i]) ? intval($modifier_value[$i]) : 70;
- $output = preg_replace("~(\b\w+\b)~e","wordwrap('\\1',\$wrapat,' ',1)",$output);
- break;
- case "limit": // default: 100
- $limit = intval($modifier_value[$i]) ? intval($modifier_value[$i]) : 100;
- $output = substr($output,0,$limit);
- break;
-
- ##### Special functions
- case "math":
- $filter = preg_replace("~([a-zA-Z\n\r\t\s])~","",$modifier_value[$i]);
- $filter = str_replace("?",$output,$filter);
- $output = eval("return ".$filter.";");
- break;
- case "ifempty": if (empty($output)) $output = $modifier_value[$i]; break;
- case "nl2br": $output = nl2br($output); break;
- case "date": $output = strftime($modifier_value[$i],0+$output); break;
- case "set":
- $c = $i+1;
- if ($count>$c&&$modifier_cmd[$c]=="value") $output = preg_replace("~([^a-zA-Z0-9])~","",$modifier_value[$i]);
- break;
- case "value":
- if ($i>0&&$modifier_cmd[$i-1]=="set") { $modx->SetPlaceholder("phx.".$output,$modifier_value[$i]); }
- $output = NULL;
- break;
- case "md5": $output = md5($output); break;
- case "userinfo":
- if ($output == "&_PHX_INTERNAL_&") $output = $this->user["id"];
- $output = $this->ModUser($output,$modifier_value[$i]);
- break;
- case "inrole": // deprecated
- if ($output == "&_PHX_INTERNAL_&") $output = $this->user["id"];
- $grps = (strlen($modifier_value) > 0 ) ? array_filter(array_map('trim', explode(',', $modifier_value[$i]))) :array();
- $output = intval($this->isMemberOfWebGroupByUserId($output,$grps));
- break;
- default:
- if (!array_key_exists($modifier_cmd[$i], $this->cache["cm"])) {
- $result = $modx->db->select('snippet', $modx->getFullTableName("site_snippets"), "name='phx:".$modifier_cmd[$i]."'");
- if ($snippet = $modx->db->getValue($result)) {
- $cm = $this->cache["cm"][$modifier_cmd[$i]] = $snippet;
- $this->Log(" |--- DB -> Custom Modifier");
- }
- } else {
- $cm = $this->cache["cm"][$modifier_cmd[$i]];
- $this->Log(" |--- Cache -> Custom Modifier");
- }
- ob_start();
- $options = $modifier_value[$i];
- $custom = eval($cm);
- $msg = ob_get_contents();
- $output = $msg.$custom;
- ob_end_clean();
- break;
- }
- if (count($condition)) $this->Log(" |--- Condition = '". $condition[count($condition)-1] ."'");
- $this->Log(" |--- Output = '". $output ."'");
- }
- }
- return $output;
- }
-
- // Event logging (debug)
- function createEventLog() {
- if($this->console) {
- $console = implode("\n",$this->console);
- $this->console = array();
- return '<pre style="overflow: auto;">' . $console . '</pre>';
- }
- }
-
- // Returns a cleaned string escaping the HTML and special MODX characters
- function LogClean($string) {
- $string = preg_replace("/&(#[0-9]+|[a-z]+);/i", "&$1;", htmlspecialchars($string));
- $string = str_replace(array("[","]","`"),array("[","]","`"),$string);
- return $string;
- }
-
- // Simple log entry
- function Log($string) {
- if ($this->debug) {$this->debugLog = true; $this->console[] = (count($this->console)+1-$this->curPass). " [". strftime("%H:%M:%S",time()). "] " . $this->LogClean($string);}
- }
-
- // Log snippet output
- function LogSnippet($string) {
- if ($this->debug) {$this->debugLog = true; $this->console[] = (count($this->console)+1-$this->curPass). " [". strftime("%H:%M:%S",time()). "] " . " |--- Returns: <div style='margin: 10px;'>".$this->LogClean($string)."</div>";}
- }
-
- // Log pass
- function LogPass() {
- $this->console[] = "<div style='margin: 2px;margin-top: 5px;border-bottom: 1px solid black;'>Pass " . $this->curPass . "</div>";
- }
-
- // Log pass
- function LogSource($string) {
- $this->console[] = "<div style='margin: 2px;margin-top: 5px;border-bottom: 1px solid black;'>Source:</div>" . $this->LogClean($string);
- }
-
-
- // Returns the specified field from the user record
- // positive userid = manager, negative integer = webuser
- function ModUser($userid,$field) {
- global $modx;
- if (!array_key_exists($userid, $this->cache["ui"])) {
- if (intval($userid) < 0) {
- $user = $modx->getWebUserInfo(-($userid));
- } else {
- $user = $modx->getUserInfo($userid);
- }
- $this->cache["ui"][$userid] = $user;
- } else {
- $user = $this->cache["ui"][$userid];
- }
- return $user[$field];
- }
-
- // Returns true if the user id is in one the specified webgroups
- function isMemberOfWebGroupByUserId($userid=0,$groupNames=array()) {
- global $modx;
-
- // if $groupNames is not an array return false
- if(!is_array($groupNames)) return false;
-
- // if the user id is a negative number make it positive
- if (intval($userid) < 0) { $userid = -($userid); }
-
- // Creates an array with all webgroups the user id is in
- if (!array_key_exists($userid, $this->cache["mo"])) {
- $tbl = $modx->getFullTableName("webgroup_names");
- $tbl2 = $modx->getFullTableName("web_groups");
- $rs = $modx->db->select('wgn.name', "$tbl AS wgn INNER JOIN $tbl2 AS wg ON wg.webgroup=wgn.id AND wg.webuser='{$userid}'");
- $this->cache["mo"][$userid] = $grpNames = $modx->db->getColumn("name",$rs);
- } else {
- $grpNames = $this->cache["mo"][$userid];
- }
- // Check if a supplied group matches a webgroup from the array we just created
- foreach($groupNames as $k=>$v)
- if(in_array(trim($v),$grpNames)) return true;
-
- // If we get here the above logic did not find a match, so return false
- return false;
- }
-
- // Returns the value of a PHx/MODX placeholder.
- function getPHxVariable($name) {
- global $modx;
- // Check if this variable is created by PHx
- if (array_key_exists($name, $this->placeholders)) {
- // Return the value from PHx
- return $this->placeholders[$name];
- } else {
- // Return the value from MODX
- return $modx->getPlaceholder($name);
- }
- }
-
- // Sets a placeholder variable which can only be access by PHx
- function setPHxVariable($name, $value) {
- if ($name != "phx") $this->placeholders[$name] = $value;
- }
- }
- ?>