PHP7語言基礎——錯誤和異常處理

常見的錯誤和異常

  1. 拼寫錯誤
    PHP中常量和變量都是區分大小寫的,如果將常量或變量名寫錯,將會得到一個**Notice**: Undefined variable:提示。
    PHP中的函數名、方法名、類名是不區分大小寫的,但是建議使用與定義時相同的名字。
    PHP中的魔術常量不區分大小寫,但是建議全部大寫。
    另外,在編寫代碼過程中,由於輸入法的原因,還容易導小括號,分號,引號等出現錯誤,因此要特別小心。

  2. 單引號和雙引號混用
    單引號、雙引號在PHP中沒有特殊區別,都可以用來創建字符串。但必須成對兒出現,另外缺少單引號或者雙引號也是經常出現的問題。系統會拋出一個**Parse error**: syntax error,錯誤。

  3. 括號使用混亂
    當編寫的代碼括號的層次較多時容易出現缺少反括號的錯誤。系統會拋出一個**Parse error**: syntax error錯誤

  4. 相等運算符與賦值符號混淆
    在進行相等比較的時候容易產生此類錯誤。此類錯誤多數情況下不會拋出異常,但是會導致程序功能與開發者意圖相悖。

  5. 缺少美元符號
    在PHP中,設置變量時需要使用美元符號,如果不添加美元符號就會引起解析錯誤,**Parse error**: syntax error

  6. 調用不存在的常量和變量
    調用沒有聲明的變量或常量,將會觸發**NOTICE: Undefined variable**錯誤。

  7. 調用不存在的文件
    調用不存在的文件,將會觸發**Warning**錯誤。如:

    Warning: include(mybook.php): failed to open stream: No such file or directory 
    Warning: include(): Failed opening 'mybook.php' for inclusion (include_path='.;C:\php\pear') in
    
  8. 環境配置錯誤
    環境配置不當也會給運行帶來錯誤,例如PHP配置文件和PHP的版本等,如果配置不正確,就會提示文件無法打開、操作權限不具備和服務器無法連接等錯誤信息。

錯誤處理

常見的錯誤處理方法包括使用錯誤處理機制、使用DIE語句調試、自定義錯誤和錯誤觸發器等。

php.ini中的錯誤處理機制

php.ini文件規定了錯誤的顯示方式,包括配置選項的名稱、默認值和表述的含義等。常見的錯誤配置選項內容如下:

名稱 默認值 含義
display_errors On 設置錯誤作爲PHP的一部分輸出。開發的過程中可以採用默認的設置,但爲了安全考慮,在生產環境中還是應設置爲Off。
error_reporting E_ALL 這個設置會顯示所有的出錯信息。這種設置會讓一些無害的提示也顯示,所以可以設置error_reporting的默認值爲error_reporting=E_ALL&~E_NOTICE,這樣只會顯示錯誤和不良編碼
error_log null 設置記錄錯誤日誌的文件。默認情況下降錯誤發送到web服務器日誌,用戶也可以指定寫入的文件
html_errors On 控制是否在錯誤信息中採用HTML格式
log_errors Off 控制是否應該將錯誤發送到主機服務器的日誌文件
display_startup_errors Off 控制是否顯示PHP啓動時的錯誤
track_errors Off 設置是否保存最近一個警告或錯誤消息

應用DIE語句調試

使用DIE語句調試的優勢是,不僅可以顯示錯誤的位置,還可以輸出錯誤信息。一點出現錯誤,程序會終止運行,並在瀏覽器上顯示出出錯之前的信息和錯誤信息。

【示例】

<?php
echo "這是程序出錯之前的信息<br />";
if (!file_exists("test.txt")) {
	die("文件不存在");
} else {
	$file=fopen("test.txt");
}
echo "<br />這是程序出錯之後的信息";
?>

程序輸出結果:

在這裏插入圖片描述

可以看到,使用DIE語句調試在錯誤之後便終止了腳本執行。

自定義錯誤和錯誤觸發器

用戶可以通過創建專用函數error_function(),來在PHP發生錯誤時調用該函數。該函數的語法格式如下:

error_function(error_level, error_message, error_file, error_line, error_context)

