PHP学习(9)——错误和异常处理

1.Exception类

  这个类是PHP为异常处理提供的内置类。构造函数的两个参数分别是错误消息和错误代码。

  除了构造函数之外,该类还提供了如下的内置方法:

    · getCode() 返回传递给构造函数的代码

    · getMessage() 返回传递给构造函数的消息

    · getFile() 返回产生异常的代码文件的完整路径

    · getLine() 返回代码文件中产生异常的代码行号

    · getTrace() 返回一个包含了产生异常的代码回退路径的数组

    · getTraceAsString() 返回与getTrace()方向相同的信息,该信息将被格式化成一个字符串。

    · _toString() 允许简单地显示一个Exception对象,并且给出以上所有方法可以提供的信息。

  通过执行以下命令,可以获得相同的异常信息(以及代码回退路径):

echo $e;

  回退路径显示了在发生异常时所执行的函数。

 

2.异常控制结构

  异常处理的基本思想是代码在try代码块被调用执行。在PHP中,异常必须手动抛出。

  异常控制结构:try...throw...catch

  可以将多个catch代码块与一个try代码块进行关联。还可以在一个catch代码块产生新的异常。

  如下代码抛出并捕获一个异常:

<?php

try  {

  throw new Exception("A terrible error has occurred", 42);

}

catch (Exception $e) {

  echo "Exception ". $e->getCode(). ": ". $e->getMessage()."<br />".

  " in ". $e->getFile(). " on line ". $e->getLine(). "<br />";

}

?>

  这里我们使用了exception类的前四个方法,catch代码块报告了异常错误信息以及发生错误位置的说明。

 

3.用户自定义异常

  可以扩展Exception类来创建自己的异常类。

  可能需要继承的代码:

 

<?php 
class Exception{
    function __construct(string $message=NULL,int $code=0){
        if(func_num_args()){
            $this->message = $message;
        }
        $this->code = $code;
        $this->file = __FILE__;    // of throw clause
        $this->line = __LINE__; // of throw clause
        $this->trace = debug_backtrace();
        $this->string = StringFormat($this);
    }

    protected $message = "Unknown exception ";    // exception message
    protected $code = 0;    // user defined exception code
    protected $file;    // source filename of exception
    protected $line;    // source line of exception

    private $trace;    // backtrace of exception
    private $string;    // internal only!!

    final function getMessage(){
        return $this->message;
    }
    final function getCode(){
        return $this->code;
    }
    final function getFile(){
        return $this->file;
    }
    final function getTrace(){
        return $this->trace;
    }
    final function getTraceAsString(){
        return self::TraceFormat($this);
    }
    function _toString(){
        return $this->string;
    }
    static private function StringFormat(Exception $exception){
        // ... a function not available in PHP scripts
        // that returns all relevant information as a string
    }
    static private function TraceFormat(Exception $exception){
        // ... a function not available in PHP scripts
        // that returns the backtrace as a string
    }
}
?>

  该类的大多数公有方法都是final的:这就意味着不能重载这些方法。我们可以创建自己的Exception子类,但是不能改变这些基本方法的行为。请注意,_toString()函数可以重载,因此我们可以改变异常的显示方式。也可以添加自己的方法。

  用户自定义的Exception类示例:

 

<?php

class myException extends Exception
{
  function __toString()
  {
       return "<table border=\"1\">
       <tr>
       <td><strong>Exception ".$this->getCode()."
       </strong>: ".$this->getMessage()."<br />"."
       in ".$this->getFile()." on line ".$this->getLine()."
       </td>
       </tr>
       </table><br />";
   }
}

try
{
  throw new myException("A terrible error has occurred", 42);
}
catch (myException $m)
{
   echo $m;
}

?>

 

  在以上代码中,我们声明了一个新的异常类myException,该类扩展了Exception基类。该类与Exception类之间的差异在于重载了_toString()方法,从而为打印异常提供了一个更好的方法。

 

4.文件I/O相关的异常

  写文件时可能会出现三种情况的错误:文件无法打开、无法获得写锁或者文件无法写入。我们为每一种可能性都创建了一个异常类:

 

<?php

class fileOpenException extends Exception
{
  function __toString()
  {
       return "fileOpenException ". $this->getCode()
              . ": ". $this->getMessage()."<br />"." in "
              . $this->getFile(). " on line ". $this->getLine()
              . "<br />";
   }
}

class fileWriteException extends Exception
{
  function __toString()
  {
       return "fileWriteException ". $this->getCode()
              . ": ". $this->getMessage()."<br />"." in "
              . $this->getFile(). " on line ". $this->getLine()
              . "<br />";
   }
}

