From 71c1fa0d8ea42fadbca4259b8b6df12454d40a21 Mon Sep 17 00:00:00 2001
From: Andrew Nicols <andrew@nicols.co.uk>
Date: Fri, 6 Jan 2023 22:26:45 +0800
Subject: [PATCH] MDL-76362 qtype_calculated*: Address issues with null strings

---
 question/type/calculated/db/upgradelib.php      | 15 +++++++++------
 question/type/calculatedmulti/db/upgradelib.php | 16 ++++++++++------
 2 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/question/type/calculated/db/upgradelib.php b/question/type/calculated/db/upgradelib.php
index c7695d89b9a..c01b8ae6344 100644
--- a/question/type/calculated/db/upgradelib.php
+++ b/question/type/calculated/db/upgradelib.php
@@ -27,22 +27,22 @@
 class qtype_calculated_qe2_attempt_updater extends question_qtype_attempt_updater {
     protected $selecteditem = null;
     /** @var array variable name => value */
-    protected $values;
+    protected $values = [];
 
     /** @var array variable names wrapped in {...}. Used by {@link substitute_values()}. */
-    protected $search;
+    protected $search = [];
 
     /**
      * @var array variable values, with negative numbers wrapped in (...).
      * Used by {@link substitute_values()}.
      */
-    protected $safevalue;
+    protected $safevalue = [];
 
     /**
      * @var array variable values, with negative numbers wrapped in (...).
      * Used by {@link substitute_values()}.
      */
-    protected $prettyvalue;
+    protected $prettyvalue = [];
 
     public function question_summary() {
         return ''; // Done later, after we know which dataset is used.
@@ -260,7 +260,7 @@ class qtype_calculated_qe2_attempt_updater extends question_qtype_attempt_update
      *      corresponding value.
      */
     protected function substitute_values_for_eval($expression) {
-        return str_replace($this->search ?? '', $this->safevalue ?? '', $expression ?? '');
+        return str_replace($this->search, $this->safevalue, $expression ?? '');
     }
 
     /**
@@ -272,7 +272,7 @@ class qtype_calculated_qe2_attempt_updater extends question_qtype_attempt_update
      *      corresponding value.
      */
     protected function substitute_values_pretty($text) {
-        return str_replace($this->search ?? '', $this->prettyvalue ?? '', $text ?? '');
+        return str_replace($this->search, $this->prettyvalue, $text ?? '');
     }
 
     /**
@@ -282,6 +282,9 @@ class qtype_calculated_qe2_attempt_updater extends question_qtype_attempt_update
      * @return string the text with values substituted.
      */
     public function replace_expressions_in_text($text, $length = null, $format = null) {
+        if ($text === null || $text === '') {
+            return $text;
+        }
         $vs = $this; // Can't see to use $this in a PHP closure.
         $text = preg_replace_callback(
             '~\{=([^{}]*(?:\{[^{}]+}[^{}]*)*)}~',
diff --git a/question/type/calculatedmulti/db/upgradelib.php b/question/type/calculatedmulti/db/upgradelib.php
index ca795687ed1..9beb0c82a0d 100644
--- a/question/type/calculatedmulti/db/upgradelib.php
+++ b/question/type/calculatedmulti/db/upgradelib.php
@@ -27,22 +27,22 @@
 class qtype_calculatedmulti_qe2_attempt_updater extends question_qtype_attempt_updater {
     protected $selecteditem = null;
     /** @var array variable name => value */
-    protected $values;
+    protected $values = [];
 
     /** @var array variable names wrapped in {...}. Used by {@link substitute_values()}. */
-    protected $search;
+    protected $search = [];
 
     /**
      * @var array variable values, with negative numbers wrapped in (...).
      * Used by {@link substitute_values()}.
      */
-    protected $safevalue;
+    protected $safevalue = [];
 
     /**
      * @var array variable values, with negative numbers wrapped in (...).
      * Used by {@link substitute_values()}.
      */
-    protected $prettyvalue;
+    protected $prettyvalue = [];
 
     protected $order;
 
@@ -284,7 +284,7 @@ class qtype_calculatedmulti_qe2_attempt_updater extends question_qtype_attempt_u
      *      corresponding value.
      */
     protected function substitute_values_for_eval($expression) {
-        return str_replace($this->search ?? '', $this->safevalue ?? '', $expression ?? '');
+        return str_replace($this->search, $this->safevalue, $expression ?? '');
     }
 
     /**
@@ -296,7 +296,7 @@ class qtype_calculatedmulti_qe2_attempt_updater extends question_qtype_attempt_u
      *      corresponding value.
      */
     protected function substitute_values_pretty($text) {
-        return str_replace($this->search ?? '', $this->prettyvalue ?? '', $text ?? '');
+        return str_replace($this->search, $this->prettyvalue, $text ?? '');
     }
 
     /**
@@ -306,6 +306,10 @@ class qtype_calculatedmulti_qe2_attempt_updater extends question_qtype_attempt_u
      * @return string the text with values substituted.
      */
     public function replace_expressions_in_text($text, $length = null, $format = null) {
+        if ($text === null || $text === '') {
+            return $text;
+        }
+
         $vs = $this; // Can't see to use $this in a PHP closure.
         $text = preg_replace_callback(
             qtype_calculated::FORMULAS_IN_TEXT_REGEX,