"\n", self::CARRIAGE_RETURN => "\r", self::HORIZONTAL_TAB => "\t", self::BACKSPACE => "\x08", self::FORM_FEED => "\x0C", self::LEFT_PARENTHESIS => "(", self::RIGHT_PARENTHESIS => ")", self::REVERSE_SOLIDUS => "\\", }; } /** @return array{0: list, 1: list} */ private static function getReplacementSet(): array { $find = $replace = []; foreach (self::cases() as $case) { $find[] = $case->value; $replace[] = $case->getActualCharacter(); } return [$find, $replace]; } public static function unescapeCharacters(string $string): string { $string = str_replace("\\\n", '', $string); // Example 2, 7.3.4.2 newlines preceded by reverse solidus should be handled like single lines [$find, $replace] = LiteralStringEscapeCharacter::getReplacementSet(); return preg_replace_callback( '/\\\\([0-7]{1,3})/', static function (array $matches) { $decimal = octdec($matches[1]); if (!is_int($decimal) || $decimal < 0 || $decimal > 255) { throw new ParseFailureException(sprintf('Invalid octal value "%s"', $matches[1])); } return mb_chr($decimal); }, str_replace($find, $replace, $string) ) ?? throw new ParseFailureException(); } }