【php】本当にtry catchを使うの?

14615 ワード

もうすぐ1年phpをやって、最も基礎的な異常処理さえ理解していないことを発見して、ネット上でこのような1篇のとても詳しい説明を発見して、そこで私の熟練した複製は貼り付けて保存して閉じます;
TPフレームワークを使い慣れてtry catchが@として使えると思っていたので、手に小さなコードを打つまで..基礎シリーズの補完
 
オリジナルを尊重して、お進みくださいhttps://www.cnblogs.com/zyf-zhaoyafei/p/6928149.html,以下の内容は純粋に写したものである.
オリジナルを尊重して、お進みくださいhttps://www.cnblogs.com/zyf-zhaoyafei/p/6928149.html,以下の内容は純粋に写したものである.
オリジナルを尊重して、お進みくださいhttps://www.cnblogs.com/zyf-zhaoyafei/p/6928149.html,以下の内容は純粋に写したものである.
 
PHP異常処理といえば、まずtry-catchを思い浮かべます.では、まずプログラムを見てみましょう.testがあります.phpファイル、1段の簡単なPHPプログラムがあって、内容は以下の通りで、それから命令行は実行します:php test.php
1 getMessage();
8     }
9 ?>

  我的问题是:这段程序能正确的捕捉到除0的错误信息吗?
  如果你回答能,那你就把这篇文章看完吧!应该能学点东西。

本文章分5个部分介绍我的异常处理的理解:

一、异常与错误的概述

二、ERROR的级别

三、PHP异常处理中的黑科技

四、巧妙的捕获错误和异常

五、自定义异常处理和异常嵌套

六、PHP7中的异常处理

 

一、异常与错误的概述

        PHP中什么是异常:
  程序在运行中出现不符合预期的情况,允许发生(你也不想让他出现不正常的情况)但他是一种不正常的情况,按照我们的正常逻辑本不该出的错误,但仍然会出现的错误,属于逻辑和业务流程的错误,而不是编译或者语法上的错误。

  PHP中什么是错误:
  属于php脚本自身的问题,大部分情况是由错误的语法,服务器环境导致,使得编译器无法通过检查,甚至无法运行的情况。warning、notice都是错误,只是他们的级别不同而已,并且错误是不能被try-catch捕获的。

  上面的说法是有前提条件的:
  在PHP中,因为在其他语言中就不能这样下结论了,也就是说异常和错误的说法在不同的语言有不同的说法。在PHP中任何自身的错误或者是非正常的代码都会当做错误对待,并不会以异常的形式抛出,但是也有一些情况会当做异常和错误同时抛出(据说是,我没有找到合适的例子)。也就是说,你想在数据库连接失败的时候自动捕获异常是行不通的,因为这就不是异常,是错误。但是在java中就不一样了,他会把很多和预期不一致的行为当做异常来进行捕获。

  PHP异常处理很鸡肋?
  在上面的分析中我们可以看出,PHP并不能主动的抛出异常,但是你可以手动抛出异常,这就很无语了,如果你知道哪里会出问题,你添加if else解决不就行了吗,为啥还要手动抛出异常,既然能手动抛出就证明这个不是异常,而是意料之中。以我的理解,这就是PHP异常处理鸡肋的地方(不一定对啊)。所以PHP的异常机制不是那么的完美,但是使用过框架的同学都知道有这个情况:你在框架中直接写开头那段php“自动”捕获异常的代码是可以的,这是为什么?看过源码的同学都知道框架中都会涉及三个函数:register_shutdown_function,set_error_handler,set_exception_handler后面我会重点讲解着三个黑科技,通过这几个函数我们可以实现PHP假自动捕获异常和错误。

 

二、ERROR的级别

 

  只有熟悉错误级别才能对错误捕捉有更好的认识。 ERROR有不同的错误级别,我之前的一篇文章中有写到:http://www.cnblogs.com/zyf-zhaoyafei/p/3649434.html
  下面我再总结性的给出这几类错误级别:

 

 1     Fatal Error:    (      )
 2         E_ERROR         //        ,      ,      
 3         E_CORE_ERROR    // PHP              
 4         E_COMPILE_ERROR //        ,   Zend         E_ERROR
 5         E_USER_ERROR    //        。  PHP  trigger_error(       :E_USER_ERROR)
 6 
 7     Parse Error:       ,    (      )
 8         E_PARSE  //          
 9 
10     Warning Error:    (       ,       )
11         E_WARNING         //       (     )。
12         E_CORE_WARNING    // PHP              (     ) 。
13         E_COMPILE_WARNING //     
14         E_USER_WARNING    //          
15 
16     Notice Error:    (       ,       )
17         E_NOTICE      //      。                 .
18         E_USER_NOTICE //          。

  由此可知有5类是产生ERROR级别的错误,这种错误直接导致PHP程序退出。
  可以定义成:

