Hàm php nào trả về thông báo lỗi liên quan đến ngoại lệ?

Vì những lý do lịch sử, các hàm cốt lõi của PHP trả về false khi không thành công thay vì đưa ra các ngoại lệ. Hãy xem làm thế nào chúng ta có thể khắc phục điều này

TL;DR

Gần đây chúng tôi đã viết một thư viện PHP mới. Nó được gọi là an toàn. Nó bao bọc tất cả các hàm lõi PHP trả về false khi lỗi thành một hàm khác đưa ra một ngoại lệ

Dự án hiện đang trong giai đoạn thử nghiệm và chúng tôi cần phản hồi

Giới thiệu

Tại TheCodingMachine, chúng tôi rất hâm mộ PHPStan. PHPStan là một công cụ phân tích tĩnh mã nguồn mở cho mã PHP của bạn. Nó giúp bạn tìm lỗi trước khi chương trình của bạn chạy. Chúng tôi thực sự rất hâm mộ PHPStan nên chúng tôi rất vui khi được tài trợ cho dự án

PHPStan có khái niệm về "cấp độ" này và chúng tôi cố gắng trên mỗi dự án của mình để đạt được "cấp độ 7" (cấp độ tối đa). Nhưng PHPStan không ngừng cải thiện và việc đạt đến cấp 7 ngày càng khó hơn khi công cụ trở nên nghiêm ngặt hơn (đây là một điều tốt. )

Nhìn vào mã dưới đây

$content = file_get_contents('foobar.json');
$foobar = json_decode($content);

Nó có vẻ hợp pháp, phải không?

bên phải?

Với việc phát hành PHPStan 0. 10, một séc mới đã được thêm vào. Đoạn mã dưới đây sẽ đưa ra cảnh báo

Tham số #1 $json của hàm json_decode mong đợi chuỗi, chuỗi. đưa ra sai

Cái gì?

Hóa ra PHPStan hoàn toàn đúng

Hàm

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
8 có thể trả về
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
9 (nội dung của tệp) hoặc
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
0 nếu xảy ra lỗi. Và mã của chúng tôi không kiểm tra xem
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
8 có thành công hay không. Rất nhiều thứ có thể đi sai

  • Tệp không thể tồn tại
  • hoặc có thể người dùng không có quyền truy cập tệp
  • hoặc đĩa có thể bị hỏng
  • ...

Vì vậy chúng ta cần kiểm tra lỗi. Mã "cố định" trông như thế này

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}

Mã này đúng, nhưng nó chắc chắn khó đọc hơn. Chúng tôi đã mất rất nhiều biểu cảm

Một chút về lịch sử

Tại thời điểm này, bạn có thể thắc mắc tại sao các hàm PHP cốt lõi (như

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
8 hoặc
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
2) không đưa ra ngoại lệ thay vì trả về
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
0 hoặc
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
4 khi có lỗi

Điều này quay trở lại gốc rễ của PHP. Tôi không thể tìm thấy cuốn sách lịch sử PHP, nhưng tôi tin rằng hầu hết các chức năng cốt lõi của PHP đã được thêm vào PHP trước khi hỗ trợ ngoại lệ được thêm vào ngôn ngữ. Vì vậy, hầu hết các hàm PHP đang sử dụng trình xử lý lỗi PHP cũ (thông báo lỗi với

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
5,
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
6. ) thay vì các ngoại lệ (mạnh mẽ hơn)

Thật thú vị khi thấy rằng các nhà phát triển PHP cốt lõi đang làm những gì có thể để khắc phục sự cố, trong khi vẫn duy trì khả năng tương thích ngược. Chẳng hạn, trong PHP 7. 3, hàm

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
2 sẽ chấp nhận một tùy chọn JSON_THROW_ON_ERROR mới sẽ cho phép
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
2 đưa ra một ngoại lệ thay vì trả về
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
4 do lỗi

// In PHP 7.3+
$json = json_decode($jsonString, $assoc = true, $depth = 512, JSON_THROW_ON_ERROR);

