Skip to content

Commit 155bc75

Browse files
timhuntdanpoltawski
authored andcommitted
MDL-46148 qtype_calculated: function to validate equations in text.
1 parent 29005a5 commit 155bc75

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

question/type/calculated/question.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ public function datasets_are_synchronised($category) {
278278
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
279279
*/
280280
class qtype_calculated_variable_substituter {
281+
281282
/** @var array variable name => value */
282283
protected $values;
283284

@@ -466,7 +467,7 @@ protected function substitute_values_pretty($text) {
466467
*/
467468
public function replace_expressions_in_text($text, $length = null, $format = null) {
468469
$vs = $this; // Can't use $this in a PHP closure.
469-
$text = preg_replace_callback('~\{=([^{}]*(?:\{[^{}]+}[^{}]*)*)}~',
470+
$text = preg_replace_callback(qtype_calculated::FORMULAS_IN_TEXT_REGEX,
470471
function ($matches) use ($vs, $format, $length) {
471472
return $vs->format_float($vs->calculate($matches[1]), $length, $format);
472473
}, $text);

question/type/calculated/questiontype.php

+26
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3838
*/
3939
class qtype_calculated extends question_type {
40+
/** Regular expression that finds the formulas in content. */
41+
const FORMULAS_IN_TEXT_REGEX = '~\{=([^{}]*(?:\{[^{}]+}[^{}]*)*)\}~';
42+
4043
const MAX_DATASET_ITEMS = 100;
4144

4245
public $wizardpagesnumber = 3;
@@ -1984,3 +1987,26 @@ function qtype_calculated_find_formula_errors($formula) {
19841987
return false;
19851988
}
19861989
}
1990+
1991+
/**
1992+
* Validate all the forumulas in a bit of text.
1993+
* @param string $text the text in which to validate the formulas.
1994+
* @return string|boolean false if there are no problems. Otherwise a string error message.
1995+
*/
1996+
function qtype_calculated_find_formula_errors_in_text($text) {
1997+
preg_match_all(qtype_calculated::FORMULAS_IN_TEXT_REGEX, $text, $matches);
1998+
1999+
$errors = array();
2000+
foreach ($matches[1] as $match) {
2001+
$error = qtype_calculated_find_formula_errors($match);
2002+
if ($error) {
2003+
$errors[] = $error;
2004+
}
2005+
}
2006+
2007+
if ($errors) {
2008+
return implode(' ', $errors);
2009+
}
2010+
2011+
return false;
2012+
}

question/type/calculated/tests/formula_validation_test.php

+19
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,23 @@ public function test_functions_with_wrong_num_args_caught() {
8383
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('atan2(1.0, 1.0, 2.0)'));
8484
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('max(1.0)'));
8585
}
86+
87+
public function test_validation_of_formulas_in_text_ok() {
88+
$this->assertFalse(qtype_calculated_find_formula_errors_in_text(
89+
'<p>Look no equations.</p>'));
90+
$this->assertFalse(qtype_calculated_find_formula_errors_in_text(
91+
'<p>Simple variable: {x}.</p>'));
92+
$this->assertFalse(qtype_calculated_find_formula_errors_in_text(
93+
'<p>This is an equation: {=1+1}, as is this: {={x}+{y}}.</p>' .
94+
'<p>Here is a more complex one: {=sin(2*pi()*{theta})}.</p>'));
95+
}
96+
97+
public function test_validation_of_formulas_in_text_bad_function() {
98+
$this->assert_nonempty_string(qtype_calculated_find_formula_errors_in_text(
99+
'<p>This is an equation: {=eval(1)}.</p>'));
100+
$this->assert_nonempty_string(qtype_calculated_find_formula_errors_in_text(
101+
'<p>Good: {=1+1}, bad: {=eval(1)}, good: {={x}+{y}}.</p>'));
102+
$this->assert_nonempty_string(qtype_calculated_find_formula_errors_in_text(
103+
'<p>Bad: {=eval(1)}, bad: {=system(1)}.</p>'));
104+
}
86105
}

0 commit comments

Comments
 (0)