1 ERROR = E_ERROR | E_CORE_ERROR |  E_COMPILE_ERROR | E_USER_ERROR | E_PARSE

 
三、PHP異常処理中のブラックテクノロジー
前のフレームワークでは、すべてのエラーと異常をキャプチャすることができますが、実現できるのはブラックテクノロジーを使用しているはずです.ハハ!実はブラックテクノロジーではなく、主に3つの重要な関数です.
  1:set_error_handler()この名前を見ると意味がわかります.この関数はエラーをキャプチャし、ユーザー定義のエラー処理関数を設定するために使用されます.
1 set_error_handler: ' . $type . ':' . $message . ' in ' . $file . ' on ' . $line . ' line .
'); 6 } 7 ?>

  当程序出现错误的时候自动调用此方法,不过需要注意一下两点:第一,如果存在该方法,相应的error_reporting()就不能在使用了。所有的错误都会交给自定义的函数处理。第二,此方法不能处理以下级别的错误:E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,set_error_handler() 函数所在文件中产生的E_STRICT,该函数只能捕获系统产生的一些Warning、Notice级别的错误。
  并且他有多种调用的方法:

1 

   2:register_shutdown_function()
  捕获PHP的错误:Fatal Error、Parse Error等,这个方法是PHP脚本执行结束前最后一个调用的函数,比如脚本错误、die()、exit、异常、正常结束都会调用,多么牛逼的一个函数啊!通过这个函数就可以在脚本结束前判断这次执行是否有错误产生,这时就要借助于一个函数:error_get_last();这个函数可以拿到本次执行产生的所有错误。error_get_last();返回的信息:
  [type]           - 错误类型
  [message] - 错误消息
  [file]              - 发生错误所在的文件
  [line]             - 发生错误所在的行

1 register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '');
7         }
8     }
9 ?>

   通过这种方法就可以巧妙的打印出程序结束前所有的错误信息。但是我在测试的时候我发现并不是所有的错误终止后都会调用这个函数,可以看下面的一个测试文件,内容是:

 1 register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '');
 7         }
 8     }
 9     var_dump(23+-+); //      
10 ?>

   自己可以试一下,你可以看到根本就不会触发zyfshutdownfunc()函数,其实这是一个语法错误,直接报了一个:

1 

何がトリガーできないのか、なぜフレームワークの中でできるのかという奇妙な質問を引き出した.実は理由は簡単で、parse-timeエラー時だけこの関数は呼び出されません.run-timeエラーが発生した場合にのみ、この関数が呼び出されます.構文チェッカーの前にregister_が実行されていないことを理解しています.shutdown_function()は、登録する必要がある関数を呼び出しのスタックに置くので、実行されません.そのフレームワークの中でなぜどんな間違いもregisterに入ることができますか?shutdown_function()では、フレームワークには一般的に統一的な入り口indexがあります.phpは、各クラスライブラリファイルをinclude**でindexにロードします.phpでは、ほとんどのプログラムがindexにあります.phpに集約すると、同じように文法エラーのあるファイルもエントリファイルに導入され、フレームワークを呼び出してindexを実行します.php,index.php自体に文法エラーはなく、parse-timeエラーも発生しません.includeファイルでエラーが発生しました.run-timeの時にエラーが発生しました.フレームワークが実行されるとregister_がトリガーされます.shutdown_function(); だから今この書き方を試してみるとzyfshutdownfunc()コールバックがトリガーされます.
 1 a.php  
 2 
 6 
 7 b.php  
 8 register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '');
14         }
15     }
16     require 'a.php';
17 ?>

   3:set_exception_handler()
  设置默认的异常处理程序,用在没有用try/catch块来捕获的异常,也就是说不管你抛出的异常有没有人捕获,如果没有人捕获就会进入到该方法中,并且在回调函数调用后异常会中止。看一下用法:

 

1 set_exception_handler: Exception: " . $exception->getMessage()  . '');
6     }
7     throw new Exception("zyf exception");
8 ?>

 
 
四、巧みな捕獲エラーと異常1:エラーを異常な形で投げ出す(完全に投げ出すことができない)上記の説明から分かるように、phpのエラーは異常な像で捕獲することはできないが、彼らに投げ出す必要があり、try-catchの影響範囲を拡張する必要がある.error_handler()メソッド、彼は何を使っているのか、彼は間違いを捕まえているので、私たちは彼を借りて間違いを捕まえて、それから異常な形で投げ出して、ok、次の書き方を試してみましょう.
1:エラーを異常な形で投げ出す(完全に投げ出すことができない)上記の説明から分かるように、phpのエラーは異常な形で捕獲することはできませんが、try-catchの影響範囲を拡張するために投げ出す必要があります.error_handler()メソッド、彼は何を使っているのか、彼は間違いを捕まえているので、私たちは彼を借りて間違いを捕まえて、それから異常な形で投げ出すことができます.ok、次の書き方を試してみてください.1:間違いを異常な形で投げ出す(完全に投げ出すことができません)上記の説明から、phpの中の間違いは異常な形で捕まえることができません.しかし、try-catchの影響範囲を拡張するために投げ出す必要があります.前述したset_error_handler()メソッド、彼は何を使っているのか、彼は間違いを捕まえているので、私たちは彼を借りて間違いを捕まえて、それから異常な形で投げ出して、ok、次の書き方を試してみましょう.
 1 getMessage();
