PHP untuk pemula. Menangani kesalahan

gambar

Hanya orang yang tidak melakukan apa pun yang tidak melakukan kesalahan, dan kami adalah contohnya - kami duduk dan bekerja tanpa lelah, baca Habr :)

Pada artikel ini, saya akan memimpin cerita saya tentang kesalahan dalam PHP, dan bagaimana cara mengekangnya.

Kesalahan


Varietas dalam keluarga kesalahan


Sebelum menjinakkan kesalahan, saya akan merekomendasikan untuk mempelajari setiap spesies dan secara terpisah memperhatikan perwakilan yang paling menonjol.

Untuk mencegah satu kesalahan tidak diketahui, Anda harus mengaktifkan pelacakan semua kesalahan menggunakan fungsi error_reporting () , dan menggunakan arahan display_errors untuk mengaktifkannya:

<?php error_reporting(E_ALL); ini_set('display_errors', 1); 

Kesalahan fatal


Jenis kesalahan yang paling hebat adalah fatal, mereka dapat terjadi baik selama kompilasi dan selama karya parser atau skrip PHP, sementara skrip terputus.

E_PARSE

Kesalahan ini muncul ketika Anda membuat kesalahan sintaksis dan penerjemah PHP tidak mengerti apa yang Anda inginkan darinya, misalnya, jika Anda tidak menutup curly atau tanda kurung:

 <?php /** * Parse error: syntax error, unexpected end of file */ { 

Atau mereka menulis dalam bahasa yang tidak bisa dimengerti:

 <?php /** * Parse error: syntax error, unexpected '...' (T_STRING) */     

Kurung tambahan juga terjadi, dan tidak begitu penting bulat atau keriting:

 <?php /** * Parse error: syntax error, unexpected '}' */ } 

Saya perhatikan satu poin penting - kode file di mana Anda membuat kesalahan parse tidak akan dieksekusi, oleh karena itu, jika Anda mencoba mengaktifkan tampilan kesalahan dalam file yang sama di mana kesalahan parser terjadi, ini tidak akan berfungsi:

 <?php //     error_reporting(E_ALL); ini_set('display_errors', 1); // ..     

E_ERROR

Kesalahan ini muncul ketika PHP mengerti apa yang Anda inginkan, tetapi ini tidak berhasil karena sejumlah alasan. Kesalahan ini juga mengganggu pelaksanaan skrip, dan kode akan berfungsi sebelum kesalahan muncul:

Pengaya tidak ditemukan:

 /** * Fatal error: require_once(): Failed opening required 'not-exists.php' * (include_path='.:/usr/share/php:/usr/share/pear') */ require_once 'not-exists.php'; 

Pengecualian dilemparkan (binatang seperti apa, saya akan beri tahu nanti), tetapi tidak diproses:

 /** * Fatal error: Uncaught exception 'Exception' */ throw new Exception(); 

Saat mencoba memanggil metode kelas yang tidak ada:

 /** * Fatal error: Call to undefined method stdClass::notExists() */ $stdClass = new stdClass(); $stdClass->notExists(); 

Kurangnya memori bebas (lebih dari yang ditentukan dalam direktif memory_limit ) atau yang serupa lainnya:

 /** * Fatal Error: Allowed Memory Size */ $arr = array(); while (true) { $arr[] = str_pad(' ', 1024); } 

Sangat umum saat membaca atau mengunduh file besar, jadi berhati-hatilah dengan masalah konsumsi memori
Panggilan fungsi rekursif. Dalam contoh ini, ia berakhir pada iterasi ke-256, karena itu ditulis dalam pengaturan xdebug (ya, kesalahan ini dapat muncul dalam formulir ini hanya ketika ekstensi xdebug diaktifkan):

 /** * Fatal error: Maximum function nesting level of '256' reached, aborting! */ function deep() { deep(); } deep(); 

Tidak fatal


Pandangan ini tidak mengganggu pelaksanaan skrip, tetapi penguji biasanya menemukannya. Ini adalah kesalahan yang menyebabkan masalah paling besar bagi pengembang pemula.

E_WARNING

Ini sering terjadi ketika Anda menghubungkan file menggunakan include , tetapi itu tidak muncul di server atau Anda membuat kesalahan yang menunjukkan path ke file:

 /** * Warning: include_once(): Failed opening 'not-exists.php' for inclusion */ include_once 'not-exists.php'; 

