public function Toolset_Parser($exp = null) { if (isset($exp) && $exp !== null) { $this->strInFix = $exp; $this->arrTokens = $this->arrPostFix = null; } // init tokenizer here, else tokens become undefined on addVar Toolset_Tokenizer::init(); }
private function _EvaluateExpression(&$myarrPostFix, &$myvarArr) { if (!isset($myarrPostFix)) { $myarrPostFix = $this->_ParseExpression(); } if (count($myarrPostFix) == 0) { throw new Exception("Unable to parse the expression!"); } if (!isset($myarrPostFix) || count($myarrPostFix) == 0) { throw new Exception("Invalid postfix expression!"); } $intIndex = 0; $myStack = new Toolset_Stack(); //echo $this->_dumpPostFix($myarrPostFix); while ($intIndex < count($myarrPostFix)) { //echo $myStack->toString(); $strTok = $myarrPostFix[$intIndex]; switch ($strTok->type) { case Toolset_Tokenizer::$TOKEN_TYPE['ARG_TERMINAL']: $myStack->Push($strTok); break; case Toolset_Tokenizer::$TOKEN_TYPE['UNARY_NEGATIVE']: if ($myStack->IsEmpty()) { throw new Exception("No operand to negate!"); } $objOp1 = null; $objOp2 = null; $objOp1 = $myStack->Pop(); if ($objOp1->isVariable) { $objOp1 = $this->_getVariable($objOp1->val, $myvarArr); } $dblNo = Toolset_Tokenizer::toNumber($objOp1->val); if (is_nan($dblNo)) { throw new Exception("Not a numeric value!"); } else { $dblNo = 0 - $dblNo; $myStack->Push(Toolset_Tokenizer::makeToken($dblNo, Toolset_Tokenizer::$TOKEN_TYPE['NUMBER'])); } break; case Toolset_Tokenizer::$TOKEN_TYPE['UNARY_NEGATION']: if ($myStack->IsEmpty()) { throw new Exception("No operand on stack!"); } $objOp1 = null; $objOp2 = null; $objOp1 = $myStack->Pop(); if ($objOp1->isVariable) { $objOp1 = $this->_getVariable($objOp1->val, $myvarArr); } $objOp1 = Toolset_Tokenizer::toBoolean($objOp1->val); if ($objOp1 === null) { throw new Exception($strTok->val . " applied not on a boolean value!"); } else { $myStack->Push(Toolset_Tokenizer::makeToken(!$objOp1, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } break; case Toolset_Tokenizer::$TOKEN_TYPE['ARITHMETIC_OP']: switch ($strTok->val) { case "*": case "/": case "%": case "^": if ($myStack->IsEmpty() || $myStack->Size() < 2) { throw new Exception("Stack is empty, can not perform [" . $strTok->val . "]"); } $objOp1 = null; $objOp2 = null; $objTmp = null; $objOp2 = $myStack->Pop(); $objOp1 = $myStack->Pop(); if ($objOp1->isVariable) { $objOp1 = $this->_getVariable($objOp1->val, $myvarArr); } if ($objOp2->isVariable) { $objOp2 = $this->_getVariable($objOp2->val, $myvarArr); } if (!$objOp1->isNumber || !$objOp2->isNumber) { throw new Exception("Either one of the operand is not a number can not perform [" . $strTok->val . "]"); } $dblVal1 = Toolset_Tokenizer::toNumber($objOp1->val); $dblVal2 = Toolset_Tokenizer::toNumber($objOp2->val); if (is_nan($dblVal1) || is_nan($dblVal2)) { throw new Exception("Either one of the operand is not a number can not perform [" . $strTok->val . "]"); } if ($strTok->val == "^") { $myStack->Push(Toolset_Tokenizer::makeToken(pow($dblVal1, $dblVal2), Toolset_Tokenizer::$TOKEN_TYPE['NUMBER'])); } else { if ($strTok->val == "*") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 * $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['NUMBER'])); } else { if ($strTok->val == "/") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 / $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['NUMBER'])); } else { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 % $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['NUMBER'])); } } } break; case "+": case "-": if ($myStack->IsEmpty() || $myStack->Size() < 2) { throw new Exception("Stack is empty, can not perform [" . $strTok->val . "]"); } $objOp1 = null; $objOp2 = null; $objTmp1 = null; $objTmp2 = null; $strOp = $strTok->val == "+" ? "Addition" : "Substraction"; $objOp2 = $myStack->Pop(); $objOp1 = $myStack->Pop(); if ($objOp1->isVariable) { $objOp1 = $this->_getVariable($objOp1->val, $myvarArr); } if ($objOp2->isVariable) { $objOp2 = $this->_getVariable($objOp2->val, $myvarArr); } if ($objOp1->isNumber && $objOp2->isNumber) { // Number addition $dblVal1 = Toolset_Tokenizer::toNumber($objOp1->val); $dblVal2 = Toolset_Tokenizer::toNumber($objOp2->val); if ($strTok->val == "+") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 + $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['NUMBER'])); } else { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 - $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['NUMBER'])); } } else { if ($objOp1->isStringLiteral && $objOp2->isStringLiteral) { if ($strTok->val == "+") { $myStack->Push(Toolset_Tokenizer::makeToken($objOp1->val . $objOp2->val, Toolset_Tokenizer::$TOKEN_TYPE['STRING_LITERAL'])); } else { throw new Exception($strOp . " not supported for strings!"); } } else { throw new Exception($strOp . " not supported for other types than numbers and strings!"); } } break; } break; case Toolset_Tokenizer::$TOKEN_TYPE['COMPARISON_OP']: switch ($strTok->val) { case "=": case "<": case ">": case "<>": case "<=": case ">=": case "eq": case "lt": case "gt": case "ne": case "lte": case "gte": if ($myStack->IsEmpty() || $myStack->Size() < 2) { throw new Exception("Stack is empty, can not perform [" . $strTok->val . "]"); } $objOp1 = null; $objOp2 = null; $objTmp1 = null; $objTmp2 = null; $objOp2 = $myStack->Pop(); $objOp1 = $myStack->Pop(); //cred_log(array($objOp1, $objOp2)); if ($objOp1->isVariable) { $objOp1 = $this->_getVariable($objOp1->val, $myvarArr); } if ($objOp2->isVariable) { $objOp2 = $this->_getVariable($objOp2->val, $myvarArr); } //cred_log(array($objOp1, $objOp2)); if ($objOp1->isStringLiteral && $objOp2->isNumber) { $dblVal1 = (string) $objOp1->val; $dblVal2 = (string) $objOp2->val; } else { if ($objOp1->isNumber && $objOp2->isStringLiteral) { $dblVal1 = (string) $objOp1->val; $dblVal2 = (string) $objOp2->val; } else { if ($objOp1->isNumber && $objOp2->isNumber) { $dblVal1 = Toolset_Tokenizer::toNumber($objOp1->val); $dblVal2 = Toolset_Tokenizer::toNumber($objOp2->val); } else { if ($objOp1->isNumber && $objOp2->isBoolean) { $dblVal1 = Toolset_Tokenizer::toNumber($objOp1->val); $dblVal2 = Toolset_Tokenizer::toNumber($objOp2->val); } else { if ($objOp2->isNumber && $objOp1->isBoolean) { $dblVal1 = Toolset_Tokenizer::toNumber($objOp1->val); $dblVal2 = Toolset_Tokenizer::toNumber($objOp2->val); } else { if ($objOp1->isDate && $objOp2->isDate) { $dblVal1 = $objOp1->val->getNormalizedTimestamp(); $dblVal2 = $objOp2->val->getNormalizedTimestamp(); } else { if ($objOp1->isStringLiteral && $objOp2->isStringLiteral) { $dblVal1 = (string) $objOp1->val; $dblVal2 = (string) $objOp2->val; } else { if ($objOp1->isBoolean && $objOp2->isBoolean) { if ($strTok->val == "=" || $strTok->val == "<>" || $strTok->val == "eq" || $strTok->val == "ne") { $dblVal1 = Toolset_Tokenizer::toBoolean($objOp1->val); $dblVal2 = Toolset_Tokenizer::toBoolean($objOp2->val); } else { throw new Exception($strTok->val + " not supported for boolean values!"); } } else { if (($strTok->val == '=' || $strTok->val == '<>' || $strTok->val == 'eq' || $strTok->val == 'ne') && ($objOp1->isStringLiteral && $objOp2->isRegex)) { if ($strTok->val == '=' || $strTok->val == 'eq') { $myStack->Push(Toolset_Tokenizer::makeToken($objOp2->val->test((string) $objOp1->val), Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { $myStack->Push(Toolset_Tokenizer::makeToken(!$objOp2->val->test((string) $objOp1->val), Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } break; } else { if (($strTok->val == '=' || $strTok->val == '<>' || $strTok->val == 'eq' || $strTok->val == 'ne') && ($objOp2->isStringLiteral && $objOp1->isRegex)) { if ($strTok->val == '=' || $strTok->val == 'eq') { $myStack->Push(Toolset_Tokenizer::makeToken($objOp1->val->test((string) $objOp2->val), Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { $myStack->Push(Toolset_Tokenizer::makeToken(!$objOp1->val->test((string) $objOp2->val), Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } break; } else { if (($strTok->val == '=' || $strTok->val == '<>' || $strTok->val == 'eq' || $strTok->val == 'ne') && ($objOp1->isArray && ($objOp2->isStringLiteral || $objOp2->isNumber))) { if ($strTok->val == '=' || $strTok->val == 'eq') { $myStack->Push(Toolset_Tokenizer::makeToken(Toolset_Functions::Contains($objOp1->val, $objOp2->val), Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { $myStack->Push(Toolset_Tokenizer::makeToken(!Toolset_Functions::Contains($objOp1->val, $objOp2->val), Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } break; } else { if (($strTok->val == '=' || $strTok->val == '<>' || $strTok->val == 'eq' || $strTok->val == 'ne') && ($objOp2->isArray && ($objOp1->isStringLiteral || $objOp1->isNumber))) { if ($strTok->val == '=' || $strTok->val == 'eq') { $myStack->Push(Toolset_Tokenizer::makeToken(Toolset_Functions::Contains($objOp2->val, $objOp1->val), Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { $myStack->Push(Toolset_Tokenizer::makeToken(!Toolset_Functions::Contains($objOp2->val, $objOp1->val), Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } break; } else { throw new Exception("For " . $strTok->val . " operator LHS & RHS should be of same data type!"); } } } } } } } } } } } } if ($strTok->val == "=" || $strTok->val == "eq") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 == $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { if ($strTok->val == "<>" || $strTok->val == "ne") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 != $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { if ($strTok->val == ">" || $strTok->val == "gt") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 > $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { if ($strTok->val == "<" || $strTok->val == "lt") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 < $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { if ($strTok->val == "<=" || $strTok->val == "lte") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 <= $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { if ($strTok->val == ">=" || $strTok->val == "gte") { $myStack->Push(Toolset_Tokenizer::makeToken($dblVal1 >= $dblVal2, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } } } } } } break; } break; case Toolset_Tokenizer::$TOKEN_TYPE['LOGICAL_OP']: switch ($strTok->val) { case 'NOT': case '!': if ($myStack->IsEmpty()) { throw new Exception("No operand on stack!"); } $objOp1 = null; $objOp2 = null; $objOp1 = $myStack->Pop(); if ($objOp1->isVariable) { $objOp1 = $this->_getVariable($objOp1->val, $myvarArr); } $objOp1 = Toolset_Tokenizer::toBoolean($objOp1->val); if ($objOp1 === null) { throw new Exception($strTok->val . " applied not on a boolean value!"); } else { $myStack->Push(Toolset_Tokenizer::makeToken(!$objOp1, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } break; case "AND": case "&": case "OR": case "|": if ($myStack->IsEmpty() || $myStack->Size() < 2) { throw new Exception("Stack is empty, can not perform [" . $strTok->val . "]"); } $objOp1 = null; $objOp2 = null; $objTmp1 = null; $objTmp2 = null; $objOp2 = $myStack->Pop(); $objOp1 = $myStack->Pop(); if ($objOp1->isVariable) { $objOp1 = $this->_getVariable($objOp1->val, $myvarArr); } if ($objOp2->isVariable) { $objOp2 = $this->_getVariable($objOp2->val, $myvarArr); } if ($objOp1->isBoolean && $objOp2->isBoolean || $objOp1->isNumber && $objOp2->isNumber || $objOp1->isNumber && $objOp2->isBoolean || $objOp1->isBoolean && $objOp2->isNumber) { $objTmp1 = Toolset_Tokenizer::toBoolean($objOp1->val); $objTmp2 = Toolset_Tokenizer::toBoolean($objOp2->val); if ($strTok->val == "AND" || $strTok->val == "&") { $myStack->Push(Toolset_Tokenizer::makeToken($objTmp1 && $objTmp2, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } else { if ($strTok->val == "OR" || $strTok->val == "|") { $myStack->Push(Toolset_Tokenizer::makeToken($objTmp1 || $objTmp2, Toolset_Tokenizer::$TOKEN_TYPE['BOOLEAN'])); } } } else { throw new Exception("Logical operator requires LHS & RHS of boolean type!"); } break; } break; case Toolset_Tokenizer::$TOKEN_TYPE['FUNCTION']: $this->_HandleFunctions($strTok, $myStack, $this->dtFormat, $myvarArr); break; default: $myStack->Push($strTok); break; } $intIndex++; //echo (string)$myStack->toString(); } if ($myStack->IsEmpty() || $myStack->Size() > 1 || $myStack->Get(0)->isVariable) { throw new Exception("Unable to evaluate expression!"); } else { return $myStack->Pop()->val; } }
/** * Uses the Toolset_Tokenizer to break down the expression, replace problematic operators by their text-only * equivalents and glue the expression back together. * * Side-effects: Loses custom whitespace characters. All operators (except parentheses) will be surrounded by spaces * while everywhere else the whitespace characters will be trimmed. * * Note: If an invalid expression is provided, it doesn't do anything with it. * * @param string $expression Condition expression. * @return string Equivalent expression but without <, <=, etc. * @since 2.0 */ protected function transform_operators_to_text_equivalents($expression) { try { // The expression may come directly from parse_str() which may add backslashes to quotes. The tokenizer // wouldn't survive that. $expression = stripslashes($expression); $toolset_bootstrap = Toolset_Common_Bootstrap::getInstance(); $toolset_bootstrap->register_parser(); $tokenizer = new Toolset_Tokenizer(); $tokens = $tokenizer->Tokanize($expression); $token_value_replacements = array('<' => 'lt', '>' => 'gt', '<=' => 'lte', '>=' => 'gte', '<>' => 'ne', '=' => 'eq'); $result = ''; foreach ($tokens as $token) { if ($token->isCompOp) { $token->val = wpcf_getarr($token_value_replacements, $token->val, $token->val); } if ($token->isCompOp || $token->isArithmeticOp || $token->isLogicOp) { $result .= ' ' . $token->val . ' '; } else { if ($token->isStringLiteral) { $result .= '\'' . $token->val . '\''; } else { $result .= $token->val; } } } return $result; } catch (Exception $e) { // Most probably we were unable to tokenize the expression. We give up. return $expression; } }