Browse Source

Fix trace/context for errors wrapped by debug kit

Add a magic `_trace_offset` context value that allows error handling
wrappers to offset the stack trace that is displayed. This is not
a great solution, but given how PHP error handling works and our
backwards compatibility requirements this is what I could come up with.

Port changes from #14090 to 4.x
Mark Story 6 years ago
parent
commit
0228bc73b6
2 changed files with 43 additions and 1 deletions
  1. 11 1
      src/Error/BaseErrorHandler.php
  2. 32 0
      tests/TestCase/Error/ErrorHandlerTest.php

+ 11 - 1
src/Error/BaseErrorHandler.php

@@ -168,9 +168,19 @@ abstract class BaseErrorHandler
 
         $debug = Configure::read('debug');
         if ($debug) {
+            // By default trim 3 frames off for the public and protected methods
+            // used by ErrorHandler instances.
+            $start = 3;
+
+            // Can be used by error handlers that wrap other error handlers
+            // to coerce the generated stack trace to the correct point.
+            if (isset($context['_trace_frame_offset'])) {
+                $start += $context['_trace_frame_offset'];
+                unset($context['_trace_frame_offset']);
+            }
             $data += [
                 'context' => $context,
-                'start' => 3,
+                'start' => $start,
                 'path' => Debugger::trimPath((string)$file),
             ];
         }

+ 32 - 0
tests/TestCase/Error/ErrorHandlerTest.php

@@ -131,6 +131,38 @@ class ErrorHandlerTest extends TestCase
         $this->assertRegExp('/<pre class="cake-error">/', $result);
         $this->assertRegExp('/<b>Notice<\/b>/', $result);
         $this->assertRegExp('/variable:\s+wrong/', $result);
+        $this->assertStringContainsString(
+            'ErrorHandlerTest.php, line ' . (__LINE__ - 7),
+            $result,
+            'Should contain file and line reference'
+        );
+    }
+
+    /**
+     * test error handling with the _trace_offset context variable
+     *
+     * @return void
+     */
+    public function testHandleErrorTraceOffset()
+    {
+        $this->_restoreError = true;
+
+        set_error_handler(function ($code, $message, $file, $line, $context = null) {
+            $errorHandler = new ErrorHandler();
+            $context['_trace_frame_offset'] = 3;
+            $errorHandler->handleError($code, $message, $file, $line, $context);
+        });
+
+        ob_start();
+        $wrong = $wrong + 1;
+        $result = ob_get_clean();
+
+        $this->assertStringNotContainsString(
+            'ErrorHandlerTest.php, line ' . (__LINE__ - 4),
+            $result,
+            'Should not contain file and line reference'
+        );
+        $this->assertStringNotContainsString('_trace_frame_offset', $result);
     }
 
     /**