From c1e07f254a4e952868be8240080661628aef8e69 Mon Sep 17 00:00:00 2001
From: Javier MarĂ­n <javier@marinros.com>
Date: Sun, 26 Jul 2020 04:27:26 +0200
Subject: Handler for not found variables (#68)

* Added handler to define not found variables
Added support for string variables
Fixed strings and ints comparison error

* Check if variables have scalar types (int, float, string and bool)
Better $onVarNotFound logic
---
 src/NXP/Classes/Calculator.php              | 14 +++++++--
 src/NXP/Exception/MathExecutorException.php |  2 +-
 src/NXP/MathExecutor.php                    | 48 +++++++++++++++++++++++------
 3 files changed, 50 insertions(+), 14 deletions(-)

(limited to 'src/NXP')

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 @@ class Calculator
      * @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 @@ class Calculator
                 $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 @@ namespace NXP\Exception;
 /**
  * @author Vitaliy Zhuk <zhuk2205@gmail.com>
  */
-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\Calculator;
 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 @@ class MathExecutor
             ],
             '==' => [
                 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 @@ class MathExecutor
             $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 @@ class MathExecutor
      * @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 @@ class MathExecutor
         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 @@ class MathExecutor
     }
 
     /**
-     * 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 @@ class MathExecutor
      */
     public function getOperators()
     {
-        return $this->tokenFactory->getOperators();
+        return $this->operators;
     }
 
     /**
@@ -485,7 +513,7 @@ class MathExecutor
      */
     public function getFunctions() : array
     {
-        return $this->tokenFactory->getFunctions();
+        return $this->functions;
     }
 
     /**
-- 
cgit v1.2.3