สร้างกลยุทธ์จัดการข้อผิดพลาดที่สมบูรณ์: business exception ที่ถูกร้อยต่อกัน finally ที่ควบคุมได้ และตัวจัดการระดับโกลบอลในฐานะแนวป้องกันสุดท้าย
เปิดบทเรียนนี้ใน Kodokonตั้งแต่ PHP 7 เป็นต้นมา ทุกสิ่งที่ถูกโยนออกมาจะนำ Throwable ไปใช้ โดยมีสองสาขา ได้แก่ Error (ความล้มเหลวของเอนจินและการกำหนดชนิด เช่น TypeError) และ Exception (ความล้มเหลวของแอปพลิเคชัน) คุณจะจับ Error เฉพาะที่ขอบเขตของโปรแกรมเท่านั้น ในฝั่งแอปพลิเคชัน SPL จัดเตรียมลำดับชั้นสำเร็จรูปมาให้: LogicException ส่งสัญญาณว่าเป็นบั๊กของนักพัฒนา ที่ต้องแก้ไข ส่วน RuntimeException เป็นเหตุการณ์ไม่คาดคิดขณะรัน ที่ต้องจัดการ ปฏิกิริยาแบบมืออาชีพคือ จงสืบทอด business exception ของคุณจากฐานเหล่านี้ หนึ่งตัวต่อหนึ่งสถานการณ์ที่ระบุได้
<?php
declare(strict_types=1);
abstract class AppException extends RuntimeException
{
}
final class UserNotFound extends AppException
{
public static function withEmail(
string $email
): self {
return new self("No user for $email");
}
}
try {
throw UserNotFound::withEmail('lea@example.com');
} catch (UserNotFound $e) {
echo $e->getMessage();
}try/catch จะคุ้มค่าก็ต่อเมื่อคุณรู้ว่าจะจัดการกับข้อผิดพลาดอย่างไร มิฉะนั้นก็ปล่อยให้มันลอยขึ้นไปข้างบน มีเครื่องมือสามอย่างที่ช่วยปรับการควบคุมให้ละเอียดขึ้น: multi-catch catch (JsonException | ValueError $e) จัดกลุ่มการจัดการที่เหมือนกันเข้าด้วยกัน พารามิเตอร์ previous ร้อยต่อ สาเหตุดั้งเดิมเข้าไว้ เมื่อคุณแปลง exception เชิงเทคนิคให้เป็นเชิงธุรกิจ ส่วน finally ทำงานในทุกกรณี ทั้งสำเร็จ เกิด exception หรือแม้แต่ return ก่อนเวลา เหมาะอย่างยิ่งสำหรับการปลดปล่อยทรัพยากร
<?php
declare(strict_types=1);
final class PayloadInvalid extends RuntimeException
{
}
function decode(string $json): mixed
{
try {
return json_decode(
$json,
true,
flags: JSON_THROW_ON_ERROR,
);
} catch (JsonException $e) {
throw new PayloadInvalid(
'Invalid JSON payload',
previous: $e,
);
} finally {
echo "decode() finished\n";
}
}
try {
decode('{broken');
} catch (PayloadInvalid $e) {
echo $e->getPrevious()?->getMessage();
}ที่ปลายสุดของสายโซ่ ตัวจัดการระดับโกลบอลสองตัวช่วยปิดฉากการตั้งค่านี้ set_exception_handler จับทุกสิ่งที่ไม่ได้ถูกจัดการ: บันทึกล็อกอย่างมีโครงสร้าง ตอบกลับด้วย 500 แบบทั่วไป โดยไม่เปิดเผยข้อความหรือ trace ให้ผู้ใช้เห็นเลย ส่วน set_error_handler แปลงข้อผิดพลาดยุคเก่า (warning, notice) ให้เป็น ErrorException ทำให้สองโลกเป็นหนึ่งเดียว: ทุกอย่างกลายเป็น exception ทุกอย่างเดินตามเส้นทางการจัดการเดียวกัน
<?php
declare(strict_types=1);
set_error_handler(function (
int $severity,
string $message,
string $file,
int $line,
): bool {
throw new ErrorException(
$message,
0,
$severity,
$file,
$line,
);
});
set_exception_handler(function (Throwable $e): void {
error_log($e->getMessage());
http_response_code(500);
echo 'A technical error occurred.';
});
trigger_error('Legacy warning', E_USER_WARNING);
echo 'Never reached';Error และ Exception มีอะไรเหมือนกันใน PHP สมัยใหม่?Error สืบทอดจาก ExceptionThrowable ไปใช้finally ทำงานเมื่อใด?previous ของ exception มีไว้เพื่ออะไร?