14     }
15 ?>

はい、試してみると、印刷されます.
1 Division by zero zyf123

プロセス:本来は0エラーを除いてset_をトリガーしますerror_handler()、set_error_handler()では,リターンガンを殺し,エラーメッセージを異常な形で投げ出すことに相当し,エラーを異常な形で投げ出すことができる.皆さんは注意してください:このようにするのは欠点があって、set_を受けますerror_handler()関数のキャプチャレベルの制限.2:すべてのエラーをset_でキャプチャerror_handler()は、システムレベルE_をキャプチャできないエラーの一部をキャプチャできることを示している.ERROR、E_PARSEなどのエラーですが、この部分はregister_shutdown_function()キャプチャ.だから両者を組み合わせると良い機能が現れます.次の手順を見てください.
 1 a.php  :
 2 
 3     //   Fatal error  
 4     //test();
 5 
 6     //       ERROR  
 7     //trigger_error('zyf-error', E_USER_ERROR);
 8 
 9     //       
10     var_dump(23+-+);
11 
12     //   Notice  
13     //echo $f;
14 
15     //   Warning  
16     //echo '123';
17     //ob_flush();
18     //flush();
19     //header("Content-type:text/html;charset=gb2312");
20 ?>
21 b.php  :
22 
23     error_reporting(0);
24     register_shutdown_function('zyfshutdownfunc');
25     function zyfshutdownfunc()
26     {
27         if ($error = error_get_last()) {
28             var_dump('register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '');
29         }
30     }
31 
32     set_error_handler('zyferror');
33     function zyferror($type, $message, $file, $line)
34     {
35         var_dump('set_error_handler: ' . $type . ':' . $message . ' in ' . $file . ' on ' . $line . ' line .
'); 36 } 37 38 require 'a.php'; 39 ?>

これで最初のプログラムを説明できるでしょうphpは単一ファイル実行ではエラーをキャプチャできません.フレームワークで実行すればいいです.もちろん、私が上述したように拡張してもいいです.
1:カスタム例外処理
複雑なシステムでは、特殊な処理が必要な異常を自分でキャプチャする必要があります.これらの異常は、特殊な場合に投げ出される可能性があります.そこで、PHPのexceptionクラスのすべての属性を継承し、カスタム関数を追加することができます.使用するときは、以前と同じように、大まかに次のように書くことができます.
 1 getLine().' in ' . $this->getFile()
 7                 .': ' . $this->getMessage() . ' Must in (0 - 60)';
 8         }
 9     }
10 
11     $age = 10;
12     try {
13         $age = intval($age);
14         if($age > 60) {
15             throw new zyfException($age);
16         }
17 
18     } catch (zyfException $e) {
19         echo $e->errorzyfMessage();
20 
21     }
22 ?>

2:例外ネスト
異常ネストは比較的一般的な書き方であり、カスタムの異常処理ではtryブロックで複数の異常キャプチャを定義し、階層的に異常を伝達し、理解と泡の差が少なく、以下の実装を参照してください.
 1  60) {
 6             throw new zyfException($age);
 7         }
 8 
 9         if ($age <= 0) {
10             throw new Exception($age . ' must > 0');
11         }
12 
13     } catch (zyfException $e) {
14         echo $e->errorzyfMessage();
15 
16     } catch(Exception $e) {
17         echo $e->getMessage();
18     }
19 ?>

もちろんcatchで異常を上層部に投げ出すこともできます.
 1  60) {
 7                 throw new Exception($age);
 8             }
 9 
10         } catch (Exception $e) {
11             throw new zyfException($age);
12 
13         }
14 
15     } catch (zyfException $e) {
16         echo $e->errorzyfMessage();
17     }
18 ?>

 
六、PHP 7における異常処理
 
PHPを書くにはバージョンを考慮しなければならない.上の書き方はPHP 7のほとんどで実現できるが、異なる点もある.PHP 7の更新には、より多くのErrorがキャプチャ可能なExceptionになり、現在のPHP 7はグローバルなthrowableインタフェースを実現し、元の古いExceptionとその一部のErrorがこのインタフェース(interface)を実現した.PHP 7でより多くのErrorがキャプチャ可能なExceptionになってキャプチャに戻るということは、前述した拡張try-catchの影響範囲と同じですが、キャプチャしない場合はErrorに従って扱います.次の2つを見てください.
 
 1 getMessage() . ' zyf';
 7     }
 8 
 9     try {
10         test();
11 
12     } catch(Error $e) {
13         echo $e->getMessage() . ' zyf';
14     }
15 ?>

PHP 7がthrowableインタフェースを実現しているので、最初のこの方法で異常をキャプチャすることができる.また、一部のErrorがインタフェースを実装し、より多くのErrorがキャプチャ可能なExceptionになるため、第2の方法で異常をキャプチャすることができる.以下はネットで探していたPHP 7の異常階層ツリー:Throwable Exception異常...ErrorエラーArithmeticError演算エラーDivisionByZeroError除数0のエラーAssertionError宣言エラーParseError解析エラーTypeErrorタイプエラー