創建該函數必須至少包含error_level和error_message兩個參數,函數各參數的含義如下:

  • error_level:必需參數,爲用戶定義的錯誤定義錯誤報告級別,必須是一個整數。
  • error_message:必需參數,爲用戶定義的錯誤定義錯誤消息。
  • error_file:可選參數,定義錯誤在其中發生的文件名
  • error_line:可選參數,定義錯誤發生的行號
  • error_context:可選參數,定義一個數組,包含當錯誤發生時在用的每個變量以及它們的值。

error_level的值和含義如下表:

數值 常量 含義
2 E_WARNING 非致命的run-time錯誤,不暫停腳本執行
8 E_NOTICE Run-time通知,腳本發現可能有錯誤發生,但也能在腳本正常運行時發生
256 E_USER_ERROR 致命的用戶生成的錯誤,類似於程序員使用PHP函數trigger_error()設置的E_ERROR
512 E_USER_WARNING 非致命的用戶生成的警告,類似於程序員使用PHP函數trigger_error()設置的E_WARNING
1024 E_USER_NOTICE 用戶生成的通知,類似於程序員使用PHP函數triger_error()設置的E_NOTICE
4096 E_RECOVERABLE_ERROR 可捕獲的執行錯誤,類似E_ERROR,但可被用戶定義的處理程序捕獲
8191 E_ALL 所有錯誤和警告

【示例】

<?php
// 首先自定義錯誤處理函數
function customError($errno, $errstr) {
	echo "<b>錯誤:<b> [$errno] $errstr<br />";
	echo "終止程序";
	die();
}
// 然後使用set_error_handler()來設置自定義錯誤處理函數
// set_error_handler(error_function, error_types)
// error_types參數可選,默認爲E_ALL
set_error_handler("customError");

// 觸發自定義錯誤函數
echo($test);
?>

程序輸出結果:

在這裏插入圖片描述

在腳本中用戶輸入數據的位置,當用戶輸入無效時觸發錯誤。在PHP中,這個任務由trigger_error()完成。

trigger_error()函數創建用戶自定義的錯誤消息。用於在用戶指定的條件下觸發一個錯誤消息。它與內建的錯誤處理器一同使用,也可以由set_error_handler()函數創建的用戶自定義函數一起使用。如果指定了一個不合法的錯誤類型,該函數返回false,否則返回true。

trigger_error()函數的具體語法格式如下:

trigger_error(error_message, error_types)

其中,error_message參數爲必需參數,定義錯誤消息,長度限制爲1024個字符。error_types爲可選參數,定義錯誤消息的錯誤類型,可能的值爲E_USER_ERROR、E_USER_WARNING、E_USER_NOTICE。

【示例】

<?php
$test = 5;
if ($test > 4) {
	// 創建自定義錯誤消息
	trigger_error("value must be 4 or below");
}
?>

程序輸出結果:

在這裏插入圖片描述

【示例2】

<?php
// 定義錯誤函數
function customError($errno, $errstr) {
	echo "<b>錯誤:</b> [$errno] $errstr";
}

// 設置錯誤處理函數
set_error_handler("customError", E_USER_WARNING);

// trigger_error函數
$test = 5;
if ($test > 4) {
	trigger_error("Value must be 4 or below", E_USER_WARNING);
}
?>

程序輸出結果:

在這裏插入圖片描述

錯誤記錄

默認情況下,根據php.ini中的error_log配置,PHP向服務器的錯誤記錄系統或文件發送錯誤記錄。通過使用error_log()函數,用戶可以向指定的文件或遠程目的地發送錯誤記錄。

error_log()函數語法格式如下:

error_log(string $message[, int $message_type=0[, string $destination[, string $extra_headers]]]):bool

函數參數說明:

  • message:應該被記錄的錯誤消息。
  • message_type:設置錯誤應該發送到何處。可能有以下信息類型:
    • 0:發送到PHP的系統日誌,使用操作系統的日誌機制或者一個文件,取決於error_log指令設置了什麼。這是默認選項。
    • 1:發送到參數destination設置的郵件地址。第四個參數extra_headers只有在這個類型裏纔會被用到。
    • 2:不再是一個選項
    • 3:被髮送到位置爲destination的文件裏。字符message不會默認被當做新的一行。
    • 4:直接發送到SAPI的日誌處理程序中。
  • destination:目標。它的含義描述於以上,由message_type參數所決定。
  • extra_headers:額外的頭。當message_type設置爲1時使用,該信息類型使用了mail()的同一個內置函數。

