symfony: [RFC][ErrorHandler] Add a simple error handler for the userland

Now that we have a separate component named ErrorHandler, I have an idea.

It’s quite common to do this in the userland code:

$handle = @fopen('file.txt', 'rb');

if (false === $handle) {
    throw new FileNotFoundException();
}

I think it would be nice to have a DSL for better handling of such cases:

$handle = errorHandler()
    ->try(static function() {
        return fopen('file.txt', 'rb');
    })
    // or ->try('fopen', 'file.txt', 'rb')
    ->catchWarning(static function(string $message) {
        throw new FileNotFoundException();
    })
    // ->catchNotice()
;

Or with some reusability options:

$fopen = errorHandler('fopen')
    ->catchWarning(static function(string $message) {
        throw new FileNotFoundException();
    })
;

$fopen('file.txt', 'rb');
$fopen('another.xml', 'wb');

This mechanism could replace error handling in the Filesystem and other components across the framework.

@yceruto , WDYT?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 16 (16 by maintainers)

Commits related to this issue

Most upvoted comments

Nice idea, there is my proposal:

It allows also ErrorHandler::call('fopen', '/some/file', 'r');, which saves the closure around (but the closure wrapper is also possible, while less strict)

class ErrorHandler
{
    // [...]

    /**
     * Calls a function and turns any PHP warnings into \ErrorException
     *
     * @return mixed Returns $function(...$arguments)
     *
     * @throws \ErrorException When $function(...$arguments) triggers a PHP warning
     */
    public static function call(callable $function, ...$arguments)
    {
        set_error_handler(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();
        }
    }

    // [...]
}

On PHP 7.4 😃 ErrorHandler::call(fn () => fopen('/some/file', 'r'));

we lose all information about fopen. What’s the recommended way for this?

I don’t know if I would recommend it, but this would work: ErrorHandler::call(function () { return fopen('/some/file', 'r'); });

Please proceed with a PR yes (with tests) 😃

Should it be a part of the ErrorHandler class?

that would ease discovery, don’t you think?

Except that my package allows some opt-in tuning as well. But that’s probably not needed in fact.