Ini terjadi jika Anda menggunakan jenis argumen yang salah saat memanggil fungsi:

 /** * Warning: join(): Invalid arguments passed */ join('string', 'string'); 

Ada banyak dari mereka, dan daftar semuanya tidak masuk akal ...

E_NOTICE

Ini adalah kesalahan yang paling umum, apalagi, ada penggemar untuk mematikan output kesalahan dan memukau mereka sepanjang hari. Ada sejumlah kesalahan sepele.

Saat mengakses variabel yang tidak ditentukan:

 /** * Notice: Undefined variable: a */ echo $a; 

Saat mengakses elemen array yang tidak ada:

 /** * Notice: Undefined index: a */ $b = []; $b['a']; 

Saat mengakses konstanta yang tidak ada:

 /** * Notice: Use of undefined constant UNKNOWN_CONSTANT - assumed 'UNKNOWN_CONSTANT' */ echo UNKNOWN_CONSTANT; 

Ketika tipe data tidak dikonversi:

 /** * Notice: Array to string conversion */ echo array(); 

Untuk menghindari kesalahan seperti itu - berhati-hatilah, dan jika IDE memberi tahu Anda sesuatu, jangan abaikan:

PHP E_NOTICE dalam PHPStorm

E_STRICT

Ini adalah kesalahan yang akan mengajarkan Anda untuk menulis kode dengan benar sehingga Anda tidak malu, terutama karena IDE segera menunjukkan kepada Anda kesalahan ini. Misalnya, jika Anda menyebut metode non-statis sebagai statis, maka kodenya akan berfungsi, tetapi entah bagaimana salah, dan kesalahan serius dapat terjadi jika metode kelas diubah di masa mendatang, dan permintaan $this muncul:

 /** * Strict standards: Non-static method Strict::test() should not be called statically */ class Strict { public function test() { echo "Test"; } } Strict::test(); 


Jenis kesalahan ini relevan untuk PHP versi 5.6, dan hampir semuanya terpotong
7 pertandingan. Baca lebih lanjut di RFC yang relevan . Jika ada yang tahu di mana kesalahan ini masih ada, maka tulis di komentar


E_DEPRECATED

Jadi PHP akan bersumpah jika Anda menggunakan fungsi yang sudah usang (mis. Yang ditandai sebagai usang, dan tidak akan ada di rilis utama berikutnya):

 /** * Deprecated: Function split() is deprecated */ //  ,   PHP 7.0 //    PHP 5.3 split(',', 'a,b'); 

Di editor saya, fungsi serupa akan dicoret:

PHP E_DEPRECATED dalam PHPStorm

Kebiasaan


Jenis ini, yang oleh pengembang kode itu sendiri “berkembang biak”, saya sudah lama tidak melihatnya, dan saya tidak menyarankan Anda menyalahgunakannya:

  • E_USER_ERROR - kesalahan kritis
  • E_USER_WARNING - bukan kesalahan kritis
  • E_USER_NOTICE - pesan yang bukan kesalahan