警告:error_log()並非二進制安全的。null字符可能截斷。

【示例】

<?php
// 定義錯誤函數
function customError($errno, $errstr) {
	echo "<b>錯誤:</b> [$errno] $errstr<br />";
	echo "錯誤記錄已經發送完畢";
	error_log(" 錯 誤:[$errno] $errstr", 1, "[email protected]", "From:[email protected]");
}

// 設置錯誤處理函數
set_error_handler("customError", E_USER_WARNING);

// trigger_error函數
$test = 5;
if ($test > 4) {
	trigger_error("Value must be 4 or below", E_USER_WARNING);
}
?>

程序輸出結果:

在這裏插入圖片描述

同時,在指定的[email protected]郵箱中也將收到同樣的信息。

異常處理

異常和錯誤不同,異常(Exception)用於在指定的錯誤發生時改變腳本的正常流程。用戶可以捕獲異常並做出相應處理。

異常類

PHP提供了一個異常類Exception,是所有異常類的基類。

Exception {
	/* 屬性 */
	protected string $message;
	protected int $code;
	protected string $file;
	protected int $line;
	
	/* 方法 */
	public __construct([string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]])
	final public getMessage( void ): string
	final public getPrevious( void ): Throwable
	final public getCode( void ): int
	final public getFile( void ): string
	final public getLine( void ): int
	final public getTrace( void ): array
	final public getTraceAsString( void ): string
	public __toString( void ): string
	final private __clone( void ): void
}

關於該類中屬性和方法的說明如下:

屬性:

  • message:異常消息內容
  • code:異常代碼
  • file:拋出異常的文件名
  • 拋出異常在該文件中的行號

方法:

  • Exeption::__construct:異常構造函數
  • Exeption::getMessage:獲取異常消息內容
  • Exeption::getPrevious:返回異常鏈中的前一個異常
  • Exeption::getCode:獲取異常代碼
  • Exeption::getFile:創建異常時的程序文件名稱
  • Exeption::getLine:獲取創建的異常所在文件中的行號
  • Exeption::getTrace:獲取異常追蹤信息
  • Exeption::getTraceAsString:獲取字符串類型的異常追蹤信息
  • Exeption::__toString:將異常對象轉換爲字符串
  • Exeption::clone:異常克隆

【示例】

<?php
// 設置錯誤級別爲0,不報錯
error_reporting(0);
function theDatabaseObj() {
	$mysql = mysqli_connect('127.0.0.1', 'test', 'test', '3306');
	if($mysql) {
		return $mysql;
	} else {
		throw new Exception("Could not connect to the database");
	}
}

function db() {
	try {
		$db = theDatabaseObj();
		var_dump($db);
	} catch (Exception $e) {
		echo $e -> getMessage();
	}
}

db();
?>

程序輸出結果:

Could not connect to the database

異常的基本處理方法

當異常被觸發時,通常會發生以下動作:

  • 當前代碼狀態被保存
  • 代碼執行被切換到預定義的異常處理器函數。
  • 根據情況,處理器也許會從保存的代碼狀態重新開始執行代碼,終止腳本執行,或從代碼中另外的位置繼續執行。

當異常被拋出時,PHP會嘗試查找匹配的catch代碼塊。如果異常沒有捕獲,而且又沒有使用set_exception_handler()做相應的處理,就將發生一個嚴重的錯誤,並且輸出Uncaught Exception(未捕獲異常)的錯誤消息。

處理異常的程序通常應當包含如下幾部分:

  • try代碼塊:可能拋出異常的代碼應該位於try代碼塊內。若沒有觸發異常,則代碼將照常繼續執行。
  • throw代碼塊:這裏定義如何觸發異常。每一個throw必須對應至少一個catch。
  • catch代碼塊:catch代碼塊會捕獲異常,並創建一個包含異常信息的對象。

