diff --git a/lib/evalmath/evalmath.class.php b/lib/evalmath/evalmath.class.php
index 54bba6b3c62..8e3f3818b46 100644
--- a/lib/evalmath/evalmath.class.php
+++ b/lib/evalmath/evalmath.class.php
@@ -210,14 +210,14 @@ class EvalMath {
$output = array(); // postfix form of expression, to be passed to pfx()
$expr = trim(strtolower($expr));
// MDL-14274: new operators for comparison added.
- $ops = array('+', '-', '*', '/', '^', '_', '>', '<', '<=', '>=', '==');
- $ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>1); // right-associative operator?
- $ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2, '>'=>3, '<'=>3, '<='=>3, '>='=>3, '=='=>3); // operator precedence
+ $ops = array('+', '-', '*', '/', '^', '_', '>', '<', '<=', '>=', '==', '%');
+ $ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>1, '%' => 0); // right-associative operator?
+ $ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2, '>'=>3, '<'=>3, '<='=>3, '>='=>3, '=='=>3, '%'=>1); // operator precedence
$expecting_op = false; // we use this in syntax-checking the expression
// and determining when a - is a negation
- if (preg_match("/[^\w\s+*^\/()\.,-<>=]/", $expr, $matches)) { // make sure the characters are all good
+ if (preg_match("/[^\%\w\s+*^\/()\.,-<>=]/", $expr, $matches)) { // make sure the characters are all good
return $this->trigger(get_string('illegalcharactergeneral', 'mathslib', $matches[0]));
}
@@ -433,7 +433,7 @@ class EvalMath {
$stack->push($this->pfx($this->f[$fnn]['func'], $args)); // yay... recursion!!!!
}
// if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on
- } elseif (in_array($token, array('+', '-', '*', '/', '^', '>', '<', '==', '<=', '>='), true)) {
+ } elseif (in_array($token, array('+', '-', '*', '/', '^', '>', '<', '==', '<=', '>=', '%'), true)) {
if (is_null($op2 = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib'));
if (is_null($op1 = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib'));
switch ($token) {
@@ -458,6 +458,8 @@ class EvalMath {
$stack->push((int)($op1 <= $op2)); break;
case '>=':
$stack->push((int)($op1 >= $op2)); break;
+ case '%':
+ $stack->push($op1%$op2); break;
}
// if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
} elseif ($token == "_") {
diff --git a/lib/evalmath/readme_moodle.txt b/lib/evalmath/readme_moodle.txt
index 3d460eb9986..948c9c39a20 100644
--- a/lib/evalmath/readme_moodle.txt
+++ b/lib/evalmath/readme_moodle.txt
@@ -32,4 +32,21 @@ e.g. if (and(condition_1, condition_2, ... condition_n))
Changes by Raquel Ortega (MDL-76413)
* Avoid PHP 8.2: Partially-supported callable deprecations
-* eg: call_user_func_array(array('self', 'sum'), $args to call_user_func_array(array(self::class, 'sum'), $args)
\ No newline at end of file
+* eg: call_user_func_array(array('self', 'sum'), $args to call_user_func_array(array(self::class, 'sum'), $args)
+
+Changes by Meirza (MDL-75464)
+* EvalMath has unit tests in lib/tests/mathslib_test.php,
+ since version 1.0.1, there are two additional tests:
+ - shouldSupportModuloOperator()
+ - shouldConsiderDoubleMinusAsPlus()
+ To pass the test, some modifications must be made:
+ - Adjust the test code so it can run properly by using \calc_formula.
+ Please see the differences between the code in MDL-75464 and the upstream code.
+ - In the dataprovider for both tests, add the formula in the first array with "=" at the first character.
+ Before:
+ ```
+ 'a%b', // 9%3 => 0
+ ```
+ After:
+ ```
+ '=a%b', // 9%3 => 0
\ No newline at end of file
diff --git a/lib/tests/mathslib_test.php b/lib/tests/mathslib_test.php
index 8e8368add18..5994e2fffdf 100644
--- a/lib/tests/mathslib_test.php
+++ b/lib/tests/mathslib_test.php
@@ -317,4 +317,93 @@ class mathslib_test extends \basic_testcase {
$result = $formula->evaluate();
$this->assertTrue(is_float($result));
}
+
+ /**
+ * Tests the modulo operator.
+ *
+ * @covers calc_formula::evaluate
+ * @dataProvider moduloOperatorData
+ *
+ * @param string $formula
+ * @param array $values
+ * @param int|float $expectedResult
+ */
+ public function shouldSupportModuloOperator($formula, $values, $expectedResult)
+ {
+ $formula = new calc_formula($formula);
+ $formula->set_params($values);
+ $this->assertEquals($expectedResult, $formula->evaluate());
+ }
+
+ /**
+ * Data provider for shouldSupportModuloOperator
+ *
+ * @return array
+ */
+ public function moduloOperatorData() {
+ return array(
+ array(
+ '=a%b', // 9%3 => 0
+ array('a' => 9, 'b' => 3),
+ 0
+ ),
+ array(
+ '=a%b', // 10%3 => 1
+ array('a' => 10, 'b' => 3),
+ 1
+ ),
+ array(
+ '=10-a%(b+c*d)', // 10-10%(7-2*2) => 9
+ array('a' => '10', 'b' => 7, 'c' => -2, 'd' => 2),
+ 9
+ )
+ );
+ }
+
+ /**
+ * Tests the double minus as plus.
+ *
+ * @covers calc_formula::evaluate
+ * @dataProvider doubleMinusData
+ *
+ * @param string $formula
+ * @param array $values
+ * @param int|float $expectedResult
+ */
+ public function shouldConsiderDoubleMinusAsPlus($formula, $values, $expectedResult)
+ {
+ $formula = new calc_formula($formula);
+ $formula->set_params($values);
+ $this->assertEquals($expectedResult, $formula->evaluate());
+ }
+
+ /**
+ * Data provider for shouldConsiderDoubleMinusAsPlus
+ *
+ * @return array
+ */
+ public function doubleMinusData() {
+ return array(
+ array(
+ '=a+b*c--d', // 1+2*3--4 => 1+6+4 => 11
+ array(
+ 'a' => 1,
+ 'b' => 2,
+ 'c' => 3,
+ 'd' => 4
+ ),
+ 11
+ ),
+ array(
+ '=a+b*c--d', // 1+2*3---4 => 1+6-4 => 3
+ array(
+ 'a' => 1,
+ 'b' => 2,
+ 'c' => 3,
+ 'd' => -4
+ ),
+ 3
+ )
+ );
+ }
}
diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml
index c6aca5f4757..6191e21b044 100644
--- a/lib/thirdpartylibs.xml
+++ b/lib/thirdpartylibs.xml
@@ -42,7 +42,7 @@
evalmath
EvalMath
Class to safely evaluate math expressions.
- 1.0.0
+ 1.0.1
BSD
https://github.com/dbojdo/eval-math