From 3e3aa00a1b4e3f0984bdd60d2228aef31eecd019 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Thu, 23 Oct 2025 08:58:58 -0600 Subject: [PATCH] rework monolog shim for better compatibility Signed-off-by: Andy Miller --- .../Grav/Framework/Compat/Monolog/Utils.php | 302 +++++++++--------- 1 file changed, 153 insertions(+), 149 deletions(-) diff --git a/system/src/Grav/Framework/Compat/Monolog/Utils.php b/system/src/Grav/Framework/Compat/Monolog/Utils.php index 529ab06a7..c4a2a5550 100644 --- a/system/src/Grav/Framework/Compat/Monolog/Utils.php +++ b/system/src/Grav/Framework/Compat/Monolog/Utils.php @@ -7,177 +7,181 @@ * that Grav 1.7 can interoperate with code targeting Monolog 3. */ -namespace Monolog; +namespace Grav\Framework\Compat\Monolog; -class Utils -{ - public const DEFAULT_JSON_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; - - /** - * @internal - */ - public static function getClass($object) +if (!class_exists(\Monolog\Utils::class, false)) { + class Utils { - $class = \get_class($object); + public const DEFAULT_JSON_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; - return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class; - } + /** + * @internal + */ + public static function getClass($object) + { + $class = \get_class($object); - /** - * Makes sure if a relative path is passed in it is turned into an absolute path - * - * @param string $streamUrl stream URL or path without protocol - * - * @return string - */ - public static function canonicalizePath($streamUrl) - { - $prefix = ''; - if ('file://' === substr($streamUrl, 0, 7)) { - $streamUrl = substr($streamUrl, 7); - $prefix = 'file://'; + return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class; } - if (false !== strpos($streamUrl, '://')) { - return $streamUrl; - } + /** + * Makes sure if a relative path is passed in it is turned into an absolute path + * + * @param string $streamUrl stream URL or path without protocol + * + * @return string + */ + public static function canonicalizePath($streamUrl) + { + $prefix = ''; + if ('file://' === substr($streamUrl, 0, 7)) { + $streamUrl = substr($streamUrl, 7); + $prefix = 'file://'; + } + + if (false !== strpos($streamUrl, '://')) { + return $streamUrl; + } + + if (substr($streamUrl, 0, 1) === '/' || substr($streamUrl, 1, 1) === ':' || substr($streamUrl, 0, 2) === '\\\\') { + return $prefix.$streamUrl; + } + + $streamUrl = getcwd() . '/' . $streamUrl; - if (substr($streamUrl, 0, 1) === '/' || substr($streamUrl, 1, 1) === ':' || substr($streamUrl, 0, 2) === '\\\\') { return $prefix.$streamUrl; } - $streamUrl = getcwd() . '/' . $streamUrl; - - return $prefix.$streamUrl; - } - - /** - * Return the JSON representation of a value - * - * @param mixed $data - * @param int $encodeFlags - * @param bool $ignoreErrors - * @return string - */ - public static function jsonEncode($data, $encodeFlags = null, $ignoreErrors = false) - { - if (null === $encodeFlags) { - $encodeFlags = self::DEFAULT_JSON_FLAGS; - if (defined('JSON_PRESERVE_ZERO_FRACTION')) { - $encodeFlags |= JSON_PRESERVE_ZERO_FRACTION; + /** + * Return the JSON representation of a value + * + * @param mixed $data + * @param int $encodeFlags + * @param bool $ignoreErrors + * @return string + */ + public static function jsonEncode($data, $encodeFlags = null, $ignoreErrors = false) + { + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + if (defined('JSON_PRESERVE_ZERO_FRACTION')) { + $encodeFlags |= JSON_PRESERVE_ZERO_FRACTION; + } + if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) { + $encodeFlags |= JSON_INVALID_UTF8_SUBSTITUTE; + } + if (defined('JSON_PARTIAL_OUTPUT_ON_ERROR')) { + $encodeFlags |= JSON_PARTIAL_OUTPUT_ON_ERROR; + } } - if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) { - $encodeFlags |= JSON_INVALID_UTF8_SUBSTITUTE; - } - if (defined('JSON_PARTIAL_OUTPUT_ON_ERROR')) { - $encodeFlags |= JSON_PARTIAL_OUTPUT_ON_ERROR; - } - } - if ($ignoreErrors) { - $json = @json_encode($data, $encodeFlags); + if ($ignoreErrors) { + $json = @json_encode($data, $encodeFlags); + if (false === $json) { + return 'null'; + } + + return $json; + } + + $json = json_encode($data, $encodeFlags); if (false === $json) { - return 'null'; + $json = self::handleJsonError(json_last_error(), $data); } return $json; } - $json = json_encode($data, $encodeFlags); - if (false === $json) { - $json = self::handleJsonError(json_last_error(), $data); - } - - return $json; - } - - /** - * Handle a json_encode failure. - * - * @param int $code - * @param mixed $data - * @param int $encodeFlags - * @return string - */ - public static function handleJsonError($code, $data, $encodeFlags = null) - { - if ($code !== JSON_ERROR_UTF8) { - self::throwEncodeError($code, $data); - } - - if (is_string($data)) { - self::detectAndCleanUtf8($data); - } elseif (is_array($data)) { - array_walk_recursive($data, array('Monolog\Utils', 'detectAndCleanUtf8')); - } else { - self::throwEncodeError($code, $data); - } - - if (null === $encodeFlags) { - $encodeFlags = self::DEFAULT_JSON_FLAGS; - if (defined('JSON_PRESERVE_ZERO_FRACTION')) { - $encodeFlags |= JSON_PRESERVE_ZERO_FRACTION; + /** + * Handle a json_encode failure. + * + * @param int $code + * @param mixed $data + * @param int $encodeFlags + * @return string + */ + public static function handleJsonError($code, $data, $encodeFlags = null) + { + if ($code !== JSON_ERROR_UTF8) { + self::throwEncodeError($code, $data); } - if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) { - $encodeFlags |= JSON_INVALID_UTF8_SUBSTITUTE; + + if (is_string($data)) { + self::detectAndCleanUtf8($data); + } elseif (is_array($data)) { + array_walk_recursive($data, [self::class, 'detectAndCleanUtf8']); + } else { + self::throwEncodeError($code, $data); } - if (defined('JSON_PARTIAL_OUTPUT_ON_ERROR')) { - $encodeFlags |= JSON_PARTIAL_OUTPUT_ON_ERROR; + + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + if (defined('JSON_PRESERVE_ZERO_FRACTION')) { + $encodeFlags |= JSON_PRESERVE_ZERO_FRACTION; + } + if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) { + $encodeFlags |= JSON_INVALID_UTF8_SUBSTITUTE; + } + if (defined('JSON_PARTIAL_OUTPUT_ON_ERROR')) { + $encodeFlags |= JSON_PARTIAL_OUTPUT_ON_ERROR; + } + } + + $json = json_encode($data, $encodeFlags); + + if ($json === false) { + self::throwEncodeError(json_last_error(), $data); + } + + return $json; + } + + /** + * @param int $code + * @param mixed $data + * @throws \RuntimeException + */ + private static function throwEncodeError($code, $data) + { + switch ($code) { + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + } + + throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true)); + } + + /** + * @param mixed $data + */ + public static function detectAndCleanUtf8(&$data) + { + if (is_string($data) && !preg_match('//u', $data)) { + $data = preg_replace_callback( + '/[\x80-\xFF]+/', + static function ($m) { return utf8_encode($m[0]); }, + $data + ); + $data = str_replace( + ['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'], + ['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'], + $data + ); } } - - $json = json_encode($data, $encodeFlags); - - if ($json === false) { - self::throwEncodeError(json_last_error(), $data); - } - - return $json; } - /** - * @param int $code - * @param mixed $data - * @throws \RuntimeException - */ - private static function throwEncodeError($code, $data) - { - switch ($code) { - case JSON_ERROR_DEPTH: - $msg = 'Maximum stack depth exceeded'; - break; - case JSON_ERROR_STATE_MISMATCH: - $msg = 'Underflow or the modes mismatch'; - break; - case JSON_ERROR_CTRL_CHAR: - $msg = 'Unexpected control character found'; - break; - case JSON_ERROR_UTF8: - $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - default: - $msg = 'Unknown error'; - } - - throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true)); - } - - /** - * @param mixed $data - */ - public static function detectAndCleanUtf8(&$data) - { - if (is_string($data) && !preg_match('//u', $data)) { - $data = preg_replace_callback( - '/[\x80-\xFF]+/', - function ($m) { return utf8_encode($m[0]); }, - $data - ); - $data = str_replace( - array('¤', '¦', '¨', '´', '¸', '¼', '½', '¾'), - array('€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'), - $data - ); - } - } + class_alias(__NAMESPACE__ . '\Utils', \Monolog\Utils::class); }