Secara terpisah, perlu dicatat E_USER_DEPRECATED - jenis ini masih sering digunakan untuk mengingatkan programmer bahwa metode atau fungsi sudah usang dan sekarang saatnya untuk menulis ulang kode tanpa menggunakannya. Fungsi trigger_error () digunakan untuk membuat kesalahan ini dan yang serupa:

 /** * @deprecated Deprecated since version 1.2, to be removed in 2.0 */ function generateToken() { trigger_error('Function `generateToken` is deprecated, use class `Token` instead', E_USER_DEPRECATED); // ... // code ... // ... } 

Sekarang setelah Anda terbiasa dengan sebagian besar jenis dan jenis kesalahan, sekarang saatnya untuk menyuarakan penjelasan singkat tentang cara kerja direktif display_errors :

  • jika display_errors = on , maka dalam hal terjadi kesalahan browser akan menerima html dengan teks kesalahan dan kode 200
  • jika display_errors = off , maka untuk kesalahan fatal kode respons akan menjadi 500 dan hasilnya tidak akan dikembalikan kepada pengguna, untuk kesalahan lain - kode tidak akan berfungsi dengan benar, tetapi tidak akan memberi tahu siapa pun tentang hal itu


Menjinakkan


Ada 3 fungsi untuk bekerja dengan kesalahan dalam PHP:

  • set_error_handler () - menetapkan penangan untuk kesalahan yang tidak mengganggu skrip (mis. untuk kesalahan tidak fatal)
  • error_get_last () - mendapat informasi tentang kesalahan terakhir
  • register_shutdown_function () - mendaftarkan handler yang akan diluncurkan ketika skrip berakhir. Fungsi ini tidak berlaku langsung untuk penangan kesalahan, tetapi sering digunakan untuk tujuan ini.

Sekarang beberapa detail tentang penanganan kesalahan menggunakan set_error_handler() , sebagai argumen fungsi ini menerima nama fungsi yang akan ditugaskan misi untuk menangani kesalahan dan jenis kesalahan yang akan dipantau. Penangan kesalahan juga bisa berupa metode kelas, atau fungsi anonim, yang terpenting adalah ia membutuhkan daftar argumen berikut:

  • $errno - argumen pertama berisi tipe kesalahan sebagai integer
  • $errstr - argumen kedua berisi pesan kesalahan
  • $errfile - argumen ketiga opsional berisi nama file di mana kesalahan terjadi
  • $errline - argumen keempat opsional berisi nomor baris tempat kesalahan terjadi
  • $errcontext - argumen opsional kelima berisi larik semua variabel yang ada dalam lingkup tempat kesalahan terjadi

Jika pawang kembali true , maka kesalahan akan dianggap diproses dan skrip akan terus dijalankan, jika tidak, pawang standar akan dipanggil yang mencatat kesalahan dan, tergantung pada jenisnya, akan terus mengeksekusi skrip atau menyelesaikannya. Berikut adalah contoh handler:

 <?php //    ,  E_NOTICE error_reporting(E_ALL & ~E_NOTICE); ini_set('display_errors', 1); //    function myHandler($level, $message, $file, $line, $context) { //         switch ($level) { case E_WARNING: $type = 'Warning'; break; case E_NOTICE: $type = 'Notice'; break; default; //   E_WARNING   E_NOTICE //      //      PHP return false; } //    echo "<h2>$type: $message</h2>"; echo "<p><strong>File</strong>: $file:$line</p>"; echo "<p><strong>Context</strong>: $". join(', $', array_keys($context))."</p>"; // ,    ,      return true; } //   ,         set_error_handler('myHandler', E_ALL); 

Anda tidak akan dapat menetapkan lebih dari satu fungsi untuk menangani kesalahan, meskipun saya benar-benar ingin mendaftarkan handler saya sendiri untuk setiap jenis kesalahan, tetapi tidak - menulis satu penangan, dan menjelaskan semua logika tampilan untuk setiap jenis secara langsung di dalamnya
Ada satu masalah signifikan dengan penangan yang ditulis di atas - tidak menangkap kesalahan fatal, dan dengan kesalahan seperti itu, alih-alih situs, pengguna hanya akan melihat halaman kosong, atau, lebih buruk lagi, pesan kesalahan. Untuk mencegah skenario seperti itu, Anda harus menggunakan fungsi register_shutdown_function () dan menggunakannya untuk mendaftarkan fungsi yang akan selalu dieksekusi di akhir skrip:

 function shutdown() { echo '    '; } register_shutdown_function('shutdown'); 

Fungsi ini akan selalu berfungsi!

Namun kembali ke kesalahan, untuk melacak tampilan kesalahan dalam kode kesalahan, kami menggunakan fungsi error_get_last () , dengan bantuannya Anda bisa mendapatkan informasi tentang kesalahan yang terakhir terdeteksi, dan karena kesalahan fatal mengganggu pelaksanaan kode, mereka akan selalu memainkan peran "terakhir":

 function shutdown() { $error = error_get_last(); if ( //       is_array($error) && //       in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR]) ) { //    (       ) while (ob_get_level()) { ob_end_clean(); } //    echo "    ,  "; } } register_shutdown_function('shutdown'); 

Saya ingin menarik perhatian pada fakta bahwa kode ini bahkan terjadi untuk penanganan kesalahan, dan Anda bahkan dapat menemukannya, tetapi telah kehilangan relevansi sejak versi ke-7 PHP. Apa yang datang untuk menggantikan saya akan katakan nanti.
Tugas
Tambahkan penangan kesalahan fatal dengan output dari kode sumber file di mana kesalahan itu dibuat, dan juga tambahkan highlight sintaks dari kode output.

Tentang kerakusan


Mari kita lakukan tes sederhana dan cari tahu berapa banyak sumber daya berharga yang dimakan kesalahan paling sepele:

 /** *      */ //     $time= microtime(true); define('AAA', 'AAA'); $arr = []; for ($i = 0; $i < 10000; $i++) { $arr[AAA] = $i; } printf('%f seconds <br/>', microtime(true) - $time); 

Sebagai hasil menjalankan skrip ini, saya mendapatkan hasil ini:

 0.002867 seconds 

Sekarang tambahkan kesalahan di loop:

 /** *     */ //     $time= microtime(true); $arr = []; for ($i = 0; $i < 10000; $i++) { $arr[BBB] = $i; //   ,      } printf('%f seconds <br/>', microtime(true) - $time); 

Hasilnya diharapkan lebih buruk, dan urutan besarnya (bahkan dua urutan besarnya!):

 0.263645 seconds 

Kesimpulannya jelas - kesalahan dalam kode menyebabkan kerakusan skrip yang berlebihan - jadi nyalakan tampilan semua kesalahan selama pengembangan dan pengujian aplikasi!
Pengujian dilakukan pada berbagai versi PHP, dan di mana-mana perbedaannya puluhan kali, jadi biarkan ini menjadi alasan lain untuk memperbaiki semua kesalahan dalam kode

Di mana anjing itu dimakamkan


PHP memiliki simbol khusus "@" - operator penekan kesalahan, digunakan agar tidak menulis penanganan kesalahan, tetapi bergantung pada perilaku PHP yang benar dalam hal ini:

 <?php echo @UNKNOWN_CONSTANT; 

Dalam kasus ini, penangan kesalahan yang ditentukan dalam set_error_handler() masih akan dipanggil, dan fakta bahwa penindasan diterapkan ke kesalahan dapat dilacak dengan memanggil fungsi error_reporting() di dalam penangan, dalam hal ini akan mengembalikan 0 .
Jika Anda menekan kesalahan dengan cara ini, ini mengurangi beban pada prosesor dibandingkan dengan jika Anda hanya menyembunyikannya (lihat tes perbandingan di atas), tetapi dalam hal apa pun, menekan kesalahan adalah jahat
Tugas
Periksa bagaimana penindasan kesalahan dengan @ mempengaruhi contoh loop sebelumnya.

Pengecualian


Di era PHP4 tidak ada pengecualian, semuanya jauh lebih rumit, dan para pengembang berjuang dengan kesalahan sebanyak yang mereka bisa, itu adalah pertempuran bukan untuk hidup tetapi untuk kematian ... Anda dapat terjun ke kisah menarik tentang konfrontasi dalam artikel Kode Luar Biasa ini. Bagian 1 Haruskah saya membacanya sekarang? Saya tidak bisa memberikan jawaban yang pasti, saya hanya ingin mencatat bahwa ini akan membantu Anda memahami evolusi bahasa, dan akan mengungkapkan semua pesona pengecualian.
Pengecualian adalah peristiwa luar biasa dalam PHP, tidak seperti kesalahan, mereka tidak hanya menyatakan masalah, tetapi membutuhkan tindakan tambahan oleh programmer untuk menangani setiap kasus tertentu.

Misalnya, skrip harus menyimpan beberapa data ke file cache jika terjadi kesalahan (tidak ada akses tulis, tidak ada ruang disk), pengecualian dari jenis yang sesuai dihasilkan, dan keputusan dibuat dalam penangan pengecualian - simpan ke lokasi lain atau beri tahu pengguna tentang masalahnya.

Pengecualian adalah objek dari kelas Exception atau salah satu dari banyak turunannya, ini berisi teks kesalahan, status, dan mungkin juga berisi tautan ke pengecualian lain yang menjadi akar penyebab masalah ini. Model pengecualian dalam PHP mirip dengan yang digunakan dalam bahasa pemrograman lain. Pengecualian dapat dilemparkan (seperti yang mereka katakan, "melempar") menggunakan operator throw , dan Anda dapat menangkap ("menangkap") pernyataan catch . Kode yang melempar pengecualian harus dikelilingi oleh blok try untuk menangkap pengecualian. Setiap blok try harus memiliki setidaknya satu catch cocok atau finally memblokir:

 try { //      if (random_int(0, 1)) { throw new Exception("One"); } echo "Zero" } catch (Exception $e) { //      echo $e->getMessage(); } 

Dalam hal ini layak menggunakan pengecualian:

  • jika dalam kerangka satu metode / fungsi ada beberapa operasi yang mungkin gagal
  • jika kerangka kerja atau pustaka Anda menyatakan penggunaannya

Untuk mengilustrasikan skenario pertama, kami mengambil contoh yang sudah disuarakan dari fungsi untuk menulis data ke file - banyak faktor yang dapat mencegah kami, tetapi untuk memberi tahu kode di atas apa sebenarnya masalahnya, Anda perlu membuat dan melemparkan pengecualian:

 $directory = __DIR__ . DIRECTORY_SEPARATOR . 'logs'; //     if (!is_dir($directory)) { throw new Exception('Directory `logs` is not exists'); } //         if (!is_writable($directory)) { throw new Exception('Directory `logs` is not writable'); } //  -   ,      if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Ym-d') . '.log', 'a+')) { throw new Exception('System can\'t create log file'); } fputs($file, date("[H:i:s]") . " done\n"); fclose($file); 

Karenanya, kami akan menangkap pengecualian ini seperti ini:

 try { //      // ... } catch (Exception $e) { //    echo " : ". $e->getMessage(); } 

Contoh ini menunjukkan skenario yang sangat sederhana untuk menangani pengecualian, ketika kami memiliki pengecualian yang ditangani dalam satu cara. Namun seringkali, berbagai pengecualian membutuhkan pendekatan yang berbeda untuk memproses, dan kemudian Anda harus menggunakan kode pengecualian dan mengatur hierarki pengecualian dalam aplikasi:

 //    class FileSystemException extends Exception {} //     class DirectoryException extends FileSystemException { //   const DIRECTORY_NOT_EXISTS = 1; const DIRECTORY_NOT_WRITABLE = 2; } //     class FileException extends FileSystemException {} 

Sekarang, jika Anda menggunakan pengecualian ini, Anda bisa mendapatkan kode berikut:

 try { //      if (!is_dir($directory)) { throw new DirectoryException('Directory `logs` is not exists', DirectoryException::DIRECTORY_NOT_EXISTS); } if (!is_writable($directory)) { throw new DirectoryException('Directory `logs` is not writable', DirectoryException::DIRECTORY_NOT_WRITABLE); } if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Ym-d') . '.log', 'a+')) { throw new FileException('System can\'t open log file'); } fputs($file, date("[H:i:s]") . " done\n"); fclose($file); } catch (DirectoryException $e) { echo "   : ". $e->getMessage(); } catch (FileException $e) { echo "   : ". $e->getMessage(); } catch (FileSystemException $e) { echo "  : ". $e->getMessage(); } catch (Exception $e) { echo " : ". $e->getMessage(); } 

Penting untuk diingat bahwa Pengecualian terutama merupakan peristiwa luar biasa, dengan kata lain pengecualian terhadap aturan. Anda tidak perlu menggunakannya untuk menangani kesalahan yang jelas, misalnya, untuk memvalidasi input pengguna (meskipun ini tidak begitu sederhana). Dalam hal ini, pawang pengecualian harus ditulis di tempat di mana ia akan dapat menanganinya. Misalnya, pawang untuk pengecualian yang disebabkan oleh tidak dapat diaksesnya file untuk menulis harus dalam metode yang bertanggung jawab untuk memilih file atau metode yang memanggilnya, sehingga dapat memilih file lain atau direktori yang berbeda.

Jadi, apa yang akan terjadi jika Anda tidak menangkap pengecualian? Anda akan mendapatkan "Kesalahan Fatal: Pengecualian tidak tertangkap ...". Tidak menyenangkan.

Untuk menghindari situasi ini, Anda harus menggunakan fungsi set_exception_handler () dan mengatur handler untuk pengecualian yang dibuang di luar blok try-catch dan belum diproses. Setelah memanggil penangan seperti itu, eksekusi skrip akan dihentikan:

 //     //     set_exception_handler(function($exception) { /** @var Exception $exception */ echo $exception->getMessage(), "<br/>\n"; echo $exception->getFile(), ':', $exception->getLine(), "<br/>\n"; echo $exception->getTraceAsString(), "<br/>\n"; }); 

Saya juga akan memberi tahu Anda tentang konstruksi menggunakan blok finally - blok ini akan dieksekusi terlepas dari apakah pengecualian dilemparkan atau tidak:

 try { //      } catch (Exception $e) { //      //     } finally { // ,       } 

Untuk memahami apa ini memberi kita, saya akan memberikan contoh menggunakan blok finally :

 try { // -    //     $handler = mysqli_connect('localhost', 'root', '', 'test'); try { //        // ... throw new Exception('DB error'); } catch (Exception $e) { //  ,     //     ,    throw new Exception('Catch exception', 0, $e); } finally { // ,      //      finally mysqli_close($handler); } //     ,       echo "Ok"; } catch (Exception $e) { //  ,    echo $e->getMessage(); echo "<br/>"; //      echo $e->getPrevious()->getMessage(); } 

Yaitu ingat - blok finally akan dieksekusi bahkan jika Anda melemparkan pengecualian di atas di blok catch (pada kenyataannya, inilah yang ia maksudkan).

Untuk artikel pengantar informasi yang tepat, yang sangat membutuhkan detail, Anda akan menemukannya di artikel kode Luar Biasa ;)

Tugas
Tulis handler pengecualian Anda, dengan output teks file di mana kesalahan terjadi, dan semua ini dengan penyorotan sintaks, juga jangan lupa untuk menampilkan jejak dalam bentuk yang dapat dibaca. Untuk referensi, lihat seberapa keren tampilannya pada whoops .

PHP7 - semuanya tidak seperti sebelumnya


Jadi, sekarang Anda telah mempelajari semua informasi di atas dan sekarang saya akan memuat Anda dengan inovasi dalam PHP7, mis.Saya akan berbicara tentang apa yang akan Anda temui ketika mengerjakan proyek PHP modern. Sebelumnya, saya memberi tahu Anda dan menunjukkan dengan contoh-contoh kruk mana yang perlu Anda bangun untuk menangkap kesalahan kritis, dan - di PHP7 mereka memutuskan untuk memperbaikinya, tetapi? seperti biasa? terikat dengan kompatibilitas kode, dan diterima, meskipun solusi universal, tetapi jauh dari ideal. Dan sekarang pada poin tentang perubahan:

  1. ketika kesalahan fatal dari jenis E_ERRORatau kesalahan fatal terjadi dengan kemungkinan memproses E_RECOVERABLE_ERRORPHP melempar pengecualian
  2. pengecualian ini tidak mewarisi kelas Pengecualian (ingat, saya berbicara tentang kompatibilitas ke belakang, itu semua demi dia)
  3. pengecualian ini mewarisi kelas Kesalahan
  4. kelas Exception dan Error mengimplementasikan antarmuka Throwable
  5. Anda tidak dapat mengimplementasikan antarmuka yang bisa dibuang dalam kode Anda

Antarmuka Throwablemengulangi kami hampir sepenuhnya Exception:

 interface Throwable { public function getMessage(): string; public function getCode(): int; public function getFile(): string; public function getLine(): int; public function getTrace(): array; public function getTraceAsString(): string; public function getPrevious(): Throwable; public function __toString(): string; } 

Apakah ini sulit? Sekarang untuk contoh, ambil contoh yang lebih tinggi dan sedikit dimodernisasi:

 try { // ,     include 'e_parse_include.php'; } catch (Error $e) { var_dump($e); } 

Akibatnya, kami menangkap kesalahan dan mencetak:

 object(ParseError)#1 (7) { ["message":protected] => string(48) "syntax error, unexpected '' (T_STRING)" ["string":"Error":private] => string(0) "" ["code":protected] => int(0) ["file":protected] => string(49) "/www/education/error/e_parse_include.php" ["line":protected] => int(4) ["trace":"Error":private] => array(0) { } ["previous":"Error":private] => NULL } 

Seperti yang Anda lihat, mereka menangkap pengecualian ParseError , yang merupakan penerus pengecualian Erroryang mengimplementasikan antarmuka Throwabledi rumah yang dibangun Jack. Ada banyak pengecualian lain, tetapi saya tidak akan menyiksa - untuk kejelasan, saya akan memberikan hierarki pengecualian:

 interface Throwable |- Exception implements Throwable | |- ErrorException extends Exception | |- ... extends Exception | `- ... extends Exception `- Error implements Throwable |- TypeError extends Error |- ParseError extends Error |- ArithmeticError extends Error | `- DivisionByZeroError extends ArithmeticError `- AssertionError extends Error 

Dan sedikit lebih detail:

TypeError - untuk kesalahan ketika tipe argumen fungsi tidak cocok dengan tipe yang diteruskan:

 try { (function(int $one, int $two) { return; })('one', 'two'); } catch (TypeError $e) { echo $e->getMessage(); } 

ArithmeticError - dapat terjadi selama operasi matematika, misalnya, ketika hasil perhitungan melebihi batas yang dialokasikan untuk integer:

 try { 1 << -1; } catch (ArithmeticError $e) { echo $e->getMessage(); } 

DivisionByZeroError - pembagian dengan kesalahan nol:

 try { 1 / 0; } catch (ArithmeticError $e) { echo $e->getMessage(); } 

AssertionError - binatang buas langka yang muncul ketika kondisi yang ditentukan dalam menegaskan () tidak puas:

 ini_set('zend.assertions', 1); ini_set('assert.exception', 1); try { assert(1 === 0); } catch (AssertionError $e) { echo $e->getMessage(); } 

Pada pengaturan produksi-server direktif zend.assertionsdan assert.exceptiondipotong, dan memang demikian
Anda akan menemukan daftar pengecualian yang telah ditentukan sebelumnya dalam manual resmi , dalam hierarki pengecualian SPL yang sama .

Tugas
Tulis universal handler error untuk PHP7 yang akan menangkap semua kemungkinan pengecualian.

Saat menulis bagian ini, bahan dari artikel Pengecualian dan Kesalahan yang Dapat Dilempar dalam PHP 7 digunakan .

Keseragaman


- Ada kesalahan, pengecualian, tapi bisakah semua ini bisa ditumpuk hingga ke tumpukan?

Ya, mudah, kami punya set_error_handler()satu dan tidak ada yang akan melarang kami untuk melemparkan pengecualian di dalam handler ini:

 //     function errorHandler($severity, $message, $file = null, $line = null) { //  ,       @ if (error_reporting() === 0) { return false; } throw new \ErrorException($message, 0, $severity, $file, $line); } //   -  set_error_handler('errorHandler', E_ALL); 

Tetapi pendekatan dengan PHP7 ini berlebihan, sekarang bisa menangani semuanya Throwable:

 try { /** ... **/ } catch (\Throwable $e) { //      echo $e->getMessage(); } 

Debugging


Terkadang, untuk men-debug kode, Anda perlu melacak apa yang terjadi pada variabel atau objek pada tahap tertentu, untuk tujuan ini ada fungsi debug_backtrace () dan debug_print_backtrace () yang akan mengembalikan riwayat panggilan ke fungsi / metode dalam urutan terbalik:

 <?php function example() { echo '<pre>'; debug_print_backtrace(); echo '</pre>'; } class ExampleClass { public static function method () { example(); } } ExampleClass::method(); 

Sebagai hasil dari eksekusi fungsi debug_print_backtrace(), daftar panggilan yang membawa kami ke titik ini akan ditampilkan:

 #0 example() called at [/www/education/error/backtrace.php:10] #1 ExampleClass::method() called at [/www/education/error/backtrace.php:14] 

Anda dapat memeriksa kode untuk kesalahan sintaks menggunakan fungsi php_check_syntax () atau perintah php -l [ ], tetapi saya belum melihat penggunaannya.

Tegas


Saya juga ingin berbicara tentang binatang buas eksotis seperti asert () di PHP. Sebenarnya, bagian ini dapat dianggap sebagai mimikri untuk metodologi pemrograman kontrak, dan kemudian saya akan memberitahu Anda bagaimana saya tidak pernah menggunakannya :)
Fungsi assert()mengubah perilakunya selama transisi dari versi 5.6 ke 7.0, dan semuanya berubah lebih kuat di versi 7.2, jadi bacalah dengan cermat changelogs dan PHP;)
Kasus pertama adalah ketika Anda harus menulis TODO secara langsung dalam kode, sehingga Anda tidak lupa untuk mengimplementasikan fungsionalitas yang diberikan:

 //  asserts  php.ini // zend.assertions=1 assert(false, "Remove it!"); 

Sebagai hasil dari mengeksekusi kode ini, kita mendapatkan E_WARNING:

 Warning: assert(): Remove it! failed 

PHP7 dapat dialihkan ke mode pengecualian, dan alih-alih kesalahan, sebuah pengecualian akan selalu muncul AssertionError:

 //    «» ini_set('assert.exception', 1); assert(false, "Remove it!"); 

Sebagai hasilnya, kami mengharapkan pengecualian AssertionError.

Jika perlu, Anda bisa melempar pengecualian sewenang-wenang:

 assert(false, new Exception("Remove it!")); 

Saya akan merekomendasikan menggunakan tag @TODO, IDE modern berfungsi baik dengan mereka, dan Anda tidak perlu melakukan upaya dan sumber daya tambahan untuk bekerja dengannya, meskipun godaan untuk “menilai” dengannya sangat bagus.
Use case kedua adalah membuat semacam TDD, tapi ingat, ini hanya sebuah kemiripan. Meskipun, jika Anda berusaha keras, Anda bisa mendapatkan hasil lucu yang akan membantu dalam menguji kode Anda:

 // callback-      function backlog($script, $line, $code, $message) { echo $message; } //  callback- assert_options(ASSERT_CALLBACK, 'backlog'); //    assert_options(ASSERT_WARNING, false); //      assert(sqr(4) === 16, 'When I send integer, function should return square of it'); // ,   function sqr($a) { return; //    } 

Opsi ketiga adalah semacam pemrograman kontrak, ketika Anda menjelaskan aturan untuk menggunakan perpustakaan Anda, tetapi Anda ingin memastikan bahwa Anda dipahami dengan benar, dan dalam hal ini segera memberi tahu pengembang kesalahan (Saya bahkan tidak yakin bahwa saya memahaminya dengan benar, tetapi sebuah contoh Kode ini cukup berfungsi):

 /** *        * * [ * 'host' => 'localhost', * 'port' => 3306, * 'name' => 'dbname', * 'user' => 'root', * 'pass' => '' * ] * * @param $settings */ function setupDb ($settings) { //   assert(isset($settings['host']), 'Db `host` is required'); assert(isset($settings['port']) && is_int($settings['port']), 'Db `port` is required, should be integer'); assert(isset($settings['name']), 'Db `name` is required, should be integer'); //    // ... } setupDb(['host' => 'localhost']); 


Jika Anda tertarik pada kontrak, maka khusus untuk Anda, saya memiliki tautan ke kerangka kerja PhpDeal .


Jangan pernah gunakan assert()untuk memeriksa parameter input, karena sebenarnya ia assert()menginterpretasikan parameter pertama (berperilaku seperti eval()), dan ini penuh dengan injeksi PHP. Dan ya, ini adalah perilaku yang benar, karena jika Anda menonaktifkan pernyataan, maka semua argumen yang diteruskan akan diabaikan, dan jika Anda melakukan seperti pada contoh di atas, kode akan dieksekusi, dan hasil boolean dari eksekusi akan diteruskan ke dalam pernyataan yang dinonaktifkan. Oh, dan itu berubah dalam PHP 7.2 :)


Jika Anda memiliki pengalaman penggunaan yang langsung assert()- bagikan dengan saya, saya akan berterima kasih. Dan ya, inilah bacaan menarik lainnya untuk Anda tentang topik ini - Pernyataan PHP , dengan pertanyaan yang sama di akhir :)

Kesimpulannya


Saya akan menulis kesimpulan dari artikel ini untuk Anda:

  • Melawan kesalahan - seharusnya tidak ada dalam kode Anda
  • Gunakan pengecualian - bekerja dengan mereka perlu diatur dengan benar dan akan ada kebahagiaan
  • Tegas - pelajari tentang mereka, dan baik-baik

PS


Ini adalah repost dari serangkaian artikel "PHP untuk pemula":


Jika Anda memiliki komentar pada materi artikel, atau mungkin dalam bentuk, kemudian jelaskan esensi dalam komentar, dan kami akan membuat materi ini lebih baik.

Terima kasih kepada Maxim Slesarenko untuk bantuan dalam menulis artikel.

Source: https://habr.com/ru/post/id440744/


All Articles