PHP unntak

I denne artikkelen skal vi lære om PHP-unntak fra grunnen opp. Disse konseptene benyttes i mange store, skalerbare og objektorienterte applikasjoner og rammer. Dra nytte av denne språkkfunksjonen for å forbedre dine ferdigheter som en webapplikasjonsutvikler.


1 Et eksempel først

Før vi begynner med alle forklaringene, vil jeg gjerne vise et eksempel først.

La oss si at du vil beregne arealet av en sirkel, ved den angitte radiusen. Denne funksjonen vil gjøre det:

 funksjon circle_area ($ radius) retur pi () * $ radius * $ radius; 

Det er veldig enkelt, men det kontrollerer ikke om radius er et gyldig nummer. Nå skal vi gjøre det, og kaste et unntak dersom radiusen er et negativt tall:

 funksjon circle_area ($ radius) // radius kan ikke være negativ hvis ($ radius < 0)  throw new Exception('Invalid Radius: ' . $radius);  else  return pi() * $radius * $radius;  

La oss se hva som skjer når vi kaller det med et negativt nummer:

 $ radius = -2; ekko "Sirkel Radius: $ radius => Sirkelområde:". circle_area ($ radius). "\ N"; ekko "En annen linje";

Skriptet krasjer med følgende melding:

 
Fatal feil: Uncaught unntak 'Unntak' med melding 'Ugyldig radius: -2' i C: \ wamp \ www \ test \ test.php: 19 Stack spor: # 0 C: \ wamp \ www \ test \ test.php (7) : circle_area (-2) # 1 main kastet inn C: \ WAMP \ www \ test \ test.php på nett 19

Siden det var en dødelig feil, skjedde det ikke mer kodekjøring etter det. Men du vil kanskje ikke alltid at skriptene dine skal stoppe når en unntak skjer. Heldigvis kan du "fange" dem og håndtere dem.

Denne gangen, la oss gjøre det en rekke radiusverdier:

 $ radius_array = array (2, -2,5, -3); foreach ($ radius_array som $ radius) prøv echo "Sirkel Radius: $ radius => Sirkelområde:". circle_area ($ radius). "\ N";  fangst (Unntak $ e) echo 'Caught Exception:', $ e-> getMessage (), "\ n"; 

Nå får vi denne utgangen:

 Sirkel Radius: 2 => Sirkelområde: 12.566370614359 Fanget Unntak: Ugyldig Radius: -2 Sirkel Radius: 5 => Sirkelområde: 78.539816339745 Fanget Unntak: Ugyldig Radius: -3

Det er ikke flere feil, og skriptet fortsetter å løpe. Det er slik du får unntak.


Full Screencast



2 Hva er en unntak?

Unntak har eksistert i andre objektorienterte programmeringsspråk for en stund. Det ble først vedtatt i PHP med versjon 5.

Per definisjon er en Unntak "kastet", når en eksepsjonell hendelse skjer. Dette kan være så enkelt som en "divisjon med null", eller en annen form for ugyldig situasjon.

 kaste ny unntak ('Noen feilmelding');

Dette kan høres ut som andre grunnleggende feil som du allerede har sett mange ganger. Men Unntak har en annen type mekanisme.

Unntak er faktisk objekter, og du har muligheten til å "fange" dem og utføre bestemt kode. Dette gjøres ved å bruke "try-catch" blokker:

 prøv // noen kode går her // som kan kaste et unntak fangst (Unntak $ e) // koden her blir bare utført // hvis et unntak skjedde i forsøksblokken ovenfor

Vi kan legge ved noen kode i en "prøve" blokk. Følgende "fangstblokk" brukes til å fange noen unntak som kan ha blitt kastet fra forsøksblokken. Fangstblokken blir aldri utført dersom det ikke var noen unntak. Også, når et unntak skjer, hopper skriptet øyeblikkelig til fangstblokken uten å utføre noen ytterligere kode.

Videre i artikkelen vil vi ha flere eksempler som skal demonstrere kraft og fleksibilitet ved å bruke unntak i stedet for enkle feilmeldinger.


3 unntak boble opp

Når et unntak kastes fra en funksjon eller en klassemetode, går det til den som kalte den funksjonen eller metoden. Og det fortsetter å gjøre dette til det når toppen av stakken ELLER blir fanget. Hvis den når toppen av stakken og aldri blir kalt, vil du få en dødelig feil.

For eksempel, her har vi en funksjon som kaster et unntak. Vi kaller den funksjonen fra en annen funksjon. Og til slutt kaller vi den andre funksjonen fra hovedkoden for å demonstrere denne boblende effekten:

 funksjonslinje () kaste ny unntak ('Melding fra bar ().');  funksjon foo () bar ();  prøv foo ();  fangst (Unntak $ e) echo 'Fanget unntak:', $ e-> getMessage (), "\ n"; 

Så, når vi kaller foo (), prøver vi å fange eventuelle unntak. Selv om foo () ikke kaster en, men bar () gjør det fortsatt bobler opp og blir fanget øverst, så vi får en utgang som sier: "Fanget unntak: Melding fra bar ()."


4 Sporing av unntak

Siden unntakene bobler opp, kan de komme fra hvor som helst. For å gjøre jobben enklere har Exception-klassen metoder som gjør at vi kan spore kilden til hvert unntak.

La oss se et eksempel som involverer flere filer og flere klasser.

Først har vi en brukerklasse, og vi lagrer den som user.php:

 klassen bruker offentlig $ navn; offentlig $ e-post; offentlig funksjon lagre () $ v = ny Validator (); $ V-> validate_email ($ dette-> e); // ... lagre ekko "Bruker lagret."; returnere sant; 

Den bruker en annen klasse som heter Validator, som vi legger inn validator.php:

 klassevalider (offentlig funksjon validate_email ($ email) if (! filter_var ($ email, FILTER_VALIDATE_EMAIL)) kaste ny unntak ('Email er ugyldig'); 

Fra hovedkoden skal vi opprette et nytt brukerobjekt, angi navn og e-postverdier. Når vi kaller save () -metoden, vil den bruke Validator-klassen for å sjekke e-postformatet, noe som kan gi et unntak:

 omfatte ( 'user.php'); omfatte ( 'validator.php'); $ u = ny bruker (); $ u-> name = 'foo'; $ u-> email = '$!% # $% # *'; $ U-> Lagre ();

Vi vil imidlertid gjerne ta unntaket, så det er ingen dødelig feilmelding. Og denne gangen skal vi se nærmere på detaljert informasjon om dette unntaket:

 omfatte ( 'user.php'); omfatte ( 'validator.php'); prøv $ u = ny bruker (); $ u-> name = 'foo'; $ u-> email = '$!% # $% # *'; $ U-> Lagre ();  fangst (Unntak $ e) echo "Melding:". $ E-> GetMessage (). "\ N \ n"; ekko "Fil:". $ E-> getFile (). "\ N \ n"; ekko "Linje:". $ E-> getline (). "\ N \ n"; ekko "Spor: \ n". $ E-> getTraceAsString (). "\ N \ n"; 

Koden ovenfor gir denne utgangen:

 Melding: E-post er ugyldig Fil: C: \ wamp \ www \ test \ validator.php Linje: 7 Spor: # 0 C: \ wamp \ www \ test \ user.php (11): Validator-> validate_email ('$! % # $% # * ') # 1 C: \ wamp \ www \ test \ test.php (12): Bruker-> lagre () # 2 main

Så, uten å se på en enkelt linje med kode, kan vi fortelle hvor unntaket kom fra. Vi kan se filnavnet, linjenummeret, unntaksmeldingen og mer. Spordataene viser selv de nøyaktige kodelinjene som ble utført.

Strukturen til standardeksepsjonen er vist i PHP-håndboken, der du kan se alle metodene og dataene som følger med:



5 Utvide Unntak

Siden dette er et objektorientert konsept og Unntak er en klasse, kan vi faktisk utvide den til å skape våre egne unntak.

For eksempel vil du kanskje ikke vise alle detaljene i et unntak til brukeren. I stedet kan du vise en brukervennlig melding og logge feilmeldingen internt:

 // for å brukes til databaseproblemer klasse DatabaseException utvider Unntak // du kan legge til noen tilpassede metoder offentlig funksjonlogg () // logg denne feilen et sted // ... // for å brukes til filsystemproblemer klasse FileException strekker seg Unntak // ...

Vi har nettopp opprettet to nye typer unntak. Og de kan ha egne metoder.

Når vi fanger unntaket, kan vi vise en fast melding, og ringe de tilpassede metodene internt:

 funksjon foo () // ... // noe galt skjedde med databasen kaste ny DatabaseException ();  prøv // sett all koden din her // ... foo ();  catch (FileException $ e) die ("Vi ser ut til å ha filsystemproblemer. Vi beklager ulempen.");  fangst (DatabaseException $ e) // kaller vår nye metode $ e-> logg (); // avslutte med en melding dør ("Vi ser ut til å ha databaseproblemer. Vi beklager ulempen.");  fangst (Unntak $ e) echo 'Fanget unntak:'. $ E-> GetMessage (). "\ N"; 

Dette er første gang vi ser på et eksempel med flere fangstblokker for en enkelt prøveblokk. Slik kan du fange forskjellige typer unntak, slik at du kan håndtere dem annerledes.

I dette tilfellet vil vi fange en DatabaseException, og bare at fangstblokken vil bli henrettet. I denne bloggen kan vi ringe til våre nye tilpassede metoder, og vise en enkel melding til brukeren.

Vær oppmerksom på at fangstblokken med standard unntaksklasse må komme sist, da våre nye barneklasser også vurderes som klasse. For eksempel er "DatabaseException" også ansett som "Unntak", så det kan bli fanget der om bestillingen er den andre veien.


6 Håndtering av uncaught unntak

Det kan hende du ikke alltid vil se etter unntak i hele koden din, ved å pakke inn alt i fangstblokkene. Imidlertid viser uncaught unntak en detaljert feilmelding til brukeren, som heller ikke er ideell i et produksjonsmiljø.

Det er faktisk en måte å sentralisere håndteringen av alle uncaught unntak, slik at du kan kontrollere produksjonen fra et enkelt sted.

For dette skal vi benytte funksjonen set_exception_handler ():

 set_exception_handler ( 'exception_handler'); funksjon exception_handler ($ e) // public message echo "Noe gikk galt. \ n"; // semi-hidden message echo "

Som du kan se skriptet avbrutt etter det første unntaket og ikke utførte den andre. Dette er den forventede oppførselen til uncaught unntak.

Hvis du vil at skriptet ditt skal fortsette å kjøre etter et unntak, må du bruke en prøvefelt-blokk i stedet.


7 Opprette en MySQL-unntaksklasse

Vi skal ferdigstille denne opplæringen ved å bygge en tilpasset MySQL Exception-klasse som har noen nyttige funksjoner, og se hvordan vi kan bruke den.

 klasse MysqlException utvider Unntak // bane til loggfilen privat $ log_file = 'mysql_errors.txt'; offentlig funksjon __construct () $ code = mysql_errno (); $ message = mysql_error (); // åpne loggfilen for å legge til hvis ($ fp = fopen ($ this-> log_file, 'a')) // konstruer loggmeldingen $ log_msg = date ("[Y-m-d H: i: s]"). "Kode: $ kode -". "Melding: $ melding \ n"; skriv ut ($ fp, $ log_msg); fclose ($ fp);  // ring foreldre konstruktør foreldre :: __ konstruere ($ melding, $ kode); 

Du kan legge merke til at vi legger stort sett hele koden i kontruktoren. Når et unntak kastes, er det som å skape et nytt objekt, derfor er konstruktøren alltid kalt først. På slutten av konstruktøren sørger vi også for å ringe foreldrekonstruktøren.

Dette unntaket blir kastet når vi støter på en MySQL-feil. Det vil da hente feilnummeret, og meldingen direkte fra mysql, og lagre den informasjonen i en loggfil sammen med tidsstempel. I vår kode kan vi få dette unntaket, vise en enkel melding til brukeren, og la unntaksklassen håndtere loggingen for oss.

For eksempel, la oss prøve å koble til MySQL uten å gi noen bruker / passord informasjon:

 prøv // forsøk på å koble til hvis (! @mysql_connect ()) kaste ny MysqlException;  catch (MysqlException $ e) die ("Vi ser ut til å ha databaseproblemer. Vi beklager ulempen."); 

Vi må forhåndsbestille feilsøkingsoperatøren (@) før mysql_connect () -samtalen, slik at den ikke viser feilen til brukeren. Hvis funksjonen mislykkes, kaster vi et unntak, og så tar det. Bare vår brukervennlige melding vil bli vist til nettleseren.

MysqlException-klassen tar seg av feilloggingen automatisk. Når du åpner loggfilen, finner du denne linjen:

 [2010-05-05 21:41:23] Kode: 1045 - Melding: Tilgang nektet for bruker 'SYSTEM' @ 'localhost' (bruker passord: NEI)

La oss legge til mer kode i vårt eksempel, og gi også en korrekt innloggingsinformasjon:

 prøv // forbindelsen skal fungere bra hvis (! @mysql_connect ('localhost', 'root', ')) kaste ny MysqlException; // velg en database (som kanskje ikke eksisterer) hvis (! mysql_select_db (' my_db ' )) kast ny MysqlException; // forsøk et spørsmål (som kan ha en syntaksfeil) hvis (! $ result = mysql_query ("INSERT INTO foo SET bar = '42")) kaste ny MysqlException; MysqlException $ e) die ("Vi ser ut til å ha databaseproblemer. Vi beklager ulempen.");

Hvis databasetilkoblingen lykkes, men databasen 'my_db' mangler, finner du dette i loggene:

 [2010-05-05 21:55:44] Kode: 1049 - Melding: Ukjent database 'my_db'

Hvis databasen er der, men spørringen mislykkes, for eksempel på grunn av en syntaksfeil kan du se dette i loggen:

 [2010-05-05 21:58:26] Kode: 1064 - Melding: Du har en feil i SQL-syntaksen din; sjekk håndboken som tilsvarer din MySQL-serverversjon for høyre syntaks for å bruke nær "42" på linje 1

Bonus Bygg en DB klasse med PHP Magic

Vi kan gjøre kodeeksemplet ovenfor enda renere ved å skrive vår egen databaseklasse, som håndterer kaste av unntakene. Denne gangen skal jeg bruke noen "magiske" funksjoner i PHP for å konstruere denne klassen.

På slutten vil vi at vår hovedkode ser slik ut:

 Prøv Database :: connect ('localhost', 'root', '); Database :: select_db (' test '); $ resultat = Database :: spørring ("INSERT INTO foo SET bar = '42"); (MysqlException $ e) die ("Vi ser ut til å ha databaseproblemer. Vi beklager ulempen.");

Det er rent og pent. Vi kontrollerer ikke for eventuelle feil i hver enkelt databasesamtale. Denne nye databaseklassen har ansvaret for å kaste unntak når feil oppstår. Og siden disse unntakene bobler opp, blir de fanget av vår fangstblokk på slutten.

Og her er den magiske databaseklassen:

 klasse database // en hvilken som helst statisk funksjon kalles på denne offentlige statiske funksjonen __callStatic ($ navn, $ args) // funksjon som skal kalles $ function = 'mysql_'. $ Navn; // eksisterer funksjonen? hvis (! function_exists ($ function)) // kaste en vanlig unntak kaste ny unntak ("Ugyldig mysql funksjon: $ funksjon.");  // ring mysql-funksjonen $ ret = @call_user_func_array ($ -funksjon, $ args); // de returnerer FALSE på feil hvis ($ ret === FALSE) // kaste db unntak kaste ny MysqlException;  // returner returverdien retur $ ret; 

Den har bare en metode, og det blir påkalt når vi kaller en statisk metode på denne klassen. Du kan lese mer om dette konseptet her: PHP Overloading.

Så, når vi kaller Database :: connect (), kaller denne koden automatisk mysql_connect (), sender argumentene, sjekker om feil, kaster unntak når det trengs, og returnerer den returnerte verdien fra funksjonssamtalen. Det fungerer i utgangspunktet som en middelmann, og håndterer det skitne arbeidet.


Konklusjon

Jeg håper du likte denne opplæringen og lærte av det. Nå bør du få en bedre forståelse av dette emnet. Prøv å se om du kan bruke PHP-unntak i ditt neste prosjekt. Ser deg neste gang!