This RFC has been incorporated into the PEAR2 Standards (pear2_standards).
This RFC proposes changes to the PEAR2 Standards regarding the Base Exception class requirement. Changes available in PHP 5.3 make the base exception class PEAR2\Exception unnecessary. Further, this RFC proposes how exceptions should be handled at the individual package level, and provides recommendations on the usage of SPL exception classes.
The PEAR2 Standards include a package related rule defining how exceptions should be handled in PEAR2 packages. This includes a base exception class named PEAR2\Exception which each package must extend.
The base exception class in PEAR2 intended to supplement the Exception class available in PHP, adding features which were missing at the time, and provide that functionality to all PEAR2 classes. The getCause() function in PEAR2_Exception provided a mechanism for nested/chained exceptions. With the addition of the getPrevious() function in PHP 5.3, PEAR2_Exception offers no advantages over the base Exception class, and should be removed from the PEAR2 Package-related rules as a required external dependency.
This RFC proposes changing the Base Exception class rule to Base Package Exception interface, and further define how exceptions should be used at the package level.
Each package must define a base Exception interface which is implemented by any exception thrown within the package.
PEAR2/PackageName/Exception.php
<?php namespace PEAR2\PackageName; interface Exception {}
SPL exceptions are encouraged, and should be used when possible.
Extending SPL Example: PEAR2/PackageName/UnexpectedValueException.php
<?php namespace PEAR2\PackageName; class UnexpectedValueException extends \UnexpectedValueException implements Exception {}
Exception throwing example:
<?php namespace PEAR2\PackageName; class Foo { function run($method) { switch($method) { case 'add': case 'del': case 'up': $this->$method(); break; default: throw new UnexpectedValueException($method . ' is not a valid method.'); } } }
User exception catching example:
<?php require_once 'PEAR2/Autoload.php'; try { $p = new \PEAR2\PackageName\Foo(); $p->run('bar'); } catch (\PEAR2\PackageName\Exception $e) { echo "Caught exception from PEAR2\\PackageName"; }
No Exceptions to this rule
Alter the Base Exception class rule to the Base Package Exception interface above.
Q. Why a base package exception interface?
A. By implementing the base package exception interface, all exceptions for a given pear package can be caught with catch (\PEAR2\PackageName\Exception $e)
.
This allows end users to easily do the following:
<?php try { //do something with PEAR2\Package1 } catch (\PEAR2\Package1\Exception $e) { //do the same thing with PEAR2\Package2 } catch(\Exception $e) { //an unknown exception occurred }
This example contrasts the usage of a base Exception interface with a base Exception class.
Foo package that uses a base Exception interface:
<?php namespace foo; interface Exception {} class InvalidArgumentException extends \InvalidArgumentException implements Exception {} class OutOfBoundsException extends \OutOfBoundsException implements Exception {} class Foo { public function badArg() { throw new InvalidArgumentException(); } public function badBounds() { throw new OutOfBoundsException(); } } ?>
Bar package that uses a base Exception class:
<?php namespace bar; class Exception extends \Exception {} class InvalidArgumentException extends \InvalidArgumentException {} class OutOfBoundsException extends \OutOfBoundsException {} class Bar { public function badArg() { throw new InvalidArgumentException(); } public function badBounds() { throw new OutOfBoundsException(); } } ?>
Test script:
<?php echo 'Testing the Exception base INTERFACE ======================' . PHP_EOL; require_once 'Foo.php'; $x = new \foo\Foo(); try { echo ' * Will catch(\foo\Exception) catch a \foo\InvalidArgumentException ???' . PHP_EOL; $x->badArg(); } catch (\foo\Exception $e) { echo ' * Caught a \foo\Exception ok, so YES... as expected.' . PHP_EOL; } echo PHP_EOL; echo 'Testing the Exception base CLASS =====================' . PHP_EOL; require_once 'Bar.php'; $y = new \bar\Bar(); try { echo ' * Will catch(\bar\Exception) catch a \bar\InvalidArgumentException ???' . PHP_EOL; $y->badArg(); } catch (\bar\Exception $e) { echo ' * Caught a \bar\Exception ok, so YES... this is unexpected!' . PHP_EOL; } ?>
Test results:
Testing the Exception base INTERFACE ====================== * Will catch(\foo\Exception) catch a \foo\InvalidArgumentException ??? * Caught a \foo\Exception ok, so YES... as expected. Testing the Exception base CLASS ===================== * Will catch(\bar\Exception) catch a \bar\InvalidArgumentException ??? PHP Fatal error: Uncaught exception 'bar\InvalidArgumentException' in /home/ashnazg/exceptions/Bar.php:11 Stack trace: #0 /home/ashnazg/exceptions/test.php(19): bar\Bar->badArg() #1 {main} thrown in /home/ashnazg/exceptions/Bar.php on line 11