Работа с изключения в PHP – try / catch

Изключенията се използват за да пренасочотат нормалното поведение на скрипта при появата на специфични грешки.

Какво е „изключение“

От PHP 5 е наличен нов обектно ориентиран начин за работа с грешки – а както може би вече сте разбрали – грешки в програмирането стават :). Засичането на изключения(Exception handling) се използва за пренасочване на очакваното поведение на скрипта по време на изпълнението му в случай на поява на предварително зададено условие за грешка(exceptional). Това условие се нарича „изключение“ /exception/. Следните неща се случват обикновено когато дадено „изключение“ бива извикано:

  • Моментното състояние на кода се запаза
  • Изпълнението на кода се пренасочва към предварително зададена функция /exception handler function/
  • В зависивост от ситуацията прекъсването може да продължи изпълнението на скрипта от момента на запазването(виж горе), да прекрати изпълнението на скрипта или да продължи изпълнението от различна позиция в кода.

Тук ще изложим различните начини за хващане на грешка /error handling/:

  • Обикновено ползване на Exceptions
  • Създаване на дефинирано от нас хващане на грешка /exception handler/
  • Множество „изключения“
  • Обхождане наново /Re-throwing an exception/
  • Задаване на top level прихващач за грешки /exception handler/

Обърнете внимание:
Изключенията следва да се ползват с условности за грешки и не трябва да се ползват просто за прескачане на кода до друга позиция. Има причини за това – например ползването на сървърни ресурси, което е добре да имаме предвид.


Обичайно ползване на изключения/Exceptions/

Когато едно изключение е налично последващият код няма да се изпълни и PHP ще опита да намери блока от код който отговаря за въпросното изключение. Ако „изключение“ не е дефинирано грешката ще възпроизведе „фатална грешка“ и съобщение с текст „Uncaught Exception“.

Кода горе ще ни даде грешка като тази. Разбира се, пътя до файла най-вероятно при вас ще е различен, а може и форматирането на текста да малко по-различно.

Fatal error: Uncaught exception ‘Exception’
with message ‘Value must be 1 or below’ in C:\webfolder\test.php:6
Stack trace: #0 C:\webfolder\test.php(12):
checkNum(28) #1 {main} thrown in C:\webfolder\test.php on line 6

Опитай, прехвърли(пренасочи) и хвани /Try, throw and catch/

За да избегнем грешка като в горния пример трябва да създадем правилния код за да хванем изключение. Правилно изпълнения код трябва да включва:

  1. Try – PHP функция използваща изключение трябва да бъде в try блок от код. Ако дадено изключение не е извикано кода ще продължи нормалното си изпълнение. Обаче ако изключение е налично то в такъв случай е „хвърлено изключение“ /an exception is „thrown“/
  2. Throw – Това е термин, който се отнася до блокът от код – кодът който указва как се изпълнява дадено изключение. Всеки блок throw трябва да има поне един catch блок
  3. Catch – един catch блок обработва едно изключение и създава обект съдържащ информация за изключението.

Сега да опитаме да извикаме изключение с валиден код:


Обяснение на примера:

Кода горе хвърля изключение и го после хваща:

  1. Създаваме функцията checkNum(). Тя проверява дали числото е по-голямо от 1. Ако е по-голямо се извиква exception.
  2. Функцията checkNum() се изпълнява в блокът от код try
  3. Изключението /exception/ във футкцията checkNum() е хвърлено /thrown/
  4. Блокът от код в catch получава информация за изключението /exception/ и създава обект, който можем да изполваме вътре в него
  5. Съобщението за грешка се изписва като извикаме метода getMessage() от обекта $e по следният начин: $e->getMessage()

Обаче един начин да заобиколим правилото, че „всеки опит трябва да има хващане“ е като зададем топ левел изключение с което да хващаме грещки, които се поднасят. Продължавай да четеш надолу…

Създаване на наш собствен клас – Exception Class

За създаването на такъв прихващач трябва да създадем наш собствен клас с функции които да бъдат извиквани, когато е засечено наличието на грешка или грешки. Този клас трябва да е разширение на класа Exception – тоест трябва да наследява класът Exception. Така се наследяват всички свойства от PHP класа Exception и чрез това можем да добавяме свои функции към него. Сега да създадем един клас:

Новият клас е копие на наследеният, но добавя errorMessage() функция. Тъй като това е копие от старият клас и наследява свойствата и методите от стария клас, можем да използваме методи като getLine() и getFile() и getMessage().
Следва обяснение на примера:

  1. Класът customException() е създаден като разширение на класа Exception. По този начин наследява всички методи и свойства от него.
  2. Функцията errorMessage() е създадена. Тази функция връща съобщение за грешка, ако адресът на електронната поща е невалиден
  3. Променливата $email е със стойност, който не е валиден имейл адрес
  4. Пробният блок се изпълнява и хвърля изключение, тъй като адресът на електронната поща е невалиден
  5. Блокът „catch“ улавя изключението и показва съобщението за грешка

Multiple Exceptions

Възможно е скриптът да използва няколко изключения, за да провери за множество условия. Възможно е да се използват няколко блока if…else, switch или да се вложат няколко изключения еднов друго. Тези изключения могат да използват различни класове с изключения и да връщат различни съобщения за грешки. След примера следва и обяснението му.

  1. Класът customException() е създаден като разширение на класа Exception. По този начин наследява всички методи и свойства от него.
  2. Функцията errorMessage() е създадена. Тази функция връща съобщение за грешка, ако адресът на електронната поща е невалиден
  3. Променливата $email е със стойност, която не е валиден имейл адрес
  4. Блокът „try“ се изпълнява и не се хвърля изключение при първото условие
  5. Второто условие задейства изключение, тъй като електронната поща съдържа низа „example“
  6. Блокът „catch“ улавя изключението и показва правилното съобщение за грешка

Ако изключението е хвърлено от класа customException и не е имало customException catch, само основното изключение ще бъде отразено и изключението ще се обработва там.

Re-throwing Exceptions

Понякога, когато се ихвърля изключение, може да искате да се справите      различно от стандартния начин. Възможно е да се повтори хвърляне на изключение в рамките на един „try“ блок.
Скриптът трябва да скрива системни грешки от потребителите. Системните грешки може да са важни      за програмиста, но не представляват интерес за потребителя. За да направите нещата по-лесни за      потребителя може да се повтори хвърлянето на изключението за да може по този начин да се изведе някакво по-ясно за потребителя съобщение:

Кодът по-горе тества дали имейл адресът съдържа низа „example“ в себе си, ако го направи, изключението се изхвърля отново:

  1. Класът customException() е създаден като разширение на класа Exception. По този начин наследява всички методи и свойства от него.
  2. Функцията errorMessage() е създадена. Тази функция връща съобщение за грешка, ако адресът на електронната поща е невалиден
  3. Променливата $email е със стойност, която не е валиден имейл адрес
  4. Блокът „try“ съдържа друг блок „try“, за да направи възможно изчистването на изключението
  5. Изключението се задейства, тъй като електронната поща съдържа низа „example“
  6. Блокът „catch“ улавя изключението и повторно хвърля „customException“
  7. customException() се улавя и показва съобщение за грешка

Ако изключението не бъде хвърлено в текущия блок „try“, той ще търси блок за изпълнение на „по-високи нива“.

Задаване на Top Level Exception Handler

Функцията set_exception_handler() определя функция, дефинирана от потребителя, за да се справи с всички неуловени изключения.

Резултатът от кода по-горе трябва да е нещо като това:

Exception: Uncaught Exception occurred

В горния код не е имало „catch“ блок. Вместо това се изпълнява exception handler от най-високото ниво. Тази функция трябва да се използва, за да се прихване неуловено изключение /uncaught exception/.


Правила за изключенията

  • Кодът трябва да бъде дефиниран в try блок, за да се уловят потенциалните изключения
  • Всеки try блок или throw трябва да има поне един съответстващ блок за прихващане
  • Няколко catch блока могат да се използват за улавяне на различни класове за изключения
  • Изключения могат да бъдат хвърлени /thrown/ (или повторно хвърлени/re-thrown/) в catch блок в try блок

Просто правило: Ако хвърлите нещо, трябва да го хванете.