pear:rfc:pear2_exception_policy

Summary

Information

Document Information

This RFC has been incorporated into the PEAR2 Standards (pear2_standards).

Author(s) Information

  • Name: Brett Bieber
  • Name: Chuck Burgess
  • Email: pear-group@php.net
  • This RFC is under the BSD License

Discussion List

Introduction

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.

Summary

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.

Proposed Changes:Base Exception interface

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";
 }

Requirement

No Exceptions to this rule

Proposed Changes

Alter the Base Exception class rule to the Base Package Exception interface above.

Common Questions

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
}

Full Code Example

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
pear/rfc/pear2_exception_policy.txt · Last modified: 2011/04/06 12:59 (external edit)