Check the other SPL Exception classes and extend one of those if your intended exception is a subclass of one of those. This allows more finesse when catching.ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΊΠ»Π°ΡΡ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΡΡ ΠΏΡΡΡΠΌ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ Π²ΡΡΡΠΎΠ΅Π½Π½ΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ° Exception. ΠΠΈΠΆΠ΅ ΠΏΠΎΠΊΠ°Π·Π°Π½Ρ ΠΌΠ΅ΡΠΎΠ΄Ρ ΠΈ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΊΠ»Π°ΡΡΠ° Exception, ΠΊΠΎΡΠΎΡΡΠ΅ Π΄ΠΎΡΡΡΠΏΠ½Ρ Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΌ ΠΊΠ»Π°ΡΡΠ°ΠΌ.
ΠΡΠΈΠΌΠ΅Ρ #1 ΠΡΡΡΠΎΠ΅Π½Π½ΡΠΉ ΠΊΠ»Π°ΡΡ Exception
<?php
class Exception implements Throwable
{
protected $message = 'Unknown exception'; // Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ
private $string; // ΠΠ΅Ρ ΠΌΠ΅ΡΠΎΠ΄Π° __toString
protected $code = 0; // ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΊΠΎΠ΄ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ
protected $file; // Π€Π°ΠΉΠ», Π² ΠΊΠΎΡΠΎΡΠΎΠΌ ΠΊΠΎΠ΄ Π²ΡΠ±ΡΠΎΡΠΈΠ» ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
protected $line; // Π‘ΡΡΠΎΠΊΠ°, Π² ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΊΠΎΠ΄ Π²ΡΠ±ΡΠΎΡΠΈΠ» ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
private $trace; // Π’ΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΠ° Π²ΡΠ·ΠΎΠ²ΠΎΠ² ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² ΠΈ ΡΡΠ½ΠΊΡΠΈΠΉ
private $previous; // ΠΡΠ΅Π΄ΡΠ΄ΡΡΠ΅Π΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅, Π΅ΡΠ»ΠΈ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π²Π»ΠΎΠΆΠ΅Π½Π½ΠΎΠ΅
public function __construct($message = '', $code = 0, ?Throwable $previous = null);
final private function __clone(); // ΠΠ°ΠΏΡΠ΅ΡΠ°Π΅Ρ ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ
final public function getMessage(); // Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ
final public function getCode(); // ΠΠΎΠ΄ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ
final public function getFile(); // Π€Π°ΠΉΠ», Π² ΠΊΠΎΡΠΎΡΠΎΠΌ ΠΊΠΎΠ΄ Π²ΡΠ±ΡΠΎΡΠΈΠ» ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
final public function getLine(); // Π‘ΡΡΠΎΠΊΠ°, Π½Π° ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΊΠΎΠ΄ Π²ΡΠ±ΡΠΎΡΠΈΠ» ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
final public function getTrace(); // ΠΠ°ΡΡΠΈΠ² ΡΡΠ½ΠΊΡΠΈΠΈ backtrace()
final public function getPrevious(); // ΠΡΠ΅Π΄ΡΠ΄ΡΡΠ΅Π΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
final public function getTraceAsString(); // ΠΡΡΠΎΡΠΌΠ°ΡΠΈΡΠΎΠ²Π°Π½Π½Π°Ρ ΡΡΡΠΎΠΊΠ° ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΠΈ
// ΠΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌΡΠΉ
public function __toString(); // ΠΡΡΠΎΡΠΌΠ°ΡΠΈΡΠΎΠ²Π°Π½Π½Π°Ρ ΡΡΡΠΎΠΊΠ° Π΄Π»Ρ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ
}Π ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΠ΅ ΠΊΠ»Π°ΡΡΠ°-Π½Π°ΡΠ»Π΅Π΄Π½ΠΈΠΊΠ° Π½ΡΠΆΠ½ΠΎ ΡΠ°ΠΊΠΆΠ΅ Π²ΡΠ·Π²Π°ΡΡ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡ ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ° β parent::__construct(), ΠΊΠΎΠ³Π΄Π° ΠΊΠ»Π°ΡΡ ΡΠ°ΡΡΠΈΡΡΠ΅Ρ ΠΊΠ»Π°ΡΡ Exception ΠΈ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡ, ΡΡΠΎΠ±Ρ Π³Π°ΡΠ°Π½ΡΠΈΡΠΎΠ²Π°ΡΡ, ΡΡΠΎ ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΊΠ»Π°ΡΡ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ ΠΏΡΠΈΡΠ²ΠΎΠΈΠ» Π·Π½Π°ΡΠ΅Π½ΠΈΡ Π΄ΠΎΡΡΡΠΏΠ½ΡΠΌ Π΄Π°Π½Π½ΡΠΌ. ΠΠ΅ΡΠΎΠ΄ __toString() Π΄ΠΎΠΏΡΡΡΠΈΠΌΠΎ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΡΡ, ΡΡΠΎΠ±Ρ Π½Π°ΡΡΡΠΎΠΈΡΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΉ Π²ΡΠ²ΠΎΠ΄, ΠΊΠΎΠ³Π΄Π° Ρ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠΌ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ ΡΠ°Π±ΠΎΡΠ°ΡΡ ΠΊΠ°ΠΊ ΡΠΎ ΡΡΡΠΎΠΊΠΎΠΉ.
ΠΠ°ΠΌΠ΅ΡΠ°Π½ΠΈΠ΅:
ΠΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ Π½Π΅Π»ΡΠ·Ρ ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°ΡΡ. ΠΠΎΠΏΡΡΠΊΠ° ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°ΡΡ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΈΠ²Π΅Π΄ΡΡ ΠΊ ΡΠ°ΡΠ°Π»ΡΠ½ΠΎΠΉ ΠΎΡΠΈΠ±ΠΊΠ΅
E_ERROR.
ΠΡΠΈΠΌΠ΅Ρ #2 ΠΠ°ΡΠ»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠ»Π°ΡΡΠ° Exception
<?php
/**
* ΠΠΏΡΠ΅Π΄Π΅Π»ΠΈΠΌ ΠΊΠ»Π°ΡΡ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ
*/
class MyException extends Exception
{
// ΠΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΠΌ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΡΠ°ΠΊ, ΡΡΠΎ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ message ΡΡΠ°Π½Π΅Ρ ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΡΠΌ
public function __construct($message, $code = 0, ?Throwable $previous = null)
{
// ΠΠ°ΠΊΠΎΠΉ-ΡΠΎ ΠΊΠΎΠ΄
// Π£Π±Π΅Π΄ΠΈΠΌΡΡ, ΡΡΠΎ ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΊΠ»Π°ΡΡ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ ΠΏΡΠΈΡΠ²ΠΎΠΈΠ» Π·Π½Π°ΡΠ΅Π½ΠΈΡ
parent::__construct($message, $code, $previous);
}
// ΠΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΠΌ ΡΡΡΠΎΠΊΠΎΠ²ΠΎΠ΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
public function __toString()
{
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
public function customFunction()
{
echo "ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠ°Ρ ΡΡΠ½ΠΊΡΠΈΡ Π΄Π»Ρ ΡΡΠΎΠ³ΠΎ ΡΠΈΠΏΠ° ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ\n";
}
}
/**
* Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΠΊΠ»Π°ΡΡ Π΄Π»Ρ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ
*/
class TestException
{
public $var;
const THROW_NONE = 0;
const THROW_CUSTOM = 1;
const THROW_DEFAULT = 2;
function __construct($avalue = self::THROW_NONE)
{
switch ($avalue) {
case self::THROW_CUSTOM:
// ΠΡΠ±ΡΠ°ΡΡΠ²Π°Π΅ΠΌ ΡΠ²ΠΎΡ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
throw new MyException('1 β Π½Π΅ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΡΠΉ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ', 5);
break;
case self::THROW_DEFAULT:
// ΠΡΠ±ΡΠ°ΡΡΠ²Π°Π΅ΠΌ Π²ΡΡΡΠΎΠ΅Π½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
throw new Exception('2 β Π½Π΅Π΄ΠΎΠΏΡΡΡΠΈΠΌΡΠΉ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ', 6);
break;
default:
// ΠΠ΅Π· ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ, PHP ΡΠΎΠ·Π΄Π°ΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ
$this->var = $avalue;
break;
}
}
}
echo "# ΠΡΠΈΠΌΠ΅Ρ 1\n";
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (MyException $e) { // ΠΠ΅ΡΠ΅Ρ
Π²Π°ΡΠΈΡΡΡ
echo "ΠΠ»ΠΎΠΊ catch ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΠΈΠ» ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΎΠ΅ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅\n", $e;
$e->customFunction();
} catch (Exception $e) { // ΠΡΠΎΠΏΡΡΠΊΠ°Π΅ΡΡΡ
echo "ΠΠΎΠΉΠΌΠ°Π½ΠΎ Π²ΡΡΡΠΎΠ΅Π½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅\n", $e;
}
// ΠΡΠΎΠ΄ΠΎΠ»ΠΆΠΈΡΡ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅
var_dump($o); // Null
echo "\n\n# ΠΡΠΈΠΌΠ΅Ρ 2\n";
try {
$o = new TestException(TestException::THROW_DEFAULT);
} catch (MyException $e) { // Π’ΠΈΠΏ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ Π½Π΅ ΡΠΎΠ²ΠΏΠ°Π΄ΡΡ
echo "ΠΠ»ΠΎΠΊ catch ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΠΈΠ» ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΎΠ΅ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅\n", $e;
$e->customFunction();
} catch (Exception $e) { // ΠΠ΅ΡΠ΅Ρ
Π²Π°ΡΠΈΡΡΡ
echo "ΠΠ»ΠΎΠΊ catch ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΠΈΠ» Π²ΡΡΡΠΎΠ΅Π½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅\n", $e;
}
// ΠΡΠΎΠ΄ΠΎΠ»ΠΆΠΈΡΡ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅
var_dump($o); // Null
echo "\n\n# ΠΡΠΈΠΌΠ΅Ρ 3\n";
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (Exception $e) { // ΠΠ΅ΡΠ΅Ρ
Π²Π°ΡΠΈΡΡΡ
echo "ΠΠ»ΠΎΠΊ catch ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΠΈΠ» Π²ΡΡΡΠΎΠ΅Π½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅\n", $e;
}
// ΠΡΠΎΠ΄ΠΎΠ»ΠΆΠΈΡΡ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅
var_dump($o); // Null
echo "\n\n# ΠΡΠΈΠΌΠ΅Ρ 4\n";
try {
$o = new TestException();
} catch (Exception $e) { // ΠΡΠΎΠΏΡΡΠΊΠ°Π΅ΡΡΡ, ΠΏΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π½Π΅ Π²ΡΠ±ΡΠ°ΡΡΠ²Π°Π΅ΡΡΡ
echo "ΠΠ»ΠΎΠΊ catch ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΠΈΠ» Π²ΡΡΡΠΎΠ΅Π½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅\n", $e;
}
// ΠΡΠΎΠ΄ΠΎΠ»ΠΆΠΈΡΡ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅
var_dump($o); // TestExceptionCheck the other SPL Exception classes and extend one of those if your intended exception is a subclass of one of those. This allows more finesse when catching.As previously noted exception linking was recently added (and what a god-send it is, it certainly makes layer abstraction (and, by association, exception tracking) easier).
Since <5.3 was lacking this useful feature I took some initiative and creating a custom exception class that all of my exceptions inherit from:
<?php
class SystemException extends Exception
{
private $previous;
public function __construct($message, $code = 0, Exception $previous = null)
{
parent::__construct($message, $code);
if (!is_null($previous))
{
$this -> previous = $previous;
}
}
public function getPrevious()
{
return $this -> previous;
}
}
?>
Hope you find it useful.Support for exception linking was added in PHP 5.3.0. The getPrevious() method and the $previous argument to the constructor are not available on any built-in exceptions in older versions of PHP.Custom exception classes can allow you to write tests that prove your exceptions
are meaningful. Usually testing exceptions, you either assert the message equals
something in which case you can't change the message format without refactoring,
or not make any assertions at all in which case you can get misleading messages
later down the line. Especially if your $e->getMessage is something complicated
like a var_dump'ed context array.
The solution is to abstract the error information from the Exception class into
properties that can be tested everywhere except the one test for your formatting.
<?php
class TestableException extends Exception {
private $property;
function __construct($property) {
$this->property = $property;
parent::__construct($this->format($property));
}
function format($property) {
return "I have formatted: " . $property . "!!";
}
function getProperty() {
return $this->property;
}
}
function testSomethingThrowsTestableException() {
try {
throw new TestableException('Property');
} Catch (TestableException $e) {
$this->assertEquals('Property', $e->getProperty());
}
}
function testExceptionFormattingOnlyOnce() {
$e = new TestableException;
$this->assertEquals('I have formatted: properly for the only required test!!',
$e->format('properly for the only required test')
);
}
?>It's important to note that subclasses of the Exception class will be caught by the default Exception handler
<?php
/**
* NewException
* Extends the Exception class so that the $message parameter is now mendatory.
*
*/
class NewException extends Exception {
//$message is now not optional, just for the extension.
public function __construct($message, $code = 0, Exception $previous = null) {
parent::__construct($message, $code, $previous);
}
}
/**
* TestException
* Tests and throws Exceptions.
*/
class TestException {
const NONE = 0;
const NORMAL = 1;
const CUSTOM = 2;
public function __construct($type = self::NONE) {
switch ($type) {
case 1:
throw new Exception('Normal Exception');
break;
case 2:
throw new NewException('Custom Exception');
break;
default:
return 0; //No exception is thrown.
}
}
}
try {
$t = new TestException(TestException::CUSTOM);
}
catch (Exception $e) {
print_r($e); //Exception Caught
}
?>
Note that if an Exception is caught once, it won't be caught again (even for a more specific handler).