Skip to content

Commit efd2fab

Browse files
carlbennettclaude
andcommitted
Log deprecations instead of failing the request
The error handler treated every reported error as fatal, so any E_DEPRECATED, which only warns about a future PHP version, returned a 500. Compile-time deprecations in vendor code, such as Parsedown 1.7.4 on PHP 8.4+, failed the first request after each php-fpm restart. Log E_DEPRECATED and E_USER_DEPRECATED to the error log and let the request continue. All other reported errors are still fatal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 1c55b57 commit efd2fab

2 files changed

Lines changed: 67 additions & 0 deletions

File tree

‎src/Libraries/Core/ExceptionHandler.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ public static function errorHandler(
2727
// Don't handle this error if it's turned off administratively:
2828
if (!(\error_reporting() & $errno)) return false;
2929

30+
// Deprecations only warn about future PHP versions, so log them and let the request continue:
31+
if ($errno === \E_DEPRECATED || $errno === \E_USER_DEPRECATED)
32+
{
33+
\error_log(\sprintf(
34+
'%s: %s in %s on line %d',
35+
self::phpErrorName($errno), $errstr, $errfile, $errline
36+
));
37+
return true;
38+
}
39+
3040
// Back out of any output buffers:
3141
while (\ob_get_level()) \ob_end_clean();
3242

‎tests/Libraries/Core/ExceptionHandlerTest.php‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,61 @@ public function testPhpErrorNameZeroReturnsEUnknown(): void
112112
{
113113
$this->assertSame('E_UNKNOWN', ExceptionHandler::phpErrorName(0));
114114
}
115+
116+
// errorHandler — deprecations are logged, not fatal
117+
118+
private function callErrorHandler(int $errno, int $reporting, ?string &$log): bool
119+
{
120+
$logFile = tempnam(sys_get_temp_dir(), 'bnetdocs-error-log-');
121+
$savedLog = ini_set('error_log', $logFile);
122+
$savedReporting = error_reporting($reporting);
123+
try
124+
{
125+
return ExceptionHandler::errorHandler($errno, 'Something is deprecated', '/path/to/file.php', 42);
126+
}
127+
finally
128+
{
129+
error_reporting($savedReporting);
130+
ini_set('error_log', $savedLog);
131+
$log = file_get_contents($logFile);
132+
unlink($logFile);
133+
}
134+
}
135+
136+
public function testErrorHandlerLogsAndContinuesOnDeprecated(): void
137+
{
138+
// Reaching the assertions at all means the handler didn't exit().
139+
$this->assertTrue($this->callErrorHandler(E_DEPRECATED, E_ALL, $log));
140+
$this->assertStringContainsString('E_DEPRECATED: Something is deprecated in /path/to/file.php on line 42', $log);
141+
}
142+
143+
public function testErrorHandlerLogsAndContinuesOnUserDeprecated(): void
144+
{
145+
$this->assertTrue($this->callErrorHandler(E_USER_DEPRECATED, E_ALL, $log));
146+
$this->assertStringContainsString('E_USER_DEPRECATED: Something is deprecated in /path/to/file.php on line 42', $log);
147+
}
148+
149+
public function testErrorHandlerKeepsOutputBuffersOnDeprecated(): void
150+
{
151+
// A deprecation must not discard the page rendered so far.
152+
ob_start();
153+
echo 'partial page';
154+
$level = ob_get_level();
155+
try
156+
{
157+
$this->callErrorHandler(E_DEPRECATED, E_ALL, $log);
158+
$this->assertSame($level, ob_get_level());
159+
$this->assertSame('partial page', ob_get_contents());
160+
}
161+
finally
162+
{
163+
ob_end_clean();
164+
}
165+
}
166+
167+
public function testErrorHandlerIgnoresErrorsExcludedFromErrorReporting(): void
168+
{
169+
$this->assertFalse($this->callErrorHandler(E_DEPRECATED, E_ALL & ~E_DEPRECATED, $log));
170+
$this->assertSame('', $log);
171+
}
115172
}

0 commit comments

Comments
 (0)