Exceptions

Web:Extend uses various exceptions for error handling, including SPL exceptions and a few others. The exceptions thrown by a function are listed in its docComment and can be found in the API. It is good practice to catch these exceptions and display a meaningful message to the user. If you don't, however, Web:Extend will handle it, as described in this article.

There's 2 kinds of exceptions: logical and runtime. The logical exceptions are thrown when something is wrong with the application itself. For example: a parameter has a wrong type. The runtime exceptions can only be detected at runtime: for example the user sent an unexpected value.

The following two tables contain all the exceptions used by the framework (but not necessarily defined by default).

Table 3.1. Logical exceptions

NameDescription
BadFunctionCallExceptionException thrown when a function call was illegal.
BadMethodCallExceptionException thrown when a method call was illegal.
BadXMLExceptionException thrown when an XML doesn't follow strictly its DTD schema.
DomainExceptionException that denotes a value not in the valid domain was used.
DoubleFaultExceptionException thrown in the exception handling code.
FileNotFoundExceptionException thrown when a required file is missing.
IllegalStateExceptionException thrown when a method is called and the object isn't in the right state (example: not initialized).
InvalidArgumentExceptionException that denotes invalid arguments were passed.
LengthExceptionException thrown when a parameter exceeds the allowed length.
OutOfRangeExceptionException thrown when an illegal index was requested.


Table 3.2. Runtime exceptions

NameDescription
ConfigurationExceptionException thrown when a configuration requirement is not met.
DatabaseExceptionException thrown when there is a database error.
EndOfFileExceptionException thrown when an end of file is reached.
NotPermittedExceptionException thrown when permission requirements are not met.
OutOfBoundsExceptionException thrown when an illegal index was requested.
OverflowExceptionException thrown to indicate arithmetic/buffer overflow.
RangeExceptionException thrown to indicate range errors during program execution.
UnderflowExceptionException thrown to indicate arithmetic/buffer underflow.
UnexpectedValueExceptionException thrown to indicate an unexpected value.


Default exception handling

By default, Web:Extend will catch all exceptions and print an error page. The framework also transforms any triggered error in an ErrorException which are then catchable like any other exception. The error page contains generic information about the error and a few instructions to help the user of the application. In DEBUG mode, the page will also contains a detailed report on the error, including a trace, if available.

You can customize the error page and set up your own instead of the generic one. To do this, simply call weeException::setErrorPage with the path to the error page file. This file will then get included, which means it can be a PHP script or just a simple HTML file. It will also be provided with $aDebug, an array containing debugging informations. You are free to use it or to ignore it.

Here is an example calling this method:

<?php

weeException::setErrorPage(ROOT_PATH . 'app/tpl/error_page.tpl');

The $aDebug array contains the following values:

  • type: either 'error' or 'exception'

  • name: name of the error/exception

  • message: description of the problem

  • trace: complete trace leading to the uncatched exception

  • file: the file where the error occurred

  • line: the line where the error occurred

  • number: only for errors; the error's number

You can take a look at the default error page located at trunk/res/wee/error.htm for a detailed example using this array.

Safely throwing exceptions

Avoid throwing exceptions directly using the throw keyword. If you make a typo in the exception's name, it will result in a fatal error.

The framework defines the burn function for throwing exceptions. It takes two parameters: the exception name and the error message. If the exception doesn't exist, a DoubleFaultException is thrown instead. If you really need a script to finish properly, catching Exception directly will ensure there is no problem, even if you made a typo.

A few examples:

<?php

// Instead of this...
throw new Exception('Oops!');

// Do this!
burn('Exception', 'Oops!');

// You can also use it this way:
$r = fopen('/path/to/file', 'r')
	or burn('FileNotFoundException', 'The file /path/to/file does not exist.');

Catching PHP errors

As explained before, when an error is triggered, the default error handler throws an ErrorException with the same informations as the triggered error. The default error handler totally ignores the error level set through error_reporting so every catchable error is caught by it.

The following snippet demonstrates how to catch PHP errors.

<?php

try {
	$a = array();
	echo $a[42]; // triggers a notice
} catch (ErrorException $e) {
	echo $e; // echoes "Undefined offset:  42"
}

Recommended practices

The shut-up operator @ should be used only in the following two conditions:

  • If it makes more sense to throw a specific exception instead of ErrorException.

  • If the target code must never fail. This condition should be very rare.

It is a really bad practice to use it when combined with an instruction that might include or parse a PHP file, as it would mask the error and display a blank page.

The following rules define how the developer can choose between the various exceptions:

  • If under normal use of the class the script should be terminated upon encountering a PHP error then use ErrorException.

  • If the normal use of the class may require the developer to catch an exception upon an error in a method and continue normally then use a normal exception instead of ErrorException. Example: FileNotFoundException, BadXMLException.

  • If the normal use of the class require the developer to catch exceptions to determine if an action was successful, then use a custom exception for the class/module. Example: FormValidationException, AuthenticationException, RouteNotFoundException.

Only for the last 2 cases the docComment should inform the developer of the exceptions that can be thrown by the function or method.

There is no need to check for invalid arguments unless not doing so could result in an illegal state. For example, the checks done in weeAuthDbTable::__construct are important because without them invalid arguments would not prevent the construction of the object and yet the authenticate and authenticateHash methods would always fail, thus rendering the object useless.