diff --git a/src/qtism/data/storage/xml/Utils.php b/src/qtism/data/storage/xml/Utils.php index 347b7c619..c3620ae4b 100644 --- a/src/qtism/data/storage/xml/Utils.php +++ b/src/qtism/data/storage/xml/Utils.php @@ -360,29 +360,65 @@ public static function valueAsString($value, $encode = true): string return (string)$value; } - public static function escapeNonCharacterRange(string $string): string + private static function isInCharacterRange(int $char): bool { - // Define the XML escape sequences - $symbolMap = [ - '"' => '"', - "'" => ''', - '&' => '&', - '<' => '<', - '>' => '>', - "\t" => ' ', - "\r" => ' ', - "\x08" => '�', - ]; - - for ($char = 0xE000; $char <= 0xFFFD; $char++) { - $symbolMap[mb_chr($char, 'UTF-8')] = '\u{FFFD}'; - } - for ($char = 0x10000; $char <= 0x10FFFF; $char++) { - $symbolMap[mb_chr($char, 'UTF-8')] = '\u{FFFD}'; - } - $string = strtr($string, $symbolMap); + return $char == 0x09 + || $char == 0x0A + || $char == 0x0D + || $char >= 0x20 && $char <= 0xDF77 + || $char >= 0xE000 && $char <= 0xFFFD + || $char >= 0x10000 && $char <= 0x10FFFF; + } - return htmlspecialchars($string, ENT_QUOTES | ENT_XML1, 'UTF-8'); + public static function escapeNonCharacterRange(string $value): string + { + $result = ''; + + $last = 0; + $length = strlen($value); + $i = 0; + + while ($i < $length) { + $r = mb_substr(substr($value, $i), 0, 1); + $width = strlen($r); + $i += $width; + switch ($r) { + case '"': + $esc = '"'; + break; + case "'": + $esc = '''; + break; + case '&': + $esc = '&'; + break; + case '<': + $esc = '<'; + break; + case '>': + $esc = '>'; + break; + case "\t": + $esc = ' '; + break; + case "\n": + $esc = ' '; + break; + case "\r": + $esc = ' '; + break; + default: + if (!self::isInCharacterRange(mb_ord($r)) || (mb_ord($r) === 0xFFFD && $width === 1)) { + $esc = "\u{FFFD}"; + break; + } + + continue 2; + } + $result .= substr($value, $last, $i - $last - $width) . $esc; + $last = $i; + } + return $result . substr($value, $last); } /** diff --git a/test/qtismtest/data/storage/xml/XmlUtilsTest.php b/test/qtismtest/data/storage/xml/XmlUtilsTest.php index f79fa0bb2..917786e85 100644 --- a/test/qtismtest/data/storage/xml/XmlUtilsTest.php +++ b/test/qtismtest/data/storage/xml/XmlUtilsTest.php @@ -394,7 +394,7 @@ public function testFindCustomNamespaces(): void public function testValueAsStringReplaceSpecialSymbols(): void { $xml = ('160°'); - $this->assertEquals('&lt;value&gt;160°&#xFFFD;&lt;/value&gt;', Utils::valueAsString($xml)); + $this->assertEquals('<value>160°�</value>', Utils::valueAsString($xml)); } public function testProcessSpecialCharsetWithoutError(): void