@@ -239,10 +239,14 @@ private function parse_general_expression(?int $stopat = null): expression {
239239 $ nextvalue = $ nexttoken ->value ;
240240
241241 // If the current token is a PREFIX and the next one is an IDENTIFIER, we will consider
242- // that one as a FUNCTION. If the next token has already been classified as a function,
243- // there is nothing to do. Otherwise, the PREFIX is simply useless and we ignore it.
242+ // that one as a FUNCTION, unless it is not a known function name. If the PREFIX is not
243+ // followed by an identifier, we silently ignore it for maximum backwards compatibility, as
244+ // legacy versions used to remove backslashes from variable definitions.
244245 if ($ type === token::PREFIX ) {
245- if ($ nexttype === token::IDENTIFIER || $ nexttype === token::FUNCTION ) {
246+ if ($ nexttype === token::IDENTIFIER ) {
247+ if (!self ::is_valid_function_name ($ nextvalue )) {
248+ $ this ->die (get_string ('error_prefix ' , 'qtype_formulas ' ));
249+ }
246250 $ nexttype = ($ nexttoken ->type = token::FUNCTION );
247251 }
248252 }
@@ -264,7 +268,7 @@ private function parse_general_expression(?int $stopat = null): expression {
264268 // break existing questions.
265269 if ($ type === token::IDENTIFIER ) {
266270 $ isnotavariable = !$ this ->is_known_variable ($ currenttoken );
267- $ isknownfunction = array_key_exists ($ value, functions:: FUNCTIONS + evaluator:: PHPFUNCTIONS );
271+ $ isknownfunction = self :: is_valid_function_name ($ value );
268272 $ nextisparen = $ nexttype === token::OPENING_PAREN ;
269273 if ($ isnotavariable && $ isknownfunction && $ nextisparen ) {
270274 $ type = ($ currenttoken ->type = token::FUNCTION );
@@ -369,6 +373,16 @@ private function parse_general_expression(?int $stopat = null): expression {
369373 return new expression (shunting_yard::infix_to_rpn ($ expression ));
370374 }
371375
376+ /**
377+ * Check if a given name is a valid function name.
378+ *
379+ * @param string $identifier the name to check
380+ * @return bool
381+ */
382+ public static function is_valid_function_name (string $ identifier ): bool {
383+ return array_key_exists ($ identifier , functions::FUNCTIONS + evaluator::PHPFUNCTIONS );
384+ }
385+
372386 /**
373387 * Retrieve the parsed statements.
374388 *
0 commit comments