diff --git a/src/NXP/Classes/Calculator.php b/src/NXP/Classes/Calculator.php index bcb4b4e..49142df 100644 --- a/src/NXP/Classes/Calculator.php +++ b/src/NXP/Classes/Calculator.php @@ -49,7 +49,7 @@ public function __construct(array $functions, array $operators) * @throws IncorrectExpressionException * @throws UnknownVariableException */ - public function calculate(array $tokens, array $variables) + public function calculate(array $tokens, array $variables, callable $onVarNotFound = null) { /** @var Token[] $stack */ $stack = []; @@ -58,10 +58,18 @@ public function calculate(array $tokens, array $variables) $stack[] = $token; } elseif ($token->type === Token::Variable) { $variable = $token->value; - if (!array_key_exists($variable, $variables)) { + + $value = null; + if (array_key_exists($variable, $variables)) { + $value = $variables[$variable]; + } elseif ($onVarNotFound) { + $value = call_user_func($onVarNotFound, $variable); + } + + if (!isset($value)) { throw new UnknownVariableException($variable); } - $value = $variables[$variable]; + $stack[] = new Token(Token::Literal, $value); } elseif ($token->type === Token::Function) { if (!array_key_exists($token->value, $this->functions)) { diff --git a/src/NXP/Exception/MathExecutorException.php b/src/NXP/Exception/MathExecutorException.php index 0e3ea84..4405f85 100644 --- a/src/NXP/Exception/MathExecutorException.php +++ b/src/NXP/Exception/MathExecutorException.php @@ -14,6 +14,6 @@ /** * @author Vitaliy Zhuk */ -abstract class MathExecutorException extends \Exception +class MathExecutorException extends \Exception { } diff --git a/src/NXP/MathExecutor.php b/src/NXP/MathExecutor.php index 8f876dc..d23ba80 100644 --- a/src/NXP/MathExecutor.php +++ b/src/NXP/MathExecutor.php @@ -15,9 +15,9 @@ use NXP\Classes\CustomFunction; use NXP\Classes\Operator; use NXP\Classes\Tokenizer; -use NXP\Exception\MathExecutorException; use NXP\Exception\DivisionByZeroException; use NXP\Exception\UnknownVariableException; +use NXP\Exception\MathExecutorException; use ReflectionException; /** @@ -33,6 +33,11 @@ class MathExecutor */ private $variables = []; + /** + * @var callable + */ + private $onVarNotFound = null; + /** * @var Operator[] */ @@ -134,14 +139,22 @@ function ($a, $b) { ], '==' => [ function ($a, $b) { - return $a == $b; + if (is_string($a) || is_string($b)) { + return strcmp($a, $b) == 0; + } else { + return $a == $b; + } }, 140, false ], '!=' => [ function ($a, $b) { - return $a != $b; + if (is_string($a) || is_string($b)) { + return strcmp($a, $b) != 0; + } else { + return $a != $b; + } }, 140, false @@ -353,7 +366,7 @@ public function execute(string $expression) $tokens = $this->cache[$cachekey]; } $calculator = new Calculator($this->functions, $this->operators); - return $calculator->calculate($tokens, $this->variables); + return $calculator->calculate($tokens, $this->variables, $this->onVarNotFound); } /** @@ -415,13 +428,13 @@ public function getVar(string $variable) * @param string $variable * @param integer|float $value * @return MathExecutor - * @throws MathExecutorException */ public function setVar(string $variable, $value) : self { - if (!is_numeric($value)) { - throw new MathExecutorException("Variable ({$variable}) value must be a number ({$value}) type ({gettype($value)})"); + if (!is_scalar($value)) { + throw new MathExecutorException("Variable ({$variable}) value must be a scalar type ({gettype($value)})"); } + $this->variables[$variable] = $value; return $this; } @@ -445,6 +458,20 @@ public function setVars(array $variables, bool $clear = true) : self return $this; } + /** + * Define a method that will be invoked when a variable is not found. + * The first parameter will be the variable name, and the returned value will be used as the variable value. + * + * @param callable $handler + * + * @return MathExecutor + */ + public function setVarNotFoundHandler(callable $handler): self + { + $this->onVarNotFound = $handler; + return $this; + } + /** * Remove variable from executor * @@ -458,12 +485,13 @@ public function removeVar(string $variable) : self } /** - * Remove all variables + * Remove all variables and the variable not found handler * @return MathExecutor */ public function removeVars() : self { $this->variables = []; + $this->onVarNotFound = null; return $this; } @@ -474,7 +502,7 @@ public function removeVars() : self */ public function getOperators() { - return $this->tokenFactory->getOperators(); + return $this->operators; } /** @@ -485,7 +513,7 @@ public function getOperators() */ public function getFunctions() : array { - return $this->tokenFactory->getFunctions(); + return $this->functions; } /** diff --git a/tests/MathTest.php b/tests/MathTest.php index ee9c6bd..d76a57c 100644 --- a/tests/MathTest.php +++ b/tests/MathTest.php @@ -409,6 +409,37 @@ public function testBeginWithBracketAndMinus() $this->assertEquals(1, $calculator->execute('(-4 + 5)')); } + public function testStringComparison() + { + $calculator = new MathExecutor(); + $this->assertEquals(true, $calculator->execute('"a" == \'a\'')); + $this->assertEquals(true, $calculator->execute('"hello world" == "hello world"')); + $this->assertEquals(false, $calculator->execute('"hello world" == "hola mundo"')); + $this->assertEquals(true, $calculator->execute('"hello world" != "hola mundo"')); + } + + public function testVarStringComparison() + { + $calculator = new MathExecutor(); + $calculator->setVar('var', 0); + $this->assertEquals($calculator->execute('0 == "a"'), $calculator->execute('var == "a"')); + } + + public function testOnVarNotFound() + { + $calculator = new MathExecutor(); + $calculator->setVarNotFoundHandler( + function ($varName) { + if ($varName == 'undefined') { + return 3; + } else { + return null; + } + } + ); + $this->assertEquals(15, $calculator->execute('5 * undefined')); + } + public function testMinusZero() { $calculator = new MathExecutor();