PHP undantag

I den här artikeln kommer vi att lära oss om PHP-undantag från grunden. Dessa begrepp används i många stora, skalbara och objektorienterade tillämpningar och ramverk. Dra nytta av denna språkfunktion för att förbättra dina kunskaper som webbapplikationsutvecklare.


1 Ett exempel först

Innan vi börjar med alla förklaringar skulle jag vilja visa ett exempel först.

Låt oss säga att du vill beräkna området för en cirkel, med den angivna radien. Den här funktionen gör det:

 funktion circle_area ($ radius) return pi () * $ radius * $ radius; 

Det är väldigt enkelt, men det kontrollerar inte om radien är ett giltigt nummer. Nu ska vi göra det och kasta ett undantag om radien är ett negativt tal:

 funktion circle_area ($ radius) // radien kan inte vara negativ om ($ radius < 0)  throw new Exception('Invalid Radius: ' . $radius);  else  return pi() * $radius * $radius;  

Låt oss se vad som händer när vi kallar det med ett negativt tal:

 $ radie = -2; eko "Cirkelradio: $ radius => Cirkelområde:". circle_area ($ radius). "\ N"; echo "Another Line";

Skriptet kraschar med följande meddelande:

 
Allvarligt fel: Undantaget undantag "Undantag" med meddelande "Ogiltig rad: -2" i C: \ wamp \ www \ test \ test.php: 19 Stackspår: # 0 C: \ wamp \ www \ test \ test.php (7) : circle_area (-2) # 1 main kastas in C: \ wamp \ www \ test \ test.php uppkopplad 19

Eftersom det var ett dödligt fel inträffade det inte längre någon kodkörning efter det. Men du kanske inte alltid vill att dina skript slutar när en undantag händer. Lyckligtvis kan du "fånga" dem och hantera dem.

Den här gången, låt oss göra det en rad radievärden:

 $ radius_array = array (2, -2,5, -3); foreach ($ radius_array som $ radie) försök echo "Cirkel Radius: $ radius => Cirkelområde:". circle_area ($ radius). "\ N";  fångst (Undantag $ e) echo 'Caught Exception:', $ e-> getMessage (), "\ n"; 

Nu får vi denna produktion:

 Cirkel Radie: 2 => Cirkelområde: 12.566370614359 Fångad Undantag: Ogiltig Radie: -2 Cirkel Radie: 5 => Cirkelområde: 78.539816339745 Fångad Undantag: Ogiltig Radius: -3

Det finns inga fler fel och skriptet fortsätter att köras. Så här får du undantag.


Full Screencast



2 Vad är en undantag?

Undantag har funnits i andra objektorienterade programmeringsspråk under en längre tid. Det antogs först i PHP med version 5.

Enligt definitionen "kastas", när en exceptionell händelse händer. Detta kan vara lika enkelt som en "division by zero", eller någon annan typ av ogiltig situation.

 kasta ny undantag ("Något felmeddelande.");

Det kan låta liknar andra grundläggande fel som du redan har sett många gånger. Men undantag har en annan typ av mekanism.

Undantag är faktiskt föremål och du har möjlighet att "fånga" dem och utföra viss kod. Detta görs genom att använda "try-catch" -block:

 försök // någon kod går här // som kan kasta ett undantag fångst (Undantag $ e) // koden här görs endast exekverad // om ett undantag hände i försöksblocket ovan

Vi kan bifoga någon kod i ett "försök" -block. Följande "catch" -block används för att få några undantag som kan ha blivit kastade från försöksblocket. Fångstblocket blir aldrig kört om det inte fanns några undantag. En gång ett undantag händer skriptet omedelbart till fångstblocket utan att exekvera någon ytterligare kod.

Vidare i artikeln kommer vi att ha fler exempel som borde visa förmågan och flexibiliteten att använda undantag istället för enkla felmeddelanden.


3 undantag bubbla upp

När ett undantag kastas från en funktion eller en klassmetod går den till den som ringde den funktionen eller metoden. Och det fortsätter att göra detta tills det når toppen av stapeln ELLER fångas. Om det når toppen av stacken och aldrig kallas kommer du att få ett dödligt fel.

Till exempel, här har vi en funktion som slår ett undantag. Vi kallar den funktionen från en andra funktion. Och äntligen kallar vi den andra funktionen från huvudkoden för att visa denna bubblande effekt:

 funktionsfältet () kasta ny Undantag ('Meddelande från stapel ().');  funktion foo () bar ();  försök foo ();  fångst (Undantag $ e) echo 'Fångat undantag:', $ e-> getMessage (), "\ n"; 

Så, när vi kallar foo (), försöker vi fånga eventuella undantag. Även om foo () inte kastar en, men bar () gör det fortfarande bubblor upp och blir fångad överst, så vi får en produktion som säger: "Fångat undantag: Meddelande från stapel ()."


4 Spår undantag

Eftersom undantag bubblar upp, kan de komma från var som helst. För att göra jobbet enklare har Exception-klassen metoder som gör att vi kan spåra källan till varje undantag.

Låt oss se ett exempel som omfattar flera filer och flera klasser.

Först har vi en användarklass, och vi sparar den som user.php:

 klass användare public $ name; offentlig $ email; offentlig funktion spara () $ v = ny Validator (); $ V-> validate_email ($ this-> e-post); // ... spara echo "User saved."; återvänd sant; 

Det använder en annan klass som heter Validator, som vi lägger in validator.php:

 klassen Validator public function validate_email ($ email) if (! filter_var ($ email, FILTER_VALIDATE_EMAIL)) släng ny Undantag ('Email är ogiltigt'); 

Från vår huvudkod kommer vi att skapa ett nytt användarobjekt, ange namn och e-postvärden. När vi väl har ringt till save () -metoden kommer den att använda Validator-klassen för att kontrollera e-postformatet, vilket kan ge ett undantag:

 innefattar ( 'user.php'); innefattar ( 'validator.php'); $ u = ny användare (); $ u-> name = 'foo'; $ u-> email = '$!% # $% # *'; $ U-> Spara ();

Vi vill dock fånga undantaget, så det finns inget dödligt felmeddelande. Och den här gången ska vi undersöka detaljerade uppgifter om detta undantag:

 innefattar ( 'user.php'); innefattar ( 'validator.php'); prova $ u = ny användare (); $ u-> name = 'foo'; $ u-> email = '$!% # $% # *'; $ U-> Spara ();  fångst (Undantag $ e) echo "Meddelande:". $ E-> getMessage (). "\ N \ n"; echo "File:". $ E-> getFile (). "\ N \ n"; echo "Line:". $ E-> getLine (). "\ N \ n"; echo "Spår: \ n". $ E-> getTraceAsString (). "\ N \ n"; 

Koden ovan ger denna utgång:

 Meddelande: E-post är ogiltig Fil: C: \ wamp \ www \ test \ validator.php Linje: 7 Spår: # 0 C: \ wamp \ www \ test \ user.php (11): Validator-> validate_email ('$! % # $% # * ') # 1 C: \ wamp \ www \ test \ test.php (12): Användare-> spara () # 2 main

Så, utan att titta på en enda kod, kan vi berätta var undantaget kom ifrån. Vi kan se filnamn, radnummer, undantagsmeddelande och mer. Spårdata visar även exakta linjer kod som kördes.

Strukturen i standard Exception klassen visas i PHP manualen, där du kan se alla metoder och data som följer med:



5 Förlängning av undantag

Eftersom detta är ett objektorienterat koncept och Undantag är en klass kan vi faktiskt förlänga det för att skapa egna egna undantag.

Till exempel kanske du inte vill visa alla detaljer om ett undantag till användaren. I stället kan du visa ett användarvänligt meddelande och logga in felmeddelandet internt:

 // för att användas för databasproblem klass DatabaseException utökar undantag // du kan lägga till några anpassade metoder public function log () // logga in det här felet någonstans // ... // för att användas för filsystem problem class FileException utökar Undantag // ...

Vi har just skapat två nya typer av undantag. Och de kan ha egna metoder.

När vi får undantaget kan vi visa ett fast meddelande och ringa in de egna metoderna internt:

 funktion foo () // ... // något fel har hänt med databasen kasta ny DatabaseException ();  försök // sätt all din kod här // ... foo ();  catch (FileException $ e) die ("Vi verkar ha problem med filsystemet. Vi är ledsna för besväret.");  fånga (DatabaseException $ e) // ringa vår nya metod $ e-> log (); // Avsluta med ett meddelande dö ("Vi verkar ha databasproblem. Vi beklagar besväret.");  fångst (Undantag $ e) echo 'Fångat undantag:'. $ E-> getMessage (). "\ N"; 

Det här är första gången vi tittar på ett exempel med flera fångstblock för ett enda provblock. Så här kan du fånga olika typer av undantag, så att du kan hantera dem annorlunda.

I det här fallet kommer vi att fånga en DatabaseException och bara det fångstblocket kommer att bli utfört. I den här bloggen kan vi ringa våra nya anpassade metoder och visa ett enkelt meddelande till användaren.

Observera att fångstblocket med standardundantagsklassen måste vara sist, eftersom våra nya barnklasser också betraktas som klass. Exempelvis anses "DatabaseException" också "Undantag" så det kan fångas där om ordern är tvärtom.


6 Hantering av oskydda undantag

Du kanske inte alltid vill leta efter undantag i hela din kod genom att förpacka allt i provtagningsblock. Oavsiktliga undantag visar dock ett detaljerat felmeddelande till användaren, vilket inte heller är idealiskt i en produktionsmiljö.

Det finns faktiskt ett sätt att centralisera hanteringen av alla oskattade undantag så att du kan styra utmatningen från en enda plats.

För detta kommer vi att använda funktionen set_exception_handler ():

 set_exception_handler ( 'exception_handler'); funktion exception_handler ($ e) // public message echo "Något gick fel. \ n"; // semi-hidden message echo "

Som du kan se skriptet avbröts efter det första undantaget och utförde inte den andra. Detta är det förväntade uppförandet av oskattade undantag.

Om du vill att ditt skript fortsätter att köras efter ett undantag, måste du istället använda ett provtagningsblock.


7 Skapa en MySQL Exception Class

Vi ska avsluta denna handledning genom att bygga en anpassad MySQL Exception-klass som har några användbara funktioner och se hur vi kan använda den.

 klass MysqlException utökar undantag // sökväg till loggfilen privat $ log_file = 'mysql_errors.txt'; allmän funktion __construct () $ code = mysql_errno (); $ message = mysql_error (); // öppna loggfilen för att lägga till om ($ fp = fopen ($ this-> log_file, 'a')) // konstruera loggmeddelandet $ log_msg = date ("[Y-m-d H: i: s]"). "Kod: $ code -". "Meddelande: $ message \ n"; skriva ut ($ fp, $ log_msg); fclose ($ fp);  // Ring förälderkonstruktörförälder :: __ konstruera ($ meddelande, $ kod); 

Du kanske märker att vi lägger i stort sett hela koden i konturstrukturen. När ett undantag kastas, är det som att skapa ett nytt objekt, varför konstruktören alltid kallas först. Vid slutet av konstruktören ser vi också till att ringa förälderkonstruktören.

Detta undantag kommer att kastas när vi stöter på ett MySQL-fel. Då hämtar du felnumret och meddelandet direkt från mysql och lagrar sedan informationen i en loggfil tillsammans med tidstämpeln. I vår kod kan vi fånga detta undantag, visa ett enkelt meddelande till användaren och låta undantagsklassen hantera loggningen för oss.

Till exempel, låt oss försöka ansluta till MySQL utan att ge någon användar- / lösenordsinformation:

 försök // försök att ansluta om (! @mysql_connect ()) släng ny MysqlException;  fånga (MysqlException $ e) die ("Vi verkar ha databasproblem. Vi är ledsna för besväret."); 

Vi måste förbereda felsökningsoperatören (@) före mysql_connect () -samtalet så att det inte visar felet för användaren. Om funktionen misslyckas, kastar vi ett undantag, och sedan fångar det. Endast vårt användarvänliga meddelande kommer att visas i webbläsaren.

Klassen MysqlException tar hand om felloggningen automatiskt. När du öppnar loggfilen hittar du den här raden:

 [2010-05-05 21:41:23] Kod: 1045 - Meddelande: Tillgång nekad för användarens system '@' localhost '(med lösenord: NEJ)

Låt oss lägga till mer kod till vårt exempel och också ge en korrekt inloggningsinformation:

 försök // anslutningen ska fungera bra om (! @mysql_connect ('localhost', 'root', ')) släng ny MysqlException; // välj en databas (som kanske inte finns) om (! mysql_select_db (' my_db ' )) kasta ny MysqlException; // försök en fråga (som kan ha ett syntaxfel) om (! $ result = mysql_query ("INSERT INTO foo SET bar = '42")) släng ny MysqlException; MysqlException $ e) die ("Vi verkar ha databasproblem. Vi beklagar besväret.");

Om databasanslutningen lyckas, men databasen med namnet 'my_db' saknas hittar du det i loggarna:

 [2010-05-05 21:55:44] Kod: 1049 - Meddelande: Okänd databas 'my_db'

Om databasen finns där, men sökningen misslyckas, kan du till exempel se det här i loggen på grund av ett syntaxfel:

 [2010-05-05 21:58:26] Kod: 1064 - Meddelande: Du har ett fel i din SQL-syntax; kolla handboken som motsvarar din MySQL-serverversion för rätt syntax för att använda nära "42" på rad 1

Bonus Bygga en DB-klass med PHP Magic

Vi kan göra kodexemplet ovan jämnare genom att skriva vår egen databasklass, som hanterar undantagets kasta. Den här gången ska jag använda några "magiska" funktioner i PHP för att konstruera den här klassen.

I slutet vill vi att vår huvudkod ska se ut så här:

 försök Database :: connect ('localhost', 'root', '); Databas :: select_db (' test '); $ result = Databas :: fråga ("INSERT INTO foo SET bar = '42"); (MysqlException $ e) die ("Vi verkar ha databasproblem. Vi är ledsna för besväret.");

Det är trevligt och rent. Vi kontrollerar inte eventuella fel i varje enskilt databassamtal. Den här nya databasklassen har ansvaret att kasta undantag när fel uppstår. Och eftersom dessa undantag bubblar upp, blir de fångade av vårt fångstblock i slutet.

Och här är den magiska databasklassen:

 klass databas // ett statiskt funktionssamtal åberopar denna offentliga statiska funktion __callStatic ($ name, $ args) // funktion som ska kallas $ function = 'mysql_'. $ Name; // finns funktionen? om (! function_exists ($ function)) // kasta ett regelbundet undantag kasta ny Undantag ("Ogiltig mysql funktion: $ function.");  // kalla mysql-funktionen $ ret = @call_user_func_array ($ function, $ args); // de returnerar FALSE om fel om ($ ret === FALSE) // kasta db undantag kasta ny MysqlException;  // returnera returvärdet retur $ ret; 

Det har bara en metod, och det blir åberopat när vi kallar en statisk metod på den här klassen. Du kan läsa mer om detta koncept här: PHP Overloading.

Så, när vi kallar Database :: connect (), kallar denna kod automatiskt mysql_connect (), skickar argumenten, kontrollerar fel, kastar undantag vid behov och returnerar returvärdet från funktionsanropet. Det fungerar i grunden som en människa och hanterar det smutsiga arbetet.


Slutsats

Jag hoppas att du haft denna handledning och lärt dig av det. Nu borde du få en bättre förståelse för detta ämne. Försök och se om du kan använda PHP-undantag i ditt nästa projekt. Vi ses nästa gång!