Vấn đề là các nhà phát triển lõi PHP phải duy trì khả năng tương thích ngược, do đó, hành vi "đúng" phải là tùy chọn. Nếu bạn quên vượt qua

// In PHP 7.3+
$json = json_decode($jsonString, $assoc = true, $depth = 512, JSON_THROW_ON_ERROR);
0, bạn sẽ quay lại hành vi cũ "return null on error"

Vì vậy, chúng tôi không thể hy vọng quá nhiều vào các chức năng cốt lõi của PHP sẽ thay đổi hoàn toàn hành vi của chúng trong các bản phát hành tiếp theo (đây thực sự là một điều tốt vì khả năng tương thích ngược rất quan trọng đối với một ngôn ngữ trưởng thành như PHP)

Vì vậy, hãy xem các giải pháp về đất của người dùng

Nhóm Guzzle đã từng xử lý khá nhiều mã hóa và giải mã JSON. Để giảm bớt khó khăn, họ đã viết 2 hàm bao hàm cho

// In PHP 7.3+
$json = json_decode($jsonString, $assoc = true, $depth = 512, JSON_THROW_ON_ERROR);
1 và
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
2

Mã trông như thế này

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
3

Ok, vậy là họ đang tạo một hàm

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
2 có tên và chữ ký giống hệt như hàm
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
2 cốt lõi, ngoại trừ nó nằm trong một không gian tên. Nó gọi hàm
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
2 cốt lõi và sẽ đưa ra một ngoại lệ nếu xảy ra lỗi

Điều đó có nghĩa là trong mã của tôi, tôi chỉ cần thêm một câu lệnh

// In PHP 7.3+
$json = json_decode($jsonString, $assoc = true, $depth = 512, JSON_THROW_ON_ERROR);
6 và tôi sẽ sử dụng phiên bản "GuzzleHttp"

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
8

tiếng kêu

Chúng tôi ngay lập tức trở nên yêu thích việc sử dụng chức năng trợ giúp nhỏ này giúp cuộc sống của chúng tôi trở nên đơn giản hơn rất nhiều. Guzzle nhà phát triển rock

Nhưng Guzzle chỉ cung cấp trình bao bọc cho

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
2 và
// In PHP 7.3+
$json = json_decode($jsonString, $assoc = true, $depth = 512, JSON_THROW_ON_ERROR);
1. Còn rất nhiều hàm PHP khác vẫn trả về
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
0 (như
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
30,
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
31,
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
8,
$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
33,. )?

Bắt đầu dự án Safe-PHP

Đây là nơi chúng tôi quyết định bắt đầu dự án "Safe-PHP"

ý tưởng thật đơn giản. gói mọi chức năng lõi PHP đơn lẻ trả về "false" khi thất bại trong một chức năng thay vào đó đưa ra một ngoại lệ

Với "Safe-PHP", bây giờ bạn có thể viết

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
4

Nếu xảy ra sự cố, một ngoại lệ sẽ được đưa ra, vì vậy không có rủi ro bạn sẽ chuyển các giá trị "false" xuống đường ống. Mã sẽ dừng ở dòng xảy ra sự cố, điều này giúp cho việc gỡ lỗi waaaaaay dễ dàng hơn

Và bạn có để ý không?

Mã của bạn không thực sự thay đổi. Vấn đề chỉ là thêm một vài câu lệnh "sử dụng chức năng" ở đầu tệp của bạn

Phát hiện các chức năng "không an toàn"

Vẫn còn một vấn đề. Các chức năng "an toàn" có sẵn, nhưng điều này có nghĩa là các nhà phát triển sẽ nghĩ đến việc sử dụng chúng?

Rõ ràng là không

Tôi là một trong những nhà phát triển của Safe-PHP và tôi không tin tưởng vào việc sử dụng biến thể An toàn của một chức năng trong mỗi trang của dự án

Vì vậy, chúng tôi cần một cách tự động để cho chúng tôi biết rằng chúng tôi nên sử dụng biến thể "an toàn" của hàm

