Added ErrorHandler::call() method utility to turns any PHP warnings into `\ErrorException`

This commit is contained in:
Yonel Ceruto 2019-08-13 19:01:52 -04:00
parent 588890aea8
commit 0faa855b5e
3 changed files with 79 additions and 1 deletions

View File

@ -5,3 +5,4 @@ CHANGELOG
-----
* added the component
* added `ErrorHandler::call()` method utility to turn any PHP error into `\ErrorException`

View File

@ -153,6 +153,32 @@ class ErrorHandler
return $handler;
}
/**
* Calls a function and turns any PHP error into \ErrorException.
*
* @return mixed What $function(...$arguments) returns
*
* @throws \ErrorException When $function(...$arguments) triggers a PHP error
*/
public static function call(callable $function, ...$arguments)
{
set_error_handler(static function (int $type, string $message, string $file, int $line) {
if (__FILE__ === $file) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$file = $trace[2]['file'] ?? $file;
$line = $trace[2]['line'] ?? $line;
}
throw new \ErrorException($message, 0, $type, $file, $line);
});
try {
return $function(...$arguments);
} finally {
restore_error_handler();
}
}
public function __construct(BufferingLogger $bootstrappingLogger = null)
{
if ($bootstrappingLogger) {

View File

@ -123,11 +123,62 @@ class ErrorHandlerTest extends TestCase
}
// dummy function to test trace in error handler.
private static function triggerNotice($that)
public static function triggerNotice($that)
{
$that->assertSame('', $foo.$foo.$bar);
}
public function testFailureCall()
{
$this->expectException(\ErrorException::class);
$this->expectExceptionMessage('fopen(unknown.txt): failed to open stream: No such file or directory');
ErrorHandler::call('fopen', 'unknown.txt', 'r');
}
public function testCallRestoreErrorHandler()
{
$prev = set_error_handler('var_dump');
try {
ErrorHandler::call('fopen', 'unknown.txt', 'r');
$this->fail('An \ErrorException should have been raised');
} catch (\ErrorException $e) {
$prev = set_error_handler($prev);
restore_error_handler();
} finally {
restore_error_handler();
}
$this->assertSame('var_dump', $prev);
}
public function testCallErrorExceptionInfo()
{
try {
ErrorHandler::call([self::class, 'triggerNotice'], $this);
$this->fail('An \ErrorException should have been raised');
} catch (\ErrorException $e) {
$trace = $e->getTrace();
$this->assertSame(E_NOTICE, $e->getSeverity());
$this->assertSame(__FILE__, $e->getFile());
$this->assertSame('Undefined variable: foo', $e->getMessage());
$this->assertSame(0, $e->getCode());
$this->assertSame('Symfony\Component\ErrorHandler\{closure}', $trace[0]['function']);
$this->assertSame(ErrorHandler::class, $trace[0]['class']);
$this->assertSame('triggerNotice', $trace[1]['function']);
$this->assertSame(__CLASS__, $trace[1]['class']);
}
}
public function testSuccessCall()
{
touch($filename = tempnam(sys_get_temp_dir(), 'sf_error_handler_'));
self::assertIsResource(ErrorHandler::call('fopen', $filename, 'r'));
unlink($filename);
}
public function testConstruct()
{
try {