rfc:throwable-interface

This is an old revision of the document!


PHP RFC: Throwable Interface

Introduction

PHP 7 has introduced exceptions as a replacement for fatal or recoverable fatal errors. These exceptions do not extend Exception, but instead extend a new class BaseException and are named EngineException, TypeException, and ParseException. These

The current exception hierarchy implemented for PHP 7 has a naming scheme that is unintuitive and will lead to confusion, especially for newer users.

Example:

function add(int $left, int $right) {
    return $left + $right;
};
 
try {
    echo add('left', 'right');
} catch (Exception $e) {
    // Handle or log exception.
}

The code above will not catch the TypeException thrown due to the mis-matched type-hint, resulting in the following message to the user:

Fatal error: Uncaught TypeException: Argument 1 passed to add() must be of the type integer, string given

The reason that an object named TypeException would not be caught by catch (Exception $e) is not obvious.

Proposal

This RFC proposes a change to the exception hierarchy for PHP 7. This proposal is based on the Throwable pull request created by Sebastian Bergmann and has been fully implemented in the Throwable Interface pull request.

  • interface Throwable
    • Exception implements Throwable
    • Error implements Throwable (Replaces EngineException)
      • TypeError extends Error
      • ParseError extends Error

BaseException will be removed. In order for an object to be thrown, it must implement the Throwable interface.

The Throwable interface specifies the following methods:

  • getMessage()
  • getCode()
  • getFile()
  • getLine()
  • getTrace()
  • getTraceAsString()
  • __toString()

While both Exception and Error are implemented using the same code in the interpreter, the Throwable interface does not preclude future classes from implementing the interface differently.

catch (Error $e) and catch (Throwable $e) may respectively be used to catch Error objects or any Throwable (current or future) object. Users should be discouraged from catching Error objects object except for logging or cleanup purposes as Error objects represent coding problems that should be fixed rather than runtime conditions that may be handled.

Error Name Choice

The name Error was chosen to correspond with PHP's other errors. Non-fatal errors will issue warnings and notices, while fatal errors are thrown. While this may also cause some confusion for users, other name choices for the class such as Failure seem equally problematic. It is likely that users would search for 'Uncaught Error' when searching for the reason behind the problem and would minimize overlap with with warning and notice messages. Users may even wish to use set_error_handler() to throw objects extending Error in their code for any type of error.

AssertionException

If this RFC is accepted, AssertionException should instead extend Error and be renamed to AssertionError as suggested in the Expectations RFC.

Proposed PHP Version

PHP 7

Backwards Compatibility

Throwable, Error, TypeError, and ParseError will no longer be available in the global namespace.

Patch

A patch for this RFC is available at https://github.com/php/php-src/pull/1284.

The patch prevents user classes from implementing Throwable, preventing users from creating their own throwable branches, ensuring only Exception and Error instances can be thrown. Error instances can be created and extended by users, allowing users to create throwable Error objects to create their own fatal errors.

References

rfc/throwable-interface.1432273574.txt.gz · Last modified: 2017/09/22 13:28 (external edit)