class fileLockException extends Exception
{
  function __toString()
  {
       return "fileLockException ". $this->getCode()
              . ": ". $this->getMessage()."<br />"." in "
              . $this->getFile(). " on line ". $this->getLine()
              . "<br />";
   }
}

?>

  Exception类的这些子类并没有执行任何特别的操作。事实上,对于这个应用程序的作用来说,可以让它们成为空的子类或者使用PHP所提供的Exception类。然而,我们为每个子类提供了_toString()方法,从而可以解释所发生的异常类型:

 

<?php

  require_once("file_exceptions.php");

  // create short variable names
  $tireqty = $_POST['tireqty'];
  $oilqty = $_POST['oilqty'];
  $sparkqty = $_POST['sparkqty'];
  $address = $_POST['address'];

  $DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
?>
<html>
<head>
  <title>Bob's Auto Parts - Order Results</title>
</head>
<body>
<h1>Bob's Auto Parts</h1>
<h2>Order Results</h2>
<?php
$date = date('H:i, jS F');

echo "<p>Order processed at ".$date."</p>";

echo '<p>Your order is as follows: </p>';

$totalqty = 0;
$totalqty = $tireqty + $oilqty + $sparkqty;
echo "Items ordered: ".$totalqty."<br />";

if( $totalqty == 0) {
  echo "You did not order anything on the previous page!<br />";
} else {
  if ( $tireqty > 0 ) {
    echo $tireqty." tires<br />";
  }
  if ( $oilqty > 0 ) {
    echo $oilqty." bottles of oil<br />";
  }
  if ( $sparkqty > 0 ) {
    echo $sparkqty." spark plugs<br />";
  }
}

$totalamount = 0.00;

define('TIREPRICE', 100);
define('OILPRICE', 10);
define('SPARKPRICE', 4);

$totalamount = $tireqty * TIREPRICE
             + $oilqty * OILPRICE
             + $sparkqty * SPARKPRICE;

$totalamount=number_format($totalamount, 2, '.', ' ');

echo "<p>Total of order is ".$totalamount."</p>";
echo "<p>Address to ship to is ".$address."</p>";

$outputstring = $date."\t".$tireqty." tires \t".$oilqty." oil\t"
                  .$sparkqty." spark plugs\t\$".$totalamount
                  ."\t". $address."\n";

// open file for appending
try
{
  if (!($fp = @fopen("$DOCUMENT_ROOT/../orders/orders.txt", 'ab')))
      throw new fileOpenException();

  if (!flock($fp, LOCK_EX))
     throw new fileLockException();

  if (!fwrite($fp, $outputstring, strlen($outputstring)))
     throw new fileWriteException();
  flock($fp, LOCK_UN);
  fclose($fp);
  echo "<p>Order written.</p>";
}
catch (fileOpenException $foe)
{
   echo "<p><strong>Orders file could not be opened.
         Please contact our webmaster for help.</strong></p>";
}
catch (Exception $e)
{
   echo "<p><strong>Your order could not be processed at this time.
         Please try again later.</strong></p>";
}

?>
</body>
</html>

  可以看到,以上脚本的文件I/O部分被封装在一个try代码块中。通常,良好的编码习惯要求try代码块的代码量较少,并且在代码块的结束处捕获相关异常。这使得异常处理代码更容易编写和维护,因为可以看到所处理的内容。

  如果无法打开文件,将抛出一个fileOpenException异常;如果无法锁定该文件,将抛出一个fileLockException异常;而如果无法写这个文件,将抛出一个fileWriteException异常。

  我们给出了两个catch代码块:一个用来处理fileOpen-Exception异常,而另一个用来处理Exception。由于其他异常都是从Exception继承过来的,它们将被第二个catch代码块捕获。catch代码块与每一个instanceof操作符相匹配。这就是为每一个类扩展自己异常类的理由。

  如果异常没有匹配的catch语句块,PHP将报告一个致命错误。

  请注意fopen()函数的调用仍然使用了@错误抑制操作符前缀。如果该函数调用失败,PHP将发出一个警告,根据php.ini中的错误报告设置不同,该警告可能会被报告或者记录。无论是否产生一个异常,这个警告仍然会发出。

  好了,从零开始攻略PHP系列到这里就水完了,仅供参考,好多我也是一知半解,只是整理出来方便查阅。下个月会陆续发一些数据库的,也就是该书整理的第二篇。

 

整理自《PHP and MySQL Web 开发》

 

posted @ 2016-08-26 15:48  Redchar  阅读(1357)  评论(0编辑  收藏  举报