Skip to content

Commit 1c37172

Browse files
committed
Update deprecations panel to use new error handlers
Update deprecations panel to trap errors using the new events. Support for the old error handler is being dropped as it was awkward and didn't really work that well anymore.
1 parent ce564ba commit 1c37172

4 files changed

Lines changed: 64 additions & 56 deletions

File tree

composer.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
},
2525
"require": {
2626
"php": ">=7.2",
27-
"cakephp/cakephp": "^4.3.0",
27+
"cakephp/cakephp": "dev-4.next as 4.4.0",
2828
"cakephp/chronos": "^2.0",
2929
"composer/composer": "^1.3 | ^2.0",
3030
"jdorn/sql-formatter": "^1.2"
@@ -58,5 +58,10 @@
5858
"psalm": "psalm.phar --show-info=false",
5959
"psalm-setup": "cp composer.json composer.backup && composer require --dev psalm/phar:^4.10 && mv composer.backup composer.json"
6060
},
61-
"prefer-stable": true
61+
"prefer-stable": true,
62+
"config": {
63+
"allow-plugins": {
64+
"dealerdirect/phpcodesniffer-composer-installer": true
65+
}
66+
}
6267
}

src/Panel/DeprecationsPanel.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@ protected function _prepare()
5959
foreach ($errors as $error) {
6060
$file = $error['file'];
6161
$line = $error['line'];
62-
if (isset($error['context']['frame'])) {
63-
$file = $error['context']['frame']['file'];
64-
$line = $error['context']['frame']['line'];
65-
}
6662

6763
$errorData = [
6864
'file' => $file,

src/Plugin.php

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use Cake\Core\BasePlugin;
2020
use Cake\Core\Configure;
2121
use Cake\Core\PluginApplicationInterface;
22+
use Cake\Error\PhpError;
23+
use Cake\Event\EventInterface;
2224
use Cake\Event\EventManager;
2325
use Cake\Http\MiddlewareQueue;
2426
use DebugKit\Command\BenchmarkCommand;
@@ -50,7 +52,6 @@ public function bootstrap(PluginApplicationInterface $app): void
5052
}
5153

5254
$this->service = $service;
53-
5455
$this->setDeprecationHandler($service);
5556

5657
// will load `config/bootstrap.php`.
@@ -92,40 +93,29 @@ public function console(CommandCollection $commands): CommandCollection
9293
public function setDeprecationHandler($service)
9394
{
9495
if (!empty($service->getConfig('panels')['DebugKit.Deprecations'])) {
95-
$previousHandler = set_error_handler(
96-
function ($code, $message, $file, $line, $context = null) use (&$previousHandler) {
97-
if ($code == E_USER_DEPRECATED || $code == E_DEPRECATED) {
98-
// In PHP 8.0+ the $context variable has been removed from the set_error_handler callback
99-
// Therefore we need to fetch the correct file and line string ourselves
100-
if (PHP_VERSION_ID >= 80000) {
101-
$trace = debug_backtrace();
102-
foreach ($trace as $idx => $traceEntry) {
103-
if ($traceEntry['function'] !== 'deprecationWarning') {
104-
continue;
105-
}
106-
$offset = 1;
107-
// ['args'][1] refers to index of $stackFrame argument in deprecationWarning()
108-
if (isset($traceEntry['args'][1])) {
109-
$offset = $traceEntry['args'][1];
110-
}
111-
$file = $trace[$idx + $offset]['file'];
112-
$line = $trace[$idx + $offset]['line'];
113-
break;
114-
}
115-
}
116-
DeprecationsPanel::addDeprecatedError(compact('code', 'message', 'file', 'line', 'context'));
117-
118-
return;
119-
}
120-
if ($previousHandler) {
121-
$context['_trace_frame_offset'] = 1;
122-
123-
return $previousHandler($code, $message, $file, $line, $context);
124-
}
96+
EventManager::instance()->on('Error.handled', function (EventInterface $event, PhpError $error) {
97+
$code = $error->getCode();
98+
if ($code !== E_USER_DEPRECATED && $code !== E_DEPRECATED) {
99+
return;
100+
}
101+
$file = $error->getFile();
102+
$line = $error->getLine();
125103

126-
return false;
104+
// Extract the line/file from the message as deprecationWarning
105+
// will calculate the application frame when generating the message.
106+
preg_match('/\\n([^\n,]+?), line: (\d+)\\n/', $error->getMessage(), $matches);
107+
if ($matches) {
108+
$file = $matches[1];
109+
$line = $matches[2];
127110
}
128-
);
111+
112+
DeprecationsPanel::addDeprecatedError([
113+
'code' => $code,
114+
'message' => $error->getMessage(),
115+
'file' => $file,
116+
'line' => $line,
117+
]);
118+
});
129119
}
130120
}
131121
}

tests/TestCase/PluginTest.php

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
namespace DebugKit\Test\TestCase;
1616

17+
use Cake\Error\PhpError;
1718
use Cake\Event\Event;
1819
use Cake\Event\EventManager;
1920
use Cake\TestSuite\TestCase;
@@ -37,28 +38,44 @@ public function testSetDeprecationHandler()
3738
$service = new ToolbarService(new EventManager(), []);
3839
$plugin = new Plugin();
3940
$plugin->setDeprecationHandler($service);
40-
$event = new Event('');
4141
$panel = new DeprecationsPanel();
4242

43-
//Without setting the $stackFrame
44-
deprecationWarning('setDeprecationHandler');
45-
//Setting the $stackFrame
46-
deprecationWarning('setDeprecationHandler_2', 2);
47-
//Raw error
48-
$line = __LINE__ + 1;
49-
trigger_error('raw_error', E_USER_DEPRECATED);
43+
$error = new PhpError(E_USER_WARNING, 'ignored', __FILE__, __LINE__, []);
44+
$event = new Event('Error.handled', null, ['error' => $error]);
45+
EventManager::instance()->dispatch($event);
46+
47+
// No file/line in message.
48+
$error = new PhpError(E_USER_DEPRECATED, 'going away', __FILE__, __LINE__, []);
49+
$event = new Event('Error.handled', null, ['error' => $error]);
50+
EventManager::instance()->dispatch($event);
51+
52+
// Formatted like deprecationWarning()
53+
$message = <<<TEXT
54+
Something deprecated happened.
55+
Don't use that thing.
56+
src/Plugin.php, line: 51
57+
You can disable all deprecation warnings by setting `Error.errorLevel` to `E_ALL & ~E_USER_DEPRECATED`.
58+
TEXT;
59+
$error = new PhpError(E_USER_DEPRECATED, $message, __FILE__, __LINE__, []);
60+
$event = new Event('Error.handled', null, ['error' => $error]);
61+
EventManager::instance()->dispatch($event);
5062

5163
$panel->shutdown($event);
52-
$data = $panel->data()['plugins']['DebugKit'];
64+
$data = $panel->data();
65+
66+
$this->assertArrayHasKey('plugins', $data);
67+
$this->assertArrayHasKey('DebugKit', $data['plugins']);
68+
$this->assertCount(1, $data['plugins']['DebugKit']);
5369

54-
$this->assertCount(3, $data);
70+
$first = $data['plugins']['DebugKit'][0];
71+
$this->assertEquals($first['message'], 'going away');
72+
$this->assertEquals($first['file'], __FILE__);
73+
$this->assertEquals($first['line'], 48);
5574

56-
//test first two deprecationWarning()
57-
foreach ([$data[0], $data[1]] as $value) {
58-
$this->assertStringContainsString($value['file'], $value['message']);
59-
$this->assertStringContainsString("line: {$value['line']}", $value['message']);
60-
}
61-
//test raw error
62-
$this->assertSame($line, $data[2]['line']);
75+
$this->assertArrayHasKey('other', $data);
76+
$parsed = $data['other'][0];
77+
$this->assertEquals($parsed['message'], $message);
78+
$this->assertEquals($parsed['file'], 'src/Plugin.php');
79+
$this->assertEquals($parsed['line'], 51);
6380
}
6481
}

0 commit comments

Comments
 (0)