Và đây là lúc PHPStan phát huy tác dụng. Tôi đã nói với bạn về PHPStan chưa? . Tất nhiên tôi đã làm. . )

Vì vậy, chúng tôi đã viết một tiện ích mở rộng PHPStan để phân tích cú pháp mã của bạn và sẽ cho bạn biết nếu bạn đang sử dụng một biến thể không an toàn. Thông báo PHPStan sẽ như thế này

Hàm json_encode không an toàn khi sử dụng. Nó có thể trả về FALSE thay vì ném một ngoại lệ. Vui lòng thêm 'sử dụng chức năng Safe\json_encode;'

Sử dụng quy tắc PHPStan, bây giờ, tôi biết rằng tôi sẽ không quên sử dụng biến thể "an toàn" của bất kỳ chức năng không an toàn nào ngoài kia. cái này hay đấy

Làm thế nào nó hoạt động?

Nếu bạn đang thắc mắc về các chi tiết thực chất, đây là một số thông tin khác

Có một số lượng lớn các hàm trong PHP và rất nhiều trong số chúng trả về false khi lỗi. Chúng tôi cần phát hiện những thứ đó nhưng quá lười để đọc toàn bộ tài liệu. Vì vậy, chúng tôi quyết định phân tích cú pháp tài liệu

Chúng tôi đã tìm nạp tài liệu từ kho lưu trữ tài liệu SVN và chúng tôi bắt đầu quét các tệp. Hy vọng rằng tài liệu này ở định dạng XML và chủ yếu dựa vào các thực thể XML

Điều này thật tuyệt vì thay vì viết "return false for failure" trong tài liệu XML, họ sử dụng phím tắt

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
34

Và nó (gần như) nhất quán trong toàn bộ tài liệu. Do đó, không quá khó để phân tích toàn bộ tài liệu cho chuỗi này

Kết quả?

930 chức năng để bọc, đó là rất nhiều công việc. Thay vì thực hiện việc này theo cách thủ công, chúng tôi đã viết một trình tạo mã PHP để phân tích cú pháp các tài liệu XML và tạo trình bao bọc từ tài liệu. Tài liệu PHP được kết hợp với dữ liệu chính xác hơn từ PHPStan về các loại đối số và kiểu trả về có thể có

Bản thân dữ liệu PHPStan đến từ một bộ phân tích tĩnh khác có tên là Phan. Vì vậy, đó thực sự là mã nguồn mở tốt nhất. )

Trong quá trình viết trình tạo mã này, tôi tình cờ phát hiện ra một số vấn đề với tài liệu PHP và đã gửi các bản vá đã được chấp nhận nhanh chóng. Vì vậy, bây giờ tôi tự hào là người đóng góp cho tài liệu PHP. Ừ

Tôi cũng tình cờ gặp phải một vấn đề về hiệu suất của PHPStorm đã được những người trong PHPStorm khắc phục nhanh chóng. vâng một lần nữa

Nó có hoạt động không?

vâng nó làm. Tuy nhiên, dự án hiện đang trong giai đoạn thử nghiệm và cần nhiều thử nghiệm, vì vậy đừng ngần ngại mở các vấn đề / yêu cầu kéo

Những gì còn lại để được thực hiện

Chúng tôi dự định thêm hỗ trợ cho các đối tượng lõi PHP (như

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
35. ) nếu dự án thành công

lựa chọn thay thế

Có một số lựa chọn thay thế cho "an toàn"

Sử dụng các gói cấp cao

Đầu tiên, một số gói bao gồm các lệnh gọi hàm gốc nhưng cũng thêm một số chức năng. Chẳng hạn, nếu bạn đang thực hiện nhiều thao tác với hệ thống tệp, tôi thực sự khuyên bạn nên sử dụng một trong

Ngoài ra còn có nette/utils bao bọc hầu hết các chức năng cốt lõi của PHP trong khi thay đổi một chút chữ ký của chúng

Có rất nhiều gói PHP chất lượng cao đang bổ sung rất nhiều giá trị cho các chức năng cốt lõi của hàng PHP. Sử dụng chúng

