PHP 5.6.0beta1 released

例外(exceptions)

目次

PHP 5 は、他のプログラミング言語に似た例外モデルを有しています。 PHP 内で例外が投げられ ("throw" され)、それが 捕捉され ("catch" され) ます。発生した例外を 捕捉するには、コードを try ブロックで囲みます。 各 try ブロックには、対応する catch ブロックが存在する必要があります。異なる型の例外を捕捉するために 複数の catch フロックを使用することができます。 通常の実行時 (try ブロック内で例外が投げられなかった 場合、あるいは投げられた例外に対応する catch ブロックが存在しなかった場合) は、catch ブロック内は処理されず、それ以降から処理が続けられます。 catch ブロックの中から例外を投げる (あるいは投げなおす) こともできます。

例外が投げられた場合、その命令に続くコードは実行されず、 PHP は最初にマッチする catch ブロックを探します。 例外が捕捉されない場合、PHP は "Uncaught Exception ..." というメッセージとともに 致命的なエラー(fatal error)を発行します。 ただし、 set_exception_handler() でハンドラが 定義されている場合を除きます。

PHP 5.5 以降では、catch ブロックの後に finally ブロックも指定できるようになりました。 finally ブロックの何かに書いたコードは、 try および catch ブロックの後で常に実行されます。 例外がスローさされたかどうかには関係ありません。 finally ブロックを実行してから、通常の処理を続行します。

スローされるオブジェクトは、Exception クラスあるいは Exception のサブクラスのインスタンスでなければなりません。 それ以外のオブジェクトをスローしようとすると PHP の Fatal Error が発生します。

注意:

PHP の内部関数の多くは エラー報告 を使っており、例外を使っているのは新しい オブジェクト指向 の拡張モジュールのみです。 しかし、ErrorException を使えば簡単にエラーを例外に変換することができます。

ヒント

Standard PHP Library (SPL) には組み込みの例外が数多く用意されています。

例1 例外を投げるには

<?php
function inverse($x) {
    if (!
$x) {
        throw new 
Exception('ゼロによる除算。');
    }
    return 
1/$x;
}

try {
    echo 
inverse(5) . "\n";
    echo 
inverse(0) . "\n";
} catch (
Exception $e) {
    echo 
'捕捉した例外: ',  $e->getMessage(), "\n";
}

// 実行は継続される
echo "Hello World\n";
?>

上の例の出力は以下となります。

0.2
捕捉した例外: ゼロによる除算。
Hello World

例2 例外処理での finally ブロック

<?php
function inverse($x) {
    if (!
$x) {
        throw new 
Exception('ゼロによる除算。');
    }
    return 
1/$x;
}

