I switched from PHP 7.2.26 to PHP 7.3.13 and that broke my WordPress blog. The problem was Aram Kocharyan’s Crayon Syntax Highlighter plugin. Version 2.8.4 has not been updated for 4 years.
PCRE to PCRE2
The transition from PHP 7.2.x to PHP 7.3.x upgraded the PCRE (Perl Compatible Regular Expressions) extension to PCRE2. User ‘baseapp’ identified the problem was in function clean_id
of class CrayonLang
:
1 2 3 4 5 6 7 |
// Override function clean_id($id) { $id = CrayonUtil::space_to_hyphen( strtolower(trim($id)) ); // Was: return preg_replace('/[^\w-+#]/msi', '', $id); // See: https://wordpress.org/support/topic/plugin-breaks-with-php-7-3/ return preg_replace("/[^\w\-+#]/msi", '', $id); } |
The preg_replace
function performs a regular expression search and replace. The fix changes the pattern from /[^\w-+#]/msi
to /[^\w\-+#]/msi
. In particular, the character class changes from the negation (^
) of \w-+#
to the negation of \w\-+#
. The hyphen (-
) is now escaped (\-
); otherwise it would be interpreted as a metacharacter indicating a character range.
I identified a similar problem with a line of the YAML language definition:
1 2 |
# LIST_KEY:STRING ((\-\s*)?[\w-\\]+(?=\s*:))|(\-\s*(?=\{)) LIST_KEY:STRING ((\-\s*)?[\w\-\\]+(?=\s*:))|(\-\s*(?=\{)) |
preg_quote
From PHP 7.3.0, the #
character is quoted by function preg_quote
. That affects the logic of the lines
function of the CrayonUtil
class:
1 2 3 4 5 6 7 8 9 |
if ($escape_regex) { for ($i = 0; $i < count($lines); $i++) { $lines[$i] = self::esc_regex($lines[$i]); // Was: Before PHP 7.3.0: If we have used \#, then we don't want it to become \\# // Was: $lines[$i] = preg_replace('|\\\\\\\\#|', '\#', $lines[$i]); // From PHP 7.3.0: If we have used \#, then we don't want it to become \\\# $lines[$i] = preg_replace('|\\\\\\\\\\\\#|', '\#', $lines[$i]); } } |
CrayonParser::validate_regex
In order to identify problems with invalid regular expressions, I extended part of the CrayonParser::validate_regex
function:
1 2 3 4 5 6 |
// Test if regex is valid if (@preg_match("#$regex#", '') === FALSE) { // Was: CrayonLog::syslog("The regex for the element '{$element->name()}' in '{$element->path()}' is not valid."); CrayonLog::syslog("The regex '{$regex}' for the element '{$element->name()}' in '{$element->path()}' is not valid."); return FALSE; } |