bug #40065 [ErrorHandler] fix handling messages with null bytes from anonymous classes (nicolas-grekas)

This PR was merged into the 4.4 branch.

Discussion
----------

[ErrorHandler] fix handling messages with null bytes from anonymous classes

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

PHP truncates error messages at null bytes before calling userland error handlers (known behavior in PHP, marked as "won't fix".)

This doesn't play well with anonymous classes.

This PR works around the issue by getting the message from the stack trace.

Commits
-------

ac94746dc7 [ErrorHandler] fix handling messages with null bytes from anonymous classes
This commit is contained in:
Nicolas Grekas 2021-02-04 11:59:55 +01:00
commit 6ce4d38d29
2 changed files with 23 additions and 12 deletions

View File

@ -422,11 +422,7 @@ class ErrorHandler
return false;
}
if (false !== strpos($message, "@anonymous\0")) {
$logMessage = $this->parseAnonymousClass($message);
} else {
$logMessage = $this->levels[$type].': '.$message;
}
$logMessage = $this->levels[$type].': '.$message;
if (null !== self::$toStringException) {
$errorAsException = self::$toStringException;
@ -455,6 +451,23 @@ class ErrorHandler
return true;
}
} else {
if (false !== strpos($message, '@anonymous')) {
$backtrace = debug_backtrace(false, 5);
for ($i = 1; isset($backtrace[$i]); ++$i) {
if (isset($backtrace[$i]['function'], $backtrace[$i]['args'][0])
&& ('trigger_error' === $backtrace[$i]['function'] || 'user_error' === $backtrace[$i]['function'])
) {
if ($backtrace[$i]['args'][0] !== $message) {
$message = $this->parseAnonymousClass($backtrace[$i]['args'][0]);
$logMessage = $this->levels[$type].': '.$message;
}
break;
}
}
}
$errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line);
if ($throw || $this->tracedErrors & $type) {

View File

@ -368,11 +368,12 @@ class ErrorHandlerTest extends TestCase
public function testHandleErrorWithAnonymousClass()
{
$anonymousObject = new class() extends \stdClass {
};
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
try {
$handler->handleError(3, 'foo '.\get_class(new class() extends \stdClass {
}).' bar', 'foo.php', 12);
trigger_error('foo '.\get_class($anonymousObject).' bar', \E_USER_WARNING);
$this->fail('Exception expected.');
} catch (\ErrorException $e) {
} finally {
@ -380,10 +381,7 @@ class ErrorHandlerTest extends TestCase
restore_exception_handler();
}
$this->assertSame('foo stdClass@anonymous bar', $e->getMessage());
$this->assertSame(3, $e->getSeverity());
$this->assertSame('foo.php', $e->getFile());
$this->assertSame(12, $e->getLine());
$this->assertSame('User Warning: foo stdClass@anonymous bar', $e->getMessage());
}
public function testHandleDeprecation()