Sử dụng xử lý lỗi nghiêm ngặt

PHP đi kèm với trình xử lý lỗi có thể tùy chỉnh. Hầu hết các hàm trả về

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
0 khi lỗi cũng sẽ đưa ra cảnh báo. Thay vào đó, bạn có thể nhận cảnh báo và đưa ra một ngoại lệ bằng trình xử lý lỗi tùy chỉnh

$content = file_get_contents('foobar.json');
if ($content === false) {
    throw new FileLoadingException('Could not load file foobar.json');
}
$foobar = json_decode($content);
if ($foobar === null) {
    throw new FileLoadingException('foobar.json does not contain valid JSON: '.json_last_error());
}
7

Giải pháp này rất hay và bạn nên sử dụng nó bằng mọi cách

Tuy nhiên, nó có 2 nhược điểm

  1. hành vi mã của bạn bây giờ phụ thuộc vào trình xử lý lỗi không được khai báo trực tiếp trong mã của bạn. Nếu bạn đang viết mã PHP để sử dụng lại trong các dự án khác nhau, bạn không có gì đảm bảo rằng một trình xử lý lỗi nghiêm ngặt
  2. các trình phân tích tĩnh như PHPStan không có kiến ​​thức về trình xử lý lỗi toàn cầu nên bạn sẽ phải định cấu hình chúng theo cách thoải mái hơn. Bộ phân tích càng ít nghiêm ngặt, càng có nhiều vấn đề thoát ra

Điều đó không có nghĩa là bạn không nên sử dụng phương pháp này. Tôi rất tin rằng bạn nên sử dụng nó và ngoài ra còn sử dụng "An toàn"

phản hồi chào mừng

Quá trình phát triển "an toàn" vừa mới bắt đầu và chắc chắn có một số nhận xét cần được thực hiện và một số lỗi cần được tìm và sửa

Hơn bất cứ điều gì, chúng tôi sẽ đánh giá cao một số phản hồi, vì vậy đừng ngần ngại

Cuối cùng, nếu bạn quan tâm đến tin tức liên quan đến An toàn, bạn có thể theo dõi tôi trên Twitter

Thông tin về các Tác giả

David là CTO và đồng sáng lập của TheCodingMachine và WorkAdventure. Anh ấy là đồng biên tập của PSR-11, tiêu chuẩn cung cấp khả năng tương tác giữa các hộp chứa tiêm phụ thuộc. Anh ấy cũng là nhà phát triển hàng đầu của GraphQLite, một thư viện PHP không phụ thuộc vào khung để triển khai API GraphQL một cách dễ dàng

Hàm nào trả về thông báo lỗi trong PHP?

Theo mặc định, PHP gửi nhật ký lỗi tới hệ thống ghi nhật ký của máy chủ hoặc một tệp, tùy thuộc vào cách đặt cấu hình error_log trong php. tập tin ini. Bằng cách sử dụng chức năng error_log() , bạn có thể gửi nhật ký lỗi tới một tệp được chỉ định hoặc một đích đến từ xa.

Làm cách nào để lấy mã lỗi từ ngoại lệ trong PHP?

Ngoại lệ PHP Phương thức getCode() .

Lỗi và ngoại lệ trong PHP là gì?

Ngoại lệ là đối tượng mô tả lỗi hoặc hành vi không mong muốn của tập lệnh PHP . Các ngoại lệ được đưa ra bởi nhiều hàm và lớp PHP. Các hàm và lớp do người dùng định nghĩa cũng có thể đưa ra các ngoại lệ. Ngoại lệ là một cách hay để dừng một chức năng khi gặp dữ liệu mà nó không thể sử dụng.

Điều nào sau đây là một ngoại lệ trong PHP?

Giải trình. Nó cung cấp 13 trường hợp ngoại lệ. BadFunctionCallException, BadMethodCallException, DomainException, UnlimitedArgumentException, LengthException, LogicException, OutOfBoundsException, OutOfRangeException, OverflowException, RangeException, RuntimeException, UnderflowException, UnexpectedValueException