try {
    echo 
inverse(5) . "\n";
} catch (
Exception $e) {
    echo 
'捕捉した例外: ',  $e->getMessage(), "\n";
finally {
    echo 
"First finally.\n";
}

try {
    echo 
inverse(0) . "\n";
} catch (
Exception $e) {
    echo 
'捕捉した例外: ',  $e->getMessage(), "\n";
finally {
    echo 
"Second finally.\n";
}

// 処理を続行します
echo "Hello World\n";
?>

上の例の出力は以下となります。

0.2
First finally.
捕捉した例外: ゼロによる除算。
Second finally.
Hello World

例3 ネストした例外

<?php

class MyException extends Exception { }

class 
Test {
    public function 
testing() {
        try {
            try {
                throw new 
MyException('foo!');
            } catch (
MyException $e) {
                
// 改めてスロー
                
throw $e;
            }
        } catch (
Exception $e) {
            
var_dump($e->getMessage());
        }
    }
}

$foo = new Test;
$foo->testing();

?>

上の例の出力は以下となります。

string(4) "foo!"
add a note add a note

User Contributed Notes 20 notes

up
23
Johan
2 years ago
Custom error handling on entire pages can avoid half rendered pages for the users:

<?php
ob_start
();
try {
   
/*contains all page logic
    and throws error if needed*/
   
...
} catch (
Exception $e) {
 
ob_end_clean();
 
displayErrorPage($e->getMessage());
}
?>
up
10
ask at nilpo dot com
4 years ago
If you intend on creating a lot of custom exceptions, you may find this code useful.  I've created an interface and an abstract exception class that ensures that all parts of the built-in Exception class are preserved in child classes.  It also properly pushes all information back to the parent constructor ensuring that nothing is lost.  This allows you to quickly create new exceptions on the fly.  It also overrides the default __toString method with a more thorough one.

<?php
interface IException
{
   
/* Protected methods inherited from Exception class */
   
public function getMessage();                 // Exception message
   
public function getCode();                    // User-defined Exception code
   
public function getFile();                    // Source filename
   
public function getLine();                    // Source line
   
public function getTrace();                   // An array of the backtrace()
   
public function getTraceAsString();           // Formated string of trace
   
    /* Overrideable methods inherited from Exception class */
   
public function __toString();                 // formated string for display
   
public function __construct($message = null, $code = 0);
}

abstract class
CustomException extends Exception implements IException
{
    protected
$message = 'Unknown exception';     // Exception message
   
private   $string;                            // Unknown
   
protected $code    = 0;                       // User-defined exception code
   
protected $file;                              // Source filename of exception
   
protected $line;                              // Source line of exception
   
private   $trace;                             // Unknown

   
public function __construct($message = null, $code = 0)
    {
        if (!
$message) {
            throw new
$this('Unknown '. get_class($this));
        }
       
parent::__construct($message, $code);
    }
   
    public function
__toString()
    {
        return
get_class($this) . " '{$this->message}' in {$this->file}({$this->line})\n"
                               
. "{$this->getTraceAsString()}";
    }
}
?>

Now you can create new exceptions in one line:

<?php
class TestException extends CustomException {}
?>

Here's a test that shows that all information is properly preserved throughout the backtrace.

<?php
function exceptionTest()
{
    try {
        throw new
TestException();
    }
    catch (
TestException $e) {
        echo
"Caught TestException ('{$e->getMessage()}')\n{$e}\n";
    }
    catch (
Exception $e) {
        echo
"Caught Exception ('{$e->getMessage()}')\n{$e}\n";
    }
}

echo
'<pre>' . exceptionTest() . '</pre>';
?>

Here's a sample output:

Caught TestException ('Unknown TestException')
TestException 'Unknown TestException' in C:\xampp\htdocs\CustomException\CustomException.php(31)
#0 C:\xampp\htdocs\CustomException\ExceptionTest.php(19): CustomException->__construct()
#1 C:\xampp\htdocs\CustomException\ExceptionTest.php(43): exceptionTest()
#2 {main}
up
4
zmunoz at gmail dot com
3 years ago
When catching an exception inside a namespace it is important that you escape to the global space:

<?php
 
namespace SomeNamespace;

 class
SomeClass {

  function
SomeFunction() {
   try {
    throw new
Exception('Some Error Message');
   } catch (\
Exception $e) {
   
var_dump($e->getMessage());
   }
  }

 }
?>
up
5
Edu
9 months ago
The "finally" block can change the exception that has been throw by the catch block.

<?php
try{
        try {
                throw new \
Exception("Hello");
        } catch(\
Exception $e) {
                echo
$e->getMessage()." catch in\n";
                throw
$e;
        } finally {
                echo
$e->getMessage()." finally \n";
                throw new \
Exception("Bye");
        }
} catch (\
Exception $e) {
        echo
$e->getMessage()." catch out\n";
}
?>

The output is:

Hello catch in
Hello finally
Bye catch out
up
2
jim at anderos dot com
6 months ago
If you are using a namespace, you must indicate the global namespace when using Exceptions.
<?php
namespace alpha;
function
foo(){
    throw new \
Exception("Something is wrong!");
   
// throw new Exception(""); will fail
}

try {
   
foo();
} catch( \
Exception $e ) {
   
// catch( Exception $e ) will give no warning, but will not catch Exception
   
echo "ERROR: $e";
}

?>
up
3
sander at rotorsolutions dot nl
9 months ago
Just an example why finally blocks are usefull (5.5)

<?php

//without catch
function example() {
  try {
   
//do something that throws an exeption
 
}
  finally {
   
//this code will be executed even when the exception is executed
 
}
}

function
example2() {
  try {
    
//open sql connection check user as example
    
if(condition) {
        return
false;
     }
  }
  finally {
   
//close the sql connection, this will be executed even if the return is called.
 
}
}
up
6
michael dot ochs at gmx dot net
6 years ago
Actually it isn't possible to do:
<?php
someFunction
() OR throw new Exception();
?>

This leads to a T_THROW Syntax Error. If you want to use this kind of exceptions, you can do the following:

<?php
function throwException($message = null,$code = null) {
    throw new
Exception($message,$code);
}

someFunction() OR throwException();
?>
up
3
sander at rotorsolutions dot nl
9 months ago
Just an example why finally blocks are usefull (5.5)

<?php

//without catch
function example() {
  try {
   
//do something that throws an exeption
 
}
  finally {
   
//this code will be executed even when the exception is executed
 
}
}

function
example2() {
  try {
    
//open sql connection check user as example
    
if(condition) {
        return
false;
     }
  }
  finally {
   
//close the sql connection, this will be executed even if the return is called.
 
}
}

?>
up
2
jon at hackcraft dot net
7 years ago
Further to dexen at google dot me dot up with "use destructors to perform a cleanup in case of exception". The fact that PHP5 has destructors, exception handling, and predictable garbage collection (if there's a single reference in scope and the scope is left then the destructor is called immediately) allows for the use of the RAII idiom.

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization and my own http://www.hackcraft.net/RAII/ describe this.
up
1
alex dowgailenko [at] g mail . com
2 years ago
If you use the set_error_handler() to throw exceptions of errors, you may encounter issues with __autoload() functionality saying that your class doesn't exist and that's it.

If you do this:

<?php

class MyException extends Exception
{
}

class
Tester
{
    public function
foobar()
    {
        try
        {
           
$this->helloWorld();
        } catch (
MyException $e) {
            throw new
Exception('Problem in foobar',0,$e);
        }
    }
   
    protected function
helloWorld()
    {
        throw new
MyException('Problem in helloWorld()');
    }
}

$tester = new Tester;
try
{
   
$tester->foobar();
} catch (
Exception $e) {
    echo
$e->getTraceAsString();
}
?>

The trace will only show $tester->foobar() and not the call made to $tester->helloWorld().

In other words, if you pass a previous exception to a new one, the previous exception's stack trace is taken into account in the new exception.
up
2
php at marcuspope dot com
2 months ago
Using a return statement inside a finally block will override any other return statement or thrown exception from the try block and all defined catch blocks.   Code execution in the parent stack will continue as if the exception was never thrown. 

Frankly this is a good design decision because it means I can optionally dismiss all thrown exceptions from 1 or more catch blocks in one place, without having to nest my whole try block inside an additional (and otherwise needless) try/catch block.

This is the same behavior as Java, whereas C# throws a compile time error when a return statement exists inside a finally block.  So I figured it was worth pointing out to PHP devs who may not have any exposure to finally blocks or how other languages do it.

<?php

function asdf()
{
    try {
        throw new
Exception('error');
    }
    catch(
Exception $e) {
        echo
"An error occurred";
        throw
$e;
    }
    finally {
               
//This overrides the exception as if it were never thrown
       
return "\nException erased";
    }
}

try {
    echo
asdf();
}
catch(
Exception $e) {
    echo
"\nResult: " . $e->getMessage();
}
?>

The output from above will look like this:

    An error occurred
    Exception erased

Without the return statement in the finally block it would look like this:

    An error occurred
    Result: error
up
1
jazfresh at hotmail.com
7 years ago
Sometimes you want a single catch() to catch multiple types of Exception. In a language like Python, you can specify multiple types in a catch(), but in PHP you can only specify one. This can be annoying when you want handle many different Exceptions with the same catch() block.

However, you can replicate the functionality somewhat, because catch(<classname> $var) will match the given <classname> *or any of it's sub-classes*.

For example:

<?php
class DisplayException extends Exception {};
class
FileException extends Exception {};
class
AccessControl extends FileException {}; // Sub-class of FileException
class IOError extends FileException {}; // Sub-class of FileException

try {
  if(!
is_readable($somefile))
     throw new
IOError("File is not readable!");
  if(!
user_has_access_to_file($someuser, $somefile))
     throw new
AccessControl("Permission denied!");
  if(!
display_file($somefile))
     throw new
DisplayException("Couldn't display file!");

} catch (
FileException $e) {
 
// This block will catch FileException, AccessControl or IOError exceptions, but not Exceptions or DisplayExceptions.
 
echo "File error: ".$e->getMessage();
  exit(
1);
}
?>

Corollary: If you want to catch *any* exception, no matter what the type, just use "catch(Exception $var)", because all exceptions are sub-classes of the built-in Exception.
up
1
Shot (Piotr Szotkowski)
5 years ago
‘Normal execution (when no exception is thrown within the try block, *or when a catch matching the thrown exception’s class is not present*) will continue after that last catch block defined in sequence.’

‘If an exception is not caught, a PHP Fatal Error will be issued with an “Uncaught Exception …” message, unless a handler has been defined with set_exception_handler().’

These two sentences seem a bit contradicting about what happens ‘when a catch matching the thrown exception’s class is not present’ (and the second sentence is actually correct).
up
0
cyrus+php at boadway dot ca
6 months ago
There's some inconsistent behaviour associated with PHP 5.5.3's finally and return statements. If a method returns a variable in a try block (e.g. return $foo;), and finally modifies that variable, the /modified/ value is returned. However, if the try block has a return that has to be evaluated in-line (e.g. return $foo+0;), finally's changes to $foo will /not/ affect the return value.

[code]
function returnVariable(){
    $foo = 1;
    try{
        return $foo;
    } finally {
        $foo++;
    }
}

function returnVariablePlusZero(){
    $foo = 1;
    try{
        return $foo + 0;
    } finally {
        $foo++;
    }
}

$test1 = returnVariable(); // returns 2, not the correct value of 1.
$test2 = returnVariablePlusZero(); // returns correct value of 1, but inconsistent with $test1.
[/code]

It looks like it's trying to be efficient by not allocating additional memory for the return value when it thinks it doesn't have to, but the spec is that finally is run after try is completed execution, and that includes the evaluation of the return expression.

One could argue (weakly) that the first method should be the correct result, but at least the two methods should be consistent.
up
0
sander at rotorsolutions dot nl
9 months ago
Just an example why finally blocks are usefull (5.5)

<?php

//without catch
function example() {
  try {
   
//do something that throws an exeption
 
}
  finally {
   
//this code will be executed even when the exception is executed
 
}
}

function
example2() {
  try {
    
//open sql connection check user as example
    
if(condition) {
        return
false;
     }
  }
  finally {
   
//close the sql connection, this will be executed even if the return is called.
 
}
}

?>
up
0
hartym dot dont dot like dot spam at gmail dot com
6 years ago
@serenity: of course you need to throw exception within the try block, catch will not watch fatal errors, nor less important errors but only exceptions that are instanceof the exception type you're giving. Of course by within the try block, i mean within every functions call happening in try block.

For example, to nicely handle old mysql errors, you can do something like this:

<?php
try
{
 
$connection = mysql_connect(...);
  if (
$connection === false)
  {
    throw new
Exception('Cannot connect do mysql');
  }

  
/* ... do whatever you need with database, that may mail and throw exceptions too ... */

  
mysql_close($connection);
}
catch (
Exception $e)
{
  
/* ... add logging stuff there if you need ... */

 
echo "This page cannot be displayed";
}

?>

By doing so, you're aiming at the don't repeat yourself (D.R.Y) concept, by managing error handling at only one place for the whole.
up
0
fjoggen at gmail dot com
7 years ago
This code will turn php errors into exceptions:

<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
    throw new
ErrorException($message, 0, $severity, $filename, $lineno);
}

set_error_handler('exceptions_error_handler');
?>

However since <?php set_error_handler()?> doesn't work with fatal errors, you will not be able to throw them as Exceptions.
up
-2
Sawsan
2 years ago
the following is an example of a re-thrown exception and the using of getPrevious function:

<?php

$name
= "Name";

//check if the name contains only letters, and does not contain the word name

try
   {
   try
     {
      if (
preg_match('/[^a-z]/i', $name))
       {
           throw new
Exception("$name contains character other than a-z A-Z");
       }  
       if(
strpos(strtolower($name), 'name') !== FALSE)
       {
          throw new
Exception("$name contains the word name");
       }
       echo
"The Name is valid";
     }
   catch(
Exception $e)
     {
     throw new
Exception("insert name again",0,$e);
     }
   }
 
catch (
Exception $e)
   {
   if (
$e->getPrevious())
   {
    echo
"The Previous Exception is: ".$e->getPrevious()->getMessage()."<br/>";
   }
   echo
"The Exception is: ".$e->getMessage()."<br/>";
   }

 
?>
up
-4
chugadie dot geo at yahoo dot com
6 years ago
@webmaster at asylum-et dot com

What Mo is describing is bug 44053 (http://bugs.php.net/bug.php?id=44053) in which exceptions cannot be caught if you are using a custom error handler to catch warnings, notices, etc.
up
-7
omnibus at omnibus dot edu dot pl
6 years ago
Just to be more precise in what Frank found:
Catch the exceptions always in order from the bottom to the top of the Exception and subclasses class hierarchy. If you have class MyException extending Exception and class My2Exception extending MyException always catch My2Exception before MyException.

Hope this helps
To Top