From 39ce6b9ac26ac265ab404aea7b87fddefeff68d0 Mon Sep 17 00:00:00 2001 From: Grass Huang Date: Tue, 10 Dec 2024 09:45:41 +0800 Subject: [PATCH 1/4] #39415 - Fixed an issue where the ExceptionHandler did not display the correct file and line number of the thrown exception. --- .../Framework/App/ExceptionHandler.php | 29 ++++++++- lib/internal/Magento/Framework/Debug.php | 64 +++++++++++++++---- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/lib/internal/Magento/Framework/App/ExceptionHandler.php b/lib/internal/Magento/Framework/App/ExceptionHandler.php index d1bd09da256af..1564f49d172e9 100644 --- a/lib/internal/Magento/Framework/App/ExceptionHandler.php +++ b/lib/internal/Magento/Framework/App/ExceptionHandler.php @@ -107,6 +107,27 @@ private function handleDeveloperMode( return false; } + /** + * Retrieves the location of an exception as a formatted string. + * + * This method extracts the file path and line number from the given exception + * and formats them into a string representing the exception's location. + * The format of the returned string is determined by the `normalizeCodeLocation` method. + * + * Example Output: + * "app/code/Vendor/Module/SomeClass.php:123" + * + * @param \Exception $exception The exception object from which to extract the file and line information. + * @return string A formatted string representing the exception's file path and line number. + */ + private function getExceptionLocation(\Exception $exception): string + { + return Debug::normalizeCodeLocation([ + 'file' => $exception->getFile(), + 'line' => $exception->getLine() + ]); + } + /** * Build content based on an exception * @@ -126,19 +147,21 @@ private function buildContentFromException(\Exception $exception): string foreach ($exceptions as $index => $exception) { $buffer .= sprintf( - "Exception #%d (%s): %s\n", + "Exception #%d (%s): %s thrown at [%s]\n", $index, get_class($exception), - $exception->getMessage() + $exception->getMessage(), + $this->getExceptionLocation($exception) ); } foreach ($exceptions as $index => $exception) { $buffer .= sprintf( - "\nException #%d (%s): %s\n%s\n", + "\nException #%d (%s): %s thrown at [%s]\n%s\n", $index, get_class($exception), $exception->getMessage(), + $this->getExceptionLocation($exception), Debug::trace( $exception->getTrace(), true, diff --git a/lib/internal/Magento/Framework/Debug.php b/lib/internal/Magento/Framework/Debug.php index 6e5222a19884c..e846690cd9e9d 100644 --- a/lib/internal/Magento/Framework/Debug.php +++ b/lib/internal/Magento/Framework/Debug.php @@ -39,6 +39,52 @@ public static function getRootPath() return self::$_filePath; } + /** + * Formats the code location by generating a relative file path and line number. + * + * This method processes an input array that contains file path and line number information. + * It removes the root path from the file path (if present) to create a relative path + * and formats the result as a string in the format "relative/path/to/file.php:line". + * + * Example Input: + * [ + * 'file' => '/var/www/magento2/app/code/Magento/Test/Block/Test.php', + * 'line' => 1 + * ] + * + * Example Output: + * "app/code/Magento/Test/Block/Test.php:1" + * + * @param array|null $data An associative array containing: + * - 'file': The absolute file path (string). + * - 'line': The line number (int, optional). + * @return string A formatted string representing the relative file path and line number, + * or an empty string if the 'file' key is not present. + */ + public static function normalizeCodeLocation(?array $data): string + { + $fileName = ''; + + // Check if 'file' exists in the data array + if (isset($data['file'])) { + // Determine the position of the root path in the file path + $pos = \strpos($data['file'], self::getRootPath()); + + // If Magento root path is part of the file path, trim it to create a relative path + if (false !== $pos) { + $data['file'] = \substr( + $data['file'], + \strlen(self::getRootPath()) + 1 + ); + } + + // Format the file path and line number into the desired string format + $fileName = \sprintf('%s:%d', $data['file'], $data['line'] ?? 0); + } + + return $fileName; + } + /** * Prints or returns a backtrace * @@ -56,10 +102,10 @@ public static function backtrace($return = false, $html = true, $withArgs = true /** * Prints or return a trace * - * @param array $trace trace array - * @param bool $return return or print - * @param bool $html output in HTML format - * @param bool $withArgs add short arguments of methods + * @param array $trace trace array + * @param bool $return return or print + * @param bool $html output in HTML format + * @param bool $withArgs add short arguments of methods * @return string|bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -107,15 +153,7 @@ public static function trace(array $trace, $return = false, $html = true, $withA $methodName = sprintf('%s(%s)', $data['function'], join(', ', $args)); } - if (isset($data['file'])) { - $pos = strpos($data['file'], self::getRootPath()); - if ($pos !== false) { - $data['file'] = substr($data['file'], strlen(self::getRootPath()) + 1); - } - $fileName = sprintf('%s:%d', $data['file'], $data['line']); - } else { - $fileName = false; - } + $fileName = self::normalizeCodeLocation($data); if ($fileName) { $out .= sprintf('#%d %s called at [%s]', $i, $methodName, $fileName); From 79d2359b6a9b6d93107d71bc2b476bfc57d6ff77 Mon Sep 17 00:00:00 2001 From: Grass Huang Date: Tue, 10 Dec 2024 10:02:35 +0800 Subject: [PATCH 2/4] Added double quotes to the message --- lib/internal/Magento/Framework/App/ExceptionHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/App/ExceptionHandler.php b/lib/internal/Magento/Framework/App/ExceptionHandler.php index 1564f49d172e9..d1929e14b69e7 100644 --- a/lib/internal/Magento/Framework/App/ExceptionHandler.php +++ b/lib/internal/Magento/Framework/App/ExceptionHandler.php @@ -147,7 +147,7 @@ private function buildContentFromException(\Exception $exception): string foreach ($exceptions as $index => $exception) { $buffer .= sprintf( - "Exception #%d (%s): %s thrown at [%s]\n", + "Exception #%d (%s): \"%s\" thrown at [%s]\n", $index, get_class($exception), $exception->getMessage(), @@ -157,7 +157,7 @@ private function buildContentFromException(\Exception $exception): string foreach ($exceptions as $index => $exception) { $buffer .= sprintf( - "\nException #%d (%s): %s thrown at [%s]\n%s\n", + "\nException #%d (%s): \"%s\" thrown at [%s]\n%s\n", $index, get_class($exception), $exception->getMessage(), From 911d50f1ea8972adae12bceac792916a607507bd Mon Sep 17 00:00:00 2001 From: Grass Huang Date: Tue, 10 Dec 2024 14:08:54 +0800 Subject: [PATCH 3/4] Fix static failure related to copyright tag --- lib/internal/Magento/Framework/App/ExceptionHandler.php | 5 +++-- lib/internal/Magento/Framework/Debug.php | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/App/ExceptionHandler.php b/lib/internal/Magento/Framework/App/ExceptionHandler.php index d1929e14b69e7..120f9c415c22f 100644 --- a/lib/internal/Magento/Framework/App/ExceptionHandler.php +++ b/lib/internal/Magento/Framework/App/ExceptionHandler.php @@ -1,8 +1,9 @@ Date: Wed, 11 Dec 2024 09:09:47 +0800 Subject: [PATCH 4/4] Fix static failure related to direct output --- lib/internal/Magento/Framework/Debug.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/internal/Magento/Framework/Debug.php b/lib/internal/Magento/Framework/Debug.php index 3fec7157e45b0..067a77598a863 100644 --- a/lib/internal/Magento/Framework/Debug.php +++ b/lib/internal/Magento/Framework/Debug.php @@ -175,6 +175,8 @@ public static function trace(array $trace, $return = false, $html = true, $withA if ($return) { return $out; } else { + // Fix static test: 'Use of echo language construct is discouraged.' + // phpcs:ignore Magento2.Security.LanguageConstruct.DirectOutput echo $out; return true; }