自定義的異常處理器

在PHP裏遇到任何錯誤都會拋出一個錯誤,很少會主動拋出異常。PHP的異常處理機制並不完善,在PHP中先處理不可預料的異常是辦不到的,我們必須事先定義一些異常,將各種可能出現的異常進行if…else判斷,手動拋出異常。自定義異常類的方式如下:

<?php
// 首先自定義異常類,繼承Exception
class MyException extends Exception {
	public function errorMessage() {
		// 錯誤消息
		$errorMsg = '異常發生的行:'. $this->getLine() . ' in ' . $this->getFile() . ':<b>' . $this->getMessage(). '</b>不是一個有效的郵箱地址';
		return $errorMsg;
	}
}

// 在實際的業務代碼中拋出自定義異常
$email = "[email protected]";
// 將可能拋出異常的代碼放入try中
try {
	// 檢查是否符合條件
	if (filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
		// 如果郵件地址無效,則拋出異常
		throw new MyException($email);
	}
} catch (MyException $e) {
	// 顯示自定義的消息
	echo $e->errorMessage();
}
?>

程序輸出結果:

在這裏插入圖片描述

處理多個異常

可以使用多個if…else代碼塊,或switch代碼塊,或者嵌套多個異常,使用不同的異常類,並返回不同的錯誤消息。

【示例】

<?php
// 首先自定義異常類,繼承Exception
class MyException extends Exception {
	public function errorMessage() {
		// 錯誤消息
		$errorMsg = '異常發生的行:'. $this->getLine() . ' in ' . $this->getFile() . ':<b>' . $this->getMessage(). '</b>不是一個有效的郵箱地址';
		return $errorMsg;
	}
}

// 在實際的業務代碼中拋出自定義異常
$email = "[email protected]";
// 將可能拋出異常的代碼放入try中
try {
	// 檢查是否符合條件
	if (filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
		// 如果郵件地址無效,則拋出異常
		throw new MyException($email);
	}
	// 檢查郵箱是否是雅虎郵箱
	if (strpos($email, "yahoo") !== FALSE) {
		throw new Exception("$email 是一個雅虎郵箱");
	}
} catch (MyException $e) {
	// 顯示自定義的消息
	echo $e->errorMessage();
} catch (Exception $e) {
	echo $e->getMessage();
}
?>

程序輸出結果:

[email protected] 是一個雅虎郵箱

設置頂層異常處理器

所有未捕獲的異常都可以通過頂層異常處理器來處理。頂層異常處理器使用set_exception_handler()函數實現。

該函數用於創建運行期間用戶自己的異常處理方法。該函數會返回舊的異常處理程序,若失敗,則返回null。該函數的語法格式如下:

set_exception_handler(callable $exception_handler):callable

設置默認的異常處理程序,用於沒有用 try/catch 塊來捕獲的異常。 在 exception_handler 調用後異常會中止。

參數:

  • exception_handler:當一個未捕獲的異常發生時所調用的函數的名稱。該處理函數需要接受一個參數,該參數是一個拋出的異常對象。

【示例】

<?php
// 定義頂層的異常處理程序
function myException($exception) {
	echo "<b>異常是:</b>", $exception->getMessage();
}

set_exception_handler('myException');
throw new Exception('正在處理未捕獲的異常');  // 拋出異常信息
?>

程序輸出結果:

在這裏插入圖片描述

PHP7中的錯誤處理

PHP7改變了大多數錯誤的報告方式。不同於傳統(PHP5)的錯誤報告機制,現在大多數錯誤都被作爲Error異常自動拋出,而不必將錯誤看作異常拋出。

這種Error異常可以像Exception異常一樣被第一個匹配的try/catch塊捕獲。Error類並非繼承自Excepiton類,所以不能用catch(Exception $e)來捕獲Error,而是用catch(Error $e)來捕獲。

如下代碼自動捕獲了一個致命錯誤:

try{
    $a = new cat();
}catch(Error $e){
    echo 'error msg:' . $e->getMessage() .' error line:' . $e->getLine();
}

以上這種形式的錯誤處理只能在PHP7中可用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章