PHP für Anfänger. Fehlerbehandlung

Bild

Nur wer nichts tut, macht keine Fehler, und wir sind ein Beispiel dafür - wir sitzen und arbeiten unermüdlich, lesen den Habr :)

In diesem Artikel werde ich meine Geschichte über Fehler in PHP und wie man sie eindämmt, führen.

Fehler


Sorten in der Fehlerfamilie


Bevor ich Fehler zähme, würde ich empfehlen, jede Art zu untersuchen und die prominentesten Vertreter separat zu beachten.

Um zu verhindern, dass ein einzelner Fehler unbemerkt bleibt, müssen Sie die Verfolgung aller Fehler mithilfe der Funktion error_reporting () und mithilfe der Anweisung display_errors aktivieren , um deren Anzeige zu aktivieren:

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

Schwerwiegende Fehler


Die schwerwiegendsten Fehler sind schwerwiegend. Sie können sowohl während der Kompilierung als auch während der Arbeit des Parsers oder des PHP-Skripts auftreten, während das Skript unterbrochen wird.

E_PARSE

Dieser Fehler tritt auf, wenn Sie einen groben Syntaxfehler machen und der PHP-Interpreter nicht versteht, was Sie davon wollen, beispielsweise wenn Sie die geschweifte Klammer oder Klammer nicht geschlossen haben:

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

Oder sie schrieben in einer unverständlichen Sprache:

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

Zusätzliche Klammern treten ebenfalls auf und sind nicht so wichtig rund oder lockig:

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

Ich stelle einen wichtigen Punkt fest: Der Code der Datei, in der Sie den Analysefehler gemacht haben, wird nicht ausgeführt. Wenn Sie daher versuchen, die Anzeige von Fehlern in derselben Datei zu aktivieren, in der der Parserfehler aufgetreten ist, funktioniert dies nicht:

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

E_ERROR

Dieser Fehler tritt auf, wenn PHP verstanden hat, was Sie wollen, aber dies hat aus mehreren Gründen nicht funktioniert. Dieser Fehler unterbricht auch die Ausführung des Skripts, und der Code funktioniert, bevor der Fehler auftritt:

Das Plug-In wurde nicht gefunden:

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

Eine Ausnahme wurde ausgelöst (was für ein Tier werde ich Ihnen etwas später erzählen), aber nicht verarbeitet:

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

Beim Versuch, eine nicht vorhandene Klassenmethode aufzurufen:

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

Mangel an freiem Speicher (mehr als in der Direktive memory_limit vorgeschrieben) oder etwas Ähnliches:

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

Dies ist sehr häufig beim Lesen oder Herunterladen großer Dateien der Fall. Seien Sie also vorsichtig mit dem Problem des Speicherverbrauchs
Rekursiver Funktionsaufruf. In diesem Beispiel endete es bei der 256. Iteration, da es in den xdebug-Einstellungen so geschrieben ist (ja, dieser Fehler kann in dieser Form nur auftreten, wenn die xdebug-Erweiterung aktiviert ist):

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

Nicht tödlich


Diese Ansicht unterbricht die Ausführung des Skripts nicht, aber es ist der Tester, der sie normalerweise findet. Es sind solche Fehler, die Anfängern die meisten Probleme bereiten.

E_WARNING

Es tritt häufig auf, wenn Sie eine Datei mit include , aber es wird nicht auf dem Server angezeigt oder Sie haben einen Fehler gemacht, der den Pfad zur Datei angibt:

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

Es passiert, wenn Sie beim Aufrufen von Funktionen die falsche Art von Argumenten verwenden:

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

Es gibt viele von ihnen, und alles aufzulisten macht keinen Sinn ...

E_HINWEIS

Dies sind die häufigsten Fehler. Außerdem gibt es Lüfter, die die Fehlerausgabe ausschalten und den ganzen Tag nieten. Es gibt eine Reihe von trivialen Fehlern.

Beim Zugriff auf eine undefinierte Variable:

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

Beim Zugriff auf ein nicht vorhandenes Array-Element:

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

Beim Zugriff auf eine nicht vorhandene Konstante:

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

Wenn Datentypen nicht konvertiert werden:

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

Um solche Fehler zu vermeiden, seien Sie vorsichtig. Wenn die IDE etwas sagt, ignorieren Sie es nicht:

PHP E_NOTICE in PHPStorm

E_STRICT

Dies sind Fehler, die Sie lehren, Code korrekt zu schreiben, damit Sie sich nicht schämen, zumal die IDE Ihnen diese Fehler sofort anzeigt. Wenn Sie beispielsweise eine nicht statische Methode als statisch bezeichnet haben, funktioniert der Code, ist jedoch irgendwie falsch, und es können schwerwiegende Fehler auftreten, wenn die Klassenmethode in Zukunft geändert wird, und es wird ein Aufruf an $this angezeigt:

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


Diese Art von Fehler ist für PHP Version 5.6 relevant und fast alle wurden herausgeschnitten
7 Spiele. Lesen Sie mehr im entsprechenden RFC . Wenn jemand weiß, wo diese Fehler sonst noch geblieben sind, dann schreibe in die Kommentare


E_DEPRECATED

PHP wird also schwören, wenn Sie veraltete Funktionen verwenden (d. H. Solche, die als veraltet markiert sind und nicht in der nächsten Hauptversion enthalten sein werden):

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

In meinem Editor werden ähnliche Funktionen durchgestrichen:

PHP E_DEPRECATED in PHPStorm

Benutzerdefiniert


Diesen Typ, den der Codeentwickler selbst „züchtet“, habe ich schon lange nicht mehr gesehen, und ich empfehle nicht, dass Sie sie missbrauchen:

  • E_USER_ERROR - kritischer Fehler
  • E_USER_WARNING - kein kritischer Fehler
  • E_USER_NOTICE - Nachrichten, die keine Fehler sind

E_USER_DEPRECATED davon ist E_USER_DEPRECATED zu E_USER_DEPRECATED Dieser Typ wird immer noch sehr häufig verwendet, um den Programmierer daran zu erinnern, dass die Methode oder Funktion veraltet ist und es Zeit ist, den Code neu zu schreiben, ohne ihn zu verwenden. Die Funktion trigger_error () wird verwendet, um diesen und ähnliche Fehler zu erstellen:

 /** * @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 ... // ... } 

Nachdem Sie sich mit den meisten Arten und Arten von Fehlern vertraut gemacht haben, ist es an der Zeit, eine kurze Erklärung zur Funktionsweise der Anweisung display_errors :

  • Wenn display_errors = on , erhält der Browser im Fehlerfall HTML mit dem Fehlertext und dem Code 200
  • Wenn display_errors = off , display_errors = off der Antwortcode für schwerwiegende Fehler 500 und das Ergebnis wird für andere Fehler nicht an den Benutzer zurückgegeben. Der Code funktioniert nicht ordnungsgemäß, teilt jedoch niemandem davon mit


Zähmen


Es gibt 3 Funktionen zum Arbeiten mit Fehlern in PHP:

  • set_error_handler () - Legt einen Handler für Fehler fest, die das Skript nicht unterbrechen (d. h. für nicht schwerwiegende Fehler).
  • error_get_last () - Ruft Informationen zum letzten Fehler ab
  • register_shutdown_function () - registriert einen Handler, der beim Beenden des Skripts gestartet wird. Diese Funktion gilt nicht direkt für Fehlerbehandlungsroutinen, wird jedoch häufig zu diesem Zweck verwendet.

Nun einige Details zur Fehlerbehandlung mit set_error_handler() . Als Argumente akzeptiert diese Funktion den Namen der Funktion, der die Mission zur Behandlung von Fehlern zugewiesen wird, und die Arten von Fehlern , die überwacht werden. Ein Fehlerbehandler kann auch eine Klassenmethode oder eine anonyme Funktion sein. Hauptsache, er benötigt die folgende Liste von Argumenten:

  • $errno - Das erste Argument enthält den Fehlertyp als Ganzzahl
  • $errstr - Das zweite Argument enthält eine Fehlermeldung
  • $errfile - Ein optionales drittes Argument enthält den Namen der Datei, in der der Fehler aufgetreten ist
  • $errline - Ein optionales viertes Argument enthält die Zeilennummer, in der der Fehler aufgetreten ist
  • $errcontext - Das optionale fünfte Argument enthält ein Array aller Variablen, die in dem Bereich vorhanden sind, in dem der Fehler aufgetreten ist

Wenn der Handler true zurückgibt, wird der Fehler als verarbeitet betrachtet und das Skript wird weiterhin ausgeführt. Andernfalls wird ein Standardhandler aufgerufen, der den Fehler protokolliert und je nach Typ das Skript weiterhin ausführt oder abschließt. Hier ist ein Beispiel für einen 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); 

Sie können nicht mehr als eine Funktion zur Behandlung von Fehlern zuweisen, obwohl ich wirklich gerne meinen eigenen Handler für jeden Fehlertyp registrieren würde, aber nein - schreiben Sie einen Handler und beschreiben Sie die gesamte Anzeigelogik für jeden Typ direkt darin
Es gibt ein erhebliches Problem mit dem oben beschriebenen Handler: Er fängt keine schwerwiegenden Fehler ab, und bei solchen Fehlern sehen Benutzer anstelle der Site nur eine leere Seite oder, noch schlimmer, eine Fehlermeldung. Um ein solches Szenario zu verhindern, sollten Sie die Funktion register_shutdown_function () verwenden und damit eine Funktion registrieren, die immer am Ende des Skripts ausgeführt wird:

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

Diese Funktion wird immer funktionieren!

Zurück zu den Fehlern: Um das Auftreten des Fehlers im Fehlercode zu verfolgen, verwenden wir die Funktion error_get_last () . Mit ihrer Hilfe können Sie Informationen über den zuletzt erkannten Fehler abrufen. Da schwerwiegende Fehler die Ausführung des Codes unterbrechen, spielen sie immer die Rolle des "letzten":

 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'); 

Ich möchte darauf aufmerksam machen, dass dieser Code sogar zur Fehlerbehandlung vorkommt und Sie vielleicht sogar darauf stoßen, aber er hat seit der 7. Version von PHP an Relevanz verloren. Was kam, um zu ersetzen, werde ich etwas später erzählen.
Aufgabe
Ergänzen Sie den Handler für schwerwiegende Fehler durch die Ausgabe des Quellcodes der Datei, in der der Fehler aufgetreten ist, und fügen Sie eine Syntaxhervorhebung des Ausgabecodes hinzu.

Über Völlerei


Lassen Sie uns einen einfachen Test durchführen und herausfinden, wie viele wertvolle Ressourcen der trivialste Fehler verbraucht:

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

Als Ergebnis der Ausführung dieses Skripts habe ich folgendes Ergebnis erhalten:

 0.002867 seconds 

Fügen Sie nun den Fehler in die Schleife ein:

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

Das Ergebnis ist voraussichtlich schlechter und eine Größenordnung (sogar zwei Größenordnungen!):

 0.263645 seconds 

Die Schlussfolgerung ist klar - Fehler im Code führen zu einer übermäßigen Fülle von Skripten - schalten Sie also die Anzeige aller Fehler während der Anwendungsentwicklung und des Testens ein!
Die Tests wurden mit verschiedenen Versionen von PHP durchgeführt, und überall ist der Unterschied zehnmal so groß. Lassen Sie dies einen weiteren Grund sein, alle Fehler im Code zu beheben

Wo ist der Hund begraben?


PHP hat ein spezielles Symbol "@" - ein Fehlerunterdrückungsoperator, der verwendet wird, um keine Fehlerbehandlung zu schreiben, sondern sich auf das korrekte Verhalten von PHP stützt. In diesem Fall:

 <?php echo @UNKNOWN_CONSTANT; 

In diesem Fall wird der in set_error_handler() angegebene set_error_handler() weiterhin aufgerufen, und die Tatsache, dass der Fehler unterdrückt wurde, kann durch Aufrufen der Funktion error_reporting() im Handler verfolgt werden. In diesem Fall wird 0 .
Wenn Sie Fehler auf diese Weise unterdrücken, wird der Prozessor weniger belastet als wenn Sie sie nur ausblenden (siehe Vergleichstest oben). In jedem Fall ist das Unterdrücken von Fehlern jedoch böse
Aufgabe
Überprüfen Sie, wie sich die Fehlerunterdrückung mit @ auf das vorherige Schleifenbeispiel auswirkt.

Ausnahmen


In der Ära von PHP4 gab es keine Ausnahmen, alles war viel komplizierter und die Entwickler kämpften mit Fehlern, so gut sie konnten. Es war ein Kampf nicht um das Leben, sondern um den Tod ... Sie können in diese faszinierende Geschichte der Konfrontation im Artikel Exceptional Code eintauchen. Teil 1 Soll ich es jetzt lesen? Ich kann keine eindeutige Antwort geben. Ich möchte nur darauf hinweisen, dass dies Ihnen hilft, die Entwicklung der Sprache zu verstehen, und den ganzen Reiz von Ausnahmen offenbart.
Ausnahmen sind außergewöhnliche Ereignisse in PHP. Im Gegensatz zu Fehlern geben sie nicht nur ein Problem an, sondern erfordern zusätzliche Aktionen des Programmierers, um den jeweiligen Fall zu behandeln.

Beispielsweise sollte ein Skript einige Daten in einer Cache-Datei speichern, wenn ein Fehler aufgetreten ist (kein Schreibzugriff, kein Speicherplatz), eine Ausnahme des entsprechenden Typs generiert wird und die Entscheidung im Ausnahmebehandler getroffen wird - Speichern an einem anderen Speicherort oder Informieren Sie den Benutzer über das Problem.

Eine Ausnahme ist ein Objekt der Exception Klasse oder eines ihrer vielen Nachkommen. Sie enthält den Fehlertext und den Status sowie möglicherweise einen Link zu einer anderen Ausnahme, die zur Hauptursache dafür geworden ist. Das Ausnahmemodell in PHP ähnelt dem in anderen Programmiersprachen verwendeten. Mit dem Operator throw kann eine Ausnahme ausgelöst werden (wie sie sagen: "throw"), und Sie können die catch Anweisung abfangen ("catch"). Der Code, der die Ausnahme auslöst, muss von einem try Block umgeben sein, um die Ausnahme abzufangen. Jeder try Block muss mindestens einen passenden catch oder finally Block haben:

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

In diesen Fällen lohnt es sich, Ausnahmen zu verwenden

  • Wenn im Rahmen einer Methode / Funktion mehrere Operationen fehlschlagen
  • wenn Ihr Framework oder Ihre Bibliothek ihre Verwendung deklariert

Um das erste Szenario zu veranschaulichen, nehmen wir ein bereits geäußertes Beispiel einer Funktion zum Schreiben von Daten in eine Datei. Viele Faktoren können uns daran hindern. Um dem obigen Code jedoch genau zu sagen, was genau das Problem war, müssen Sie eine Ausnahme erstellen und auslösen:

 $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); 

Dementsprechend werden wir diese Ausnahmen wie folgt abfangen:

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

Dieses Beispiel zeigt ein sehr einfaches Szenario für die Behandlung von Ausnahmen, wenn eine Ausnahme auf eine Weise behandelt wird. Verschiedene Ausnahmen erfordern jedoch häufig einen unterschiedlichen Verarbeitungsansatz. Anschließend sollten Sie Ausnahmecodes verwenden und die Hierarchie der Ausnahmen in der Anwendung festlegen:

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

Wenn Sie diese Ausnahmen verwenden, können Sie den folgenden Code erhalten:

 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(); } 

Es ist wichtig, sich daran zu erinnern, dass eine Ausnahme in erster Linie ein außergewöhnliches Ereignis ist, dh eine Ausnahme von der Regel. Sie müssen sie nicht verwenden, um offensichtliche Fehler zu behandeln, z. B. um Benutzereingaben zu überprüfen (obwohl dies nicht so einfach ist). In diesem Fall sollte der Ausnahmebehandler an der Stelle geschrieben werden, an der er damit umgehen kann. Der Handler für Ausnahmen, die durch die Unzugänglichkeit einer Datei zum Schreiben verursacht werden, sollte sich beispielsweise in der Methode befinden, die für die Auswahl der Datei verantwortlich ist, oder in der Methode, mit der sie aufgerufen wird, damit eine andere Datei oder ein anderes Verzeichnis ausgewählt werden kann.

Was passiert also, wenn Sie die Ausnahme nicht abfangen? Sie erhalten "Schwerwiegender Fehler: Nicht erfasste Ausnahme ...". Unangenehm.

Um diese Situation zu vermeiden, sollten Sie die Funktion set_exception_handler () verwenden und den Handler für Ausnahmen festlegen, die außerhalb des try-catch-Blocks ausgelöst werden und nicht verarbeitet wurden. Nach dem Aufrufen eines solchen Handlers wird die Skriptausführung gestoppt:

 //     //     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"; }); 

Ich erzähle Ihnen auch von der Konstruktion mit dem finally Block. Dieser Block wird ausgeführt, unabhängig davon, ob eine Ausnahme ausgelöst wurde oder nicht:

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

Um zu verstehen, was uns dies gibt, werde ich das folgende Beispiel für die Verwendung des finally Blocks geben:

 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(); } 

Das heißt, Denken Sie daran - der finally Block wird auch dann ausgeführt, wenn Sie oben im catch Block eine Ausnahme auslösen (genau das hat er beabsichtigt).

Für einen genau einführenden Informationsartikel, der sich nach weiteren Details sehnt, finden Sie diese im Artikel Außergewöhnlicher Code ;)

Aufgabe
Schreiben Sie Ihren Ausnahmebehandler mit der Ausgabe des Texts der Datei, in der der Fehler aufgetreten ist, und vergessen Sie nicht, die Ablaufverfolgung in lesbarer Form anzuzeigen. Als Referenz sehen Sie, wie cool es auf whoops aussieht.

PHP7 - alles ist nicht mehr so ​​wie vorher


Jetzt haben Sie alle oben genannten Informationen gelernt und jetzt werde ich Sie mit Innovationen in PHP7 beladen, d. H.Ich werde darüber sprechen, was Ihnen bei der Arbeit an einem modernen PHP-Projekt begegnen wird. Ich habe es Ihnen bereits gesagt und anhand von Beispielen gezeigt, welche Krücke Sie bauen müssen, um kritische Fehler zu erkennen, und so - in PHP7 haben sie beschlossen, sie zu beheben, aber? wie gewöhnlich? an die Abwärtskompatibilität des Codes gebunden und erhalten, obwohl eine universelle Lösung, aber es ist alles andere als ideal. Und nun zu den Punkten über die Änderungen:

  1. Wenn schwerwiegende Fehler des Typs E_ERRORoder schwerwiegende Fehler mit der Möglichkeit der Verarbeitung von E_RECOVERABLE_ERRORPHP auftreten, wird eine Ausnahme ausgelöst
  2. Diese Ausnahmen erben nicht die Exception- Klasse (denken Sie daran, ich habe über Abwärtskompatibilität gesprochen, alles für sie)
  3. Diese Ausnahmen erben die Fehlerklasse
  4. Sowohl die Ausnahme- als auch die Fehlerklasse implementieren die Throwable- Schnittstelle
  5. Sie können die Throwable-Schnittstelle nicht in Ihrem Code implementieren

Die Schnittstelle Throwablewiederholt uns fast vollständig 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; } 

Ist es schwierig Nehmen wir zum Beispiel die höheren und leicht modernisierten:

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

Als Ergebnis fangen wir den Fehler und drucken:

 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 } 

Wie Sie sehen können, haben sie die ParseError- Ausnahme abgefangen , die der Nachfolger der Ausnahme ist Error, die die Schnittstelle Throwablein dem von Jack erstellten Haus implementiert . Es gibt viele andere Ausnahmen, aber ich werde sie nicht quälen - aus Gründen der Klarheit werde ich eine Hierarchie von Ausnahmen angeben:

 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 

Und ein bisschen mehr Details:

TypeError - für Fehler, wenn der Typ der Funktionsargumente nicht mit dem übergebenen Typ übereinstimmt:

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

ArithmeticError - kann während mathematischer Operationen auftreten, z. B. wenn das Berechnungsergebnis den für eine Ganzzahl zugewiesenen Grenzwert überschreitet:

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

DivisionByZeroError - Division durch Null Fehler:

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

AssertionError - ein seltenes Tier, das auftritt, wenn die in assert () angegebene Bedingung nicht erfüllt ist:

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

Bei Einstellungen Produktion-Server - Richtlinie zend.assertionsund assert.exceptionabgeschnitten, und das zu Recht
Eine vollständige Liste der vordefinierten Ausnahmen finden Sie im offiziellen Handbuch in derselben Hierarchie der SPL-Ausnahmen .

Aufgabe
Schreiben Sie einen universellen Fehlerbehandler für PHP7, der alle möglichen Ausnahmen abfängt.

Beim Schreiben dieses Abschnitts wurden Materialien aus dem Artikel Auslösbare Ausnahmen und Fehler in PHP 7 verwendet .

Einheitlichkeit


- Es gibt Fehler, Ausnahmen, aber kann das alles irgendwie auf den Haufen gebracht werden?

Ja, es ist einfach, wir haben set_error_handler()eine und niemand wird uns verbieten, eine Ausnahme in diesen Handler zu werfen:

 //     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); 

Dieser Ansatz mit PHP7 ist jedoch redundant und kann jetzt alles verarbeiten Throwable:

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

Debuggen


Manchmal müssen Sie zum Debuggen von Code nachverfolgen, was zu einem bestimmten Zeitpunkt mit einer Variablen oder einem Objekt passiert ist. Für diese Zwecke gibt es eine Funktion debug_backtrace () und debug_print_backtrace () , die den Verlauf von Aufrufen von Funktionen / Methoden in umgekehrter Reihenfolge zurückgeben:

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

Als Ergebnis der Funktionsausführung debug_print_backtrace()wird eine Liste der Aufrufe angezeigt, die uns an diesen Punkt gebracht haben:

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

Sie können den Code mit der Funktion php_check_syntax () oder dem Befehl auf Syntaxfehler überprüfen php -l [ ], aber ich habe deren Verwendung nicht gesehen.

Behauptet


Ich möchte auch über ein so exotisches Biest wie assert () in PHP sprechen . Eigentlich kann dieses Stück als Mimikry für die Vertragsprogrammierungsmethode angesehen werden, und dann werde ich Ihnen sagen, wie ich es nie benutzt habe :)
Die Funktion assert()hat ihr Verhalten während des Übergangs von Version 5.6 auf 7.0 geändert, und in Version 7.2 hat sich alles noch stärker geändert. Lesen Sie daher die Änderungsprotokolle und PHP sorgfältig durch.
Der erste Fall ist, wenn Sie TODO direkt in den Code schreiben müssen, damit Sie nicht vergessen, die angegebene Funktionalität zu implementieren:

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

Als Ergebnis dieses Codes erhalten wir E_WARNING:

 Warning: assert(): Remove it! failed 

PHP7 kann in den Ausnahmemodus geschaltet werden, und anstelle eines Fehlers wird immer eine Ausnahme angezeigt AssertionError:

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

Infolgedessen erwarten wir eine Ausnahme AssertionError.

Bei Bedarf können Sie eine beliebige Ausnahme auslösen:

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

Ich würde die Verwendung von Tags empfehlen @TODO, moderne IDEs funktionieren gut mit ihnen, und Sie müssen keine zusätzlichen Anstrengungen und Ressourcen aufwenden, um mit ihnen zu arbeiten, obwohl die Versuchung groß ist, mit ihnen zu „punkten“
Der zweite Anwendungsfall besteht darin, eine Art TDD zu erstellen. Denken Sie jedoch daran, dass dies nur eine Ähnlichkeit ist. Wenn Sie sich jedoch anstrengen, erhalten Sie ein lustiges Ergebnis, das Ihnen beim Testen Ihres Codes hilft:

 // 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; //    } 

Die dritte Option ist eine Art Vertragsprogrammierung, bei der Sie die Regeln für die Verwendung Ihrer Bibliothek beschrieben haben, aber sicherstellen möchten, dass Sie richtig verstanden werden. In diesem Fall teilen Sie dem Entwickler sofort einen Fehler mit (ich bin mir nicht einmal sicher, ob ich ihn richtig verstehe, sondern ein Beispiel Der Code funktioniert ziemlich gut):

 /** *        * * [ * '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']); 


Wenn Sie an Verträgen interessiert sind, habe ich speziell für Sie einen Link zum PhpDeal- Framework .


Verwenden assert()Sie diese Option niemals , um die Eingabeparameter zu überprüfen, da sie tatsächlich assert()den ersten Parameter interpretiert (verhält sich wie eval()) und dies mit einer PHP-Injektion behaftet ist. Und ja, dies ist das richtige Verhalten, denn wenn Sie die Zusicherung deaktivieren, werden alle übergebenen Argumente ignoriert. Wenn Sie wie im obigen Beispiel vorgehen, wird der Code ausgeführt und das boolesche Ergebnis der Ausführung wird innerhalb der deaktivierten Zusicherung übergeben. Oh, und es hat sich in PHP 7.2 geändert :)


Wenn Sie eine Live-Nutzungserfahrung haben assert()- teilen Sie sie mir mit, ich bin Ihnen dankbar. Und ja, hier ist eine weitere interessante Lektüre zu diesem Thema - PHP Assertions , mit der gleichen Frage am Ende :)

Abschließend


Ich werde die Schlussfolgerungen aus diesem Artikel für Sie schreiben:

  • Fehler bekämpfen - sie sollten nicht in Ihrem Code enthalten sein
  • Verwenden Sie Ausnahmen - die Arbeit mit ihnen muss richtig organisiert sein und es wird Glück geben
  • Behauptung - über sie gelernt, und gut

PS


Dies ist ein Repost aus einer Reihe von Artikeln "PHP für Anfänger":


Wenn Sie Kommentare zum Material des Artikels oder möglicherweise in Form haben, beschreiben Sie das Wesentliche in den Kommentaren, und wir werden dieses Material noch besser machen.

Vielen Dank an Maxim Slesarenko für die Hilfe beim Schreiben des Artikels.

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


All Articles