Сейчас мои страницы выглядят примерно так:
if($_GET['something'] == 'somevalue') { $output .= 'somecode'; // make a DB query, fetch a row //... $row = $stmt->Fetch(PDO::ASSOC); if($row != null) { $output .= 'morecode'; if(somethingIsOK()) { $output .= 'yet more page output'; } else { $error = 'something is most definitely not OK.'; } } else { $error = 'the row does not exist.'; } } else { $error = 'something is not a valid value'; } if($error == '') // no error { //display $output on page } else // an error { // display whatever error occurred on the page }
То, как я делаю все, работает, но это очень громоздко и утомительно для того, что, вероятно, очевидно: предположим, что я вызываю функцию где-то посередине моего кода или хочу проверить значение переменной или проверить запрос БД вернул действительный результат, и если он сбой, я хочу вывести ошибку? Я должен был бы сделать еще один if / else блок и переместить весь код внутри нового блока if. Это не похоже на умный способ делать что-то.
Я читал о try / catch и думал о том, чтобы поместить весь мой код внутри инструкции try, а затем пусть код запускается без каких-либо блоков if / else, и если что-то не удается просто выбросить исключение. Из того, что я прочитал, это остановит выполнение и заставит его перейти прямо к блоку catch (точно так же, как неудачный оператор if перейдет в блок else), где я мог бы вывести сообщение об ошибке. Но является ли это приемлемой или стандартной практикой?
Каков наилучший способ обработки ошибок, фатальных или нет, в php-приложении, которое создает и выводит HTML-страницу? Я не хочу просто умирать с пустым экраном, так как это было бы очень неприхотливым для пользователя, но вместо этого хотелось бы выводить сообщение в теле страницы, все еще позволяя показывать верхний и нижний колонтитулы.
Спасибо за ваш совет!
Есть много способов, которыми вы можете справиться с этим, и, честно говоря, ни один из них не является по сути «правильным».
Вам придется решить для себя, какой метод более «удобен» для вас – это всегда манера предпочтений (хотя есть определенные методы, которых вам следует избегать и по уважительным причинам).
Это будет сильно зависеть от того, как вы разделите свою логику, однако я склонен заключать весь код, который может возвращать нефатальные ошибки внутри функции, и использовать возвращаемое значение указанной функции, чтобы указать на наличие ошибки.
Для фатальных ошибок я использую исключения (с блоками try-catch
).
Теперь, чтобы быть ясным:
NTP
, но сервер не ответил, вы можете решить использовать локальную функцию time
и по-прежнему отображать некоторые ценные данные для пользователя. SQL Exception
– нет никаких ценных данных, которые будут показаны, и вы можете сообщить об этом только пользователю. Хорошим примером использования функций-возвратов в качестве способа решения проблем, не связанных со смертельным исходом, была бы функция, которая пытается отображать содержимое какого-либо файла на странице, если это не главная цель страницы (например, вы бы функция, которая отображает значки, извлекаемые из текстового файла, на каждой странице – я знаю, что это далека, но неся со мной).
function getBadge($file){ $f = fopen($file,'r'); if(!$f){ return null; } .. do some processing .. return $badges; } $badges = getBadges('badges.txt'); if(!$badges){ echo "Cannot display badges."; } else { echo $badges; } .. carry on doing whatever page should be doing ..
Фактически, функция fopen
сама по себе является примером этого – она вернется .
Возвращает ресурс указателя файла при успешном выполнении или FALSE при ошибке.
Когда у вас есть часть кода, которая должна быть выполнена, потому что это именно то, чего хотел пользователь (например, считывая все новости из базы данных и отображая их пользователю), вы можете использовать исключения. Возьмем простой пример: пользователь посетил его профиль и захотел увидеть все полученные ему сообщения (предположим, что на данный момент они хранятся в виде простого текста). У вас может быть такая функция, как:
function getMessages($user){ $messages = array(); $f = fopen("messages_$user.txt","r"); if(!$f){ throw new Exception("Could not read messages!"); } ... do some processing ... return $messages; }
И используйте его вот так:
try{ ..do some stuff.. $messages = getMessages($_SESSION['user'])); //assuming you store username in $_SESSION foreach($messages as $msg){ echo $msg."<br/>"; } } catch(Exception $e){ echo "Sorry, there was an error: ".$e->getMessage(); }
Теперь это может пригодиться, если у вас есть сценарий «верхнего уровня», который будет выполнять весь другой код. Это означает, что, например, в вашем index.php
вас просто будет:
try{ .. execute some code, perform some functions .. } catch(Exception $e){ echo "Sorry, there was an error: ".$e->getMessage(); }
Независимо от того, что вы делаете, никогда не используйте исключения, чтобы проверить, что вы можете восстановить. Прочтите по другому вопросу (полный кредит принадлежит Антону Гоголеву за очень хорошее объяснение этому, а также другие ответчики) о том, почему это так.
Теперь нет лучшего способа узнать, как справляться с ошибками, чем попробовать несколько вещей и посмотреть, что хорошо для вас. Вы можете найти ниже полезное:
trigger_error()
, о которой я не упоминал, потому что я не использую его и мало знаю об этом, но, по-видимому, он действительно полезен. Это особенно хорошо читается. Надеюсь это поможет 🙂
Это намного элегантнее и удобочитаемо.
try { if($_GET['something'] != 'somevalue') { throw new Exception ('something is not a valid value'); } $output .= 'somecode'; // make a DB query, fetch a row //... $row = $stmt->Fetch(PDO::ASSOC); if($row == null) { throw new Exception ('the row does not exist.'); } $output .= 'morecode'; if(somethingIsOK()) { $output .= 'yet more page output'; } else { throw new Exception ('something is most definitely not OK.'); } echo $output; } catch (Exception $e) { echo $e->getMessage(); }
PHP имеет встроенный класс ErrorException для преобразования ошибок PHP в исключения, которые, если они необработаны, естественно прекратили выполнение.
Исключения улучшили механизмы обработки ошибок (try catch) и лучшую информацию об отладке (трассировки стека).
Включите это в начало большей части вашего пути выполнения (config или что-то, что включено сначала со всем вашим кодом):
set_error_handler(function($nNumber, $strMessage, $strFilePath, $nLineNumber){ throw new \ErrorException($strMessage, 0, $nNumber, $strFilePath, $nLineNumber); }, /*E_ALL*/ -1);
Хотя PDO поддерживает исключение для исключения, он отключен по умолчанию, вы должны включить его:
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
Если вы используете MySQL, вы также хотите, чтобы ошибка не устанавливала обязательные поля и многие другие ошибки / предупреждения, которые были прощены по умолчанию:
$pdo->exec("SET sql_mode = 'STRICT_ALL_TABLES'");
Исключения можно обрабатывать, как и во многих других языках программирования, используя try catch finally :
try { echo $iAmAnUndefinedVariable; } catch(\Throwable $exception) { /*[...]*/ }
При проверке материала просто бросайте исключения: throw new Exception("Missing URL variable userId!");
Было бы неплохо, если бы PHP сделал чистую перерыв когда-нибудь из устаревшей информации об ошибках и просто выбросил исключения по умолчанию (deprecate error_reporting () и изменил значение по умолчанию).
Использование try-catch
– одно из самых чистых решений, которое вы можете использовать.
Я привел пример, который по-прежнему отображает верхний и нижний колонтитулы при возникновении ошибки, используя код, преобразованный в формат try-catch
:
PHP:
<?php try { $output = array(); if($_GET['something'] != 'somevalue') throw new Exception('something does not have a valid value.'); $output[] = 'Some Code'; $row = mt_rand(0, 10) < 5 ? null : mt_rand(0, 100); if($row === null) throw new Exception('The row does not exist.'); $output[] = $row; if(!somethingIsOK()) throw new Exception('Something is most definitely not OK.'); $output[] = 'Yet more page output'; } catch(Exception $e) { $output[] = 'Error: ' . $e->getMessage(); // To show output and error $output = array('Error: ' . $e->getMessage()); // To only show error } function somethingIsOK() { return mt_rand(0, 10) < 5; } ?>
HTML:
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8" /> <title>PHP Error test</title> <style type="text/css"> body { background: #eee; text-align: center } #content { padding: 60px } #header { padding: 30px; background: #fff } #footer { padding: 10px; background: #ddd } </style> </head> <body> <div id="header">Header</div> <div id="content"> <?php echo implode('<br />', $output); ?> </div> <div id="footer">Footer</div> </body> </html>
Рекомендации:
Обработка ошибок ошибок PDO для запросов, и действительно весь код должен быть запущен через:
try{ } catch{ } finally{ }
Причина этого заключается в том, что упростить отладку, когда вы можете точно определить, где в длинных сценариях возникает ошибка
подробнее здесь: http://php.net/manual/en/language.exceptions.php
Создайте обработчик ошибок (set_error_handler) и исключите исключения из него.
Это поможет для функций, которые не поддерживают исключения.
Обрабатывайте ошибки и предупреждения PHP правильно, используя функции обработки ошибок. (См. Пример здесь )
Лучший способ обработки ошибок в PHP – это остановить все сообщения об ошибках, добавив эту строку в верхнюю часть вашего php-файла –
error_reporting(0); //OR error_reporting('E_ALL'); // Predefined Constant
Обработка ошибок в PHP с использованием функций:
Все функции, перечисленные выше, используются для обработки ошибок в PHP.
Если вы ищете структуру кода, которая будет выглядеть красиво и будет работать – вы можете использовать метод « whitelist
который я всегда использую. Например – проверка переменной $_GET
:
$error = false; if(!isset($_GET['var'])) { $error = 'Please enter var\'s value'; } elseif(empty($_GET['var'])) { $error = 'Var shouldn\'t be empty'; } elseif(!ctype_alnum($_GET['var'])) { $error = 'Var should be alphanumeric'; } //if we have no errors -> proceed to db part if(!$error) { //inserting var into database table }
Итак, это все, всего 2 блока if/elseif
, без вложенности