PHP5_nachinayushim
.pdf254 Глава 5
Вторая строка удаляет ограничивающие символы косой черты или обратной косой черты:
$outpath = ereg_replace("^[\/]+", "", $outpath);
Третья строка удаляет префиксы DOS/Windows+стиля (например, "C:\"):
$outpath = ereg_replace("\^[A-Za-z][:\|][\/]?", "", $outpath);
Изящная обработка ошибок
Главное отличие между изящной и неряшливой обработкой ошибок заключается
втом, что видит пользователь. Конечно, правильная обработка ошибок в программе может быть реализована по+разному, но для пользователя, который только что пытал+ ся открыть какую+либо страницу, гораздо более понятным будет четко написанное со+ общение, а не просто встроенное PHP+сообщение об ошибке.
Как и многие другие языки программирования, PHP постепенно становится более зрелым. В PHP5 появились новые встроенные возможности обработки ошибок (они рассматриваются в следующем разделе). Для начала следует описать, в чем заключает+ ся обработка ошибок, а затем рассмотреть обработку ошибок с момента загрузки PHP
всистеме и до выполнения программ в реальном времени.
Конфигурирование обработки ошибок в PHP
Главными конфигурационными параметрами, которые следует использовать в разрабатываемых (а не в уже действующих) системах, являются уровень ошибок (error_reporting), настройка отображения сообщений об ошибках (display_errors) и параметр log_errors. В приложении ‘‘Конфигурация PHP5’’ рассматриваются и мно+ гие другие настройки, однако данные три параметра вызывают наибольший интерес. Первый параметр устанавливает уровень отображаемых ошибок, второй включает или выключает отображение сообщений об ошибках в браузере (в реально действующих сайтах отображать стандартные сообщения об ошибках нежелательно), а третий вклю+ чает или выключает протоколирование ошибок в файле журнала.
Подавление сообщений об ошибках
Подавление ошибок, строго говоря, нельзя назвать элементом отладки, но иногда известно, что ошибки в PHP+сценарии есть и избавляться от соответствующих сооб+ щений нежелательно, необходимо просто проигнорировать их или обработать ошиб+ ки особым образом. Для таких ситуаций в PHP используется специальная форма запи+ си: @+нотация. Когда символ @ используется с какой+либо функцией, PHP подавляет все сообщения об ошибках, возникающие в данной функции. Например, следующая функция вызывается с предшествующим символом @:
function ProcessFormDetails ($Name, $Email)
{
//какая-либо обработка
}
@ProcessFormDetails($Name, $Email);
Благодаря такой записи любые ошибки внутри данной функции не отображаются. Конечно, если в функции есть ошибка, то все равно возвращается нулевое значение. Если эта ошибка критическая и в обычных условиях привела бы к остановке сценария,
Надежный и понятный код 255
то она и в этом случае останавливает сценарий, но не выводит на экран каких+либо сообщений. И хотя подавление сообщений об ошибках может привести к некоторой путанице, оно, тем не менее, остается полезным средством. Оно позволяет обрабо+ тать ошибку отдельно внутри предназначенной для этого функции, которая возвра+ щает пользователю специальное сообщение.
Проверка журнала ошибок
По умолчанию PHP не записывает сообщения об ошибках в журнал Web+сервера, однако такое поведение можно исправить. PHP может протоколировать ошибки в файл журнала, если в файле php.ini присутствуют следующие директивы:
log_errors = On
error_log = /var/log/php.log
В PHP также имеется функция error_log, которая позволяет протоколировать не+ ожиданные ситуации, не предусмотренные программистом и не имеющие специального способа программной обработки. Это позволяет изучать возникающие ошибки, даже если вывод соответствующих сообщений был подавлен описанным выше способом.
Функция error_log может принимать до четырех аргументов, но обязательными являются только первые два из них. Функция имеет следующий формат:
error_log("Error Message", MessageType, "Destination", "Extra Headers");
Первый аргумент Error Message представляет собой фактическое сообщение об ошибке. Второй аргумент, MessageType, ++++++ код, означающий то место, куда необхо+ димо отправлять сообщение об ошибке; существует четыре возможных значения это+ го аргумента:
0:сообщения отправляются в журнал ошибок PHP;
1:сообщения отправляются на e+mail+адрес, заданный третьим аргументом;
2:сообщения отправляются в отладочный канал PHP (только если включен ре+ жим отладки);
3:сообщения отправляются в файл журнала, путь к которому задается третьим аргументом.
Третий аргумент, Destination, принимает либо e+mail+адрес, либо путь к файлу, а четвертый аргумент, Extra Headers, можно использовать для отправки дополни+ тельной информации в форме e+mail+заголовоков.
Чтобы получить подробные сведения о возникшей ошибке, можно включить про+ токолирование ошибок в журнал или использовать функцию error_log, а затем про+ смотреть содержащуюся в журнале информацию.
Try/Catch — нововведения в PHP5
Несколько новых средств в PHP5 позволяют программам изящно реагировать на возникающие ошибки. Речь идет о новых возможностях по обработке исключитель+ ных ситуаций ++++++ блоках try/catch. По существу, создается блок try и в него помещает+ ся обрабатываемый код, который может генерировать ошибки. Затем в зависимости от сгенерированной ошибки используется блок catch, в котором принимается реше+ ние о том, что следует делать с этой ошибкой. Одно из важных преимуществ такой схемы состоит в том, что она облегчает перехват ошибок в блоках функций.
256 Глава 5
Функциональность try/catch генерирует исключения, а исключения являются объек+ тами. Из блока try с помощью оператора new Exception ‘‘выбрасываются’’ исключе+ ния, которыми затем занимается блок catch. Созданное исключение представляет со+ бой объект, его можно использовать так же, как и любой другой объект (подробнее объекты рассматриваются в этой книге далее). Метод getMessage объекта Exception позволяет получить сообщение, связанное с возникшей ошибкой. Использование функциональности try/catch наглядно демонстрируется в следующем примере.
Практика Использование блоков try/catch для проверки данных формы
1. Запустите HTML+редакотор и создайте файл со следующим кодом:
<html>
<head><title>PHP5 для начинающих</title></head> <body bgcolor="#FFFFFF">
<?php
if (isset($_POST['posted'])) {
//записать переданные значения в обычные переменные
$first_name = $_POST['first_name']; $last_name = $_POST['last_name']; $birth_date = $_POST['birth_date']; $phone = $_POST['phone'];
$age = $_POST['age']; $address = $_POST['address']; $city = $_POST['city']; $state = $_POST['state'];
$postal_code = $_POST['postal_code'];
//создать массив из имен полей и типов данных
$field_names = array("first_name" => "string", "last_name" => "string", "birth_date" => "date",
"phone" => "string", "age" => "integer", "address" => "string", "city" => "string", "state" => "string",
"postal_code" => "string");
//по имени поля проверить тип данных каждого переданного значения
function form_validate($fns) {
foreach ($fns as $key => $value) { $field_value = $key;
global $$field_value;
//echo "фактическое значение поля " . $$field_value . "<br>"; switch ($value) {
Case "string";
if ((strlen($$field_value) < 1) or (strlen($$field_value) > 99)) { throw new Exception("Пожалуйста, введите в поле
<b>$key</b> строку от 1 до 100 символов");
}
break; Case "date";
if (!ereg("^[0-9]{4}\-([1-9]|(0[1-9])|(1[0-2]))\-([1-9]| (0[1-9])|([1-2][0-9])|3[0-1])$",$$field_value)) {
Надежный и понятный код 257
throw new Exception("Пожалуйста, введите в поле <b>$key</b>
корректную дату в формате ГГГГ-ММ-ДД");
}
break;
Case "integer";
if (!is_numeric($$field_value)) {
throw new Exception("Пожалуйста, введите в поле <b>$key</b>
число без десятичных разделителей или алфавитных символов.");
}
break;
default;
break;
}
}
}
//перехватить исключение и сгенерировать сообщение об ошибке try
{
form_validate($field_names);
}
catch (Exception $e)
{
echo $e -> getMessage(); echo "<br>";
}
}
//если ошибок не было, то поблагодарить пользователя
if (!is_object($e) and isset($posted)) {
echo "Спасибо за информацию, мы свяжемся с Вами."; } else {
//вернуть пользователю заполненную форму и предложить ввести исправленные данные
?>
<form action="try_catch.php" method=post> <input type="hidden" name="posted" value="true">
<table width="50%" border="1"> <tr>
<td colspan="2"><font face="Arial, Helvetica, sans-serif" size="-1">
Пожалуйста, введите контактную информацию:</font></td> </tr>
<tr>
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1">Имя </font></td>
<td width="74%">
<input type="text" name="first_name" value="<?php echo $first_name; ?>"> </td>
</tr>
<tr>
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1">
Фамилия
</font></td> <td width="74%">
<input type="text" name="last_name" value="<?php echo $last_name; ?>"> </td>
</tr>
<tr>
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1">
Дата рождения</font></td> <td width="74%">
<input type="text" name="birth_date" value="<?php echo $birth_date; ?>"> </td>
</tr>
<tr>
258 Глава 5
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1">
Номер телефона</font></td> <td width="74%">
<input type="text" name="phone" value="<?php echo $phone; ?>"> </td>
</tr>
<tr>
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1"> Возраст</font></td>
<td width="74%">
<input type="text" name="age" value="<?php echo $age; ?>"> </td>
</tr>
<tr>
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1"> Адрес</font></td>
<td width="74%">
<input type="text" name="address" value="<?php echo $address; ?>"> </td>
</tr>
<tr>
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1"> Город</font></td>
<td width="74%">
<input type="text" name="city" value="<?php echo $city; ?>"> </td>
</tr>
<tr>
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1"> Штат</font></td>
<td width="74%">
<input type="text" name="state" value="<?php echo $state; ?>"> </td>
</tr>
<tr>
<td width="26%"><font face="Arial, Helvetica, sans-serif" size="-1">
Почтовый индекс</font></td> <td width="74%">
<input type="text" name="postal_code" value="<?php echo $postal_code; ?>"> <input type="submit" value="Отправить информацию" name="submit">
</td>
</tr>
</table>
</form>
<?php
}
?>
</body>
</html>
2.Сохраните данный файл как try_catch.php и закройте его.
3. Откройте файл в браузере (рис. 5.7), введите значения для каждого поля и просмотрите полученные после отправки формы сообщения об ошибках. На рис. 5.8 и 5.9 показаны некоторые примеры сообщений об ошибках.
Если все данные ввести правильно, то появится сообщение с благодарностью.
Надежный и понятный код 259
Рис. 5.7.
Как это работает
Программа начинается с формы, в которую пользователь вводит данные. Эти дан+ ные должны быть определенных типов. Форму можно было бы легко расширить для многих других типов данных. Затем, как и во всех остальных примерах, чтобы опре+ делить, была ли отправлена данная форма, используется функция isset() (в данном случае проверка выполняется в нескольких местах). Переданные данные записывают+ ся в обычные переменные, и создается массив имен полей и типов данных, который будет использоваться позднее для настройки подпрограмм проверки данных. Массив выглядит следующим образом:
//создать массив из имен полей и типов данных
$field_names = array("first_name" => "string", "last_name" => "string", "birth_date" => "date",
"phone" => "string", "age" => "integer", "address" => "string", "city" => "string", "state" => "string",
"postal_code" => "string");
260 Глава 5
Рис. 5.8.
Рис. 5.9.
Надежный и понятный код 261
Разработка баз данных в книге пока еще не рассматривалась (эта тема описана в нескольких последующих главах), тем не менее, следует отметить, что имена полей и типы данных (а также другую информацию для проверки формы) можно легко по+ лучить из базы данных. На самом деле можно было бы хранить в базе данных большое количество сведений о форме и процессе проверки данных этой формы. И хотя такой подход требует несколько больших трудозатрат, заказчик приложения впоследствии сможет легко изменить количество и тип отображаемых в форме полей, зная, что форма все равно будет обрабатываться соответствующим образом.
Затем создается функция, осуществляющая проверку данных, переданных фор+ мой. Для того чтобы процесс проверки охватил все поля формы, внутри функции ис+ пользуется цикл foreach, проверяющий имена полей и их значения. Создавать дан+ ную функцию было необязательно, однако это облегчило бы работу, если бы надо было проверять несколько массивов значений полей. Функция начинается так:
function form_validate($fns) {
foreach ($fns as $key => $value) {
Затем имя поля передается в переменную, после чего используется способность PHP обращаться к значению определенного поля переданной HTML+формы (необходимо использовать ключевое слово global, потому что внутри области види+ мости функции внешние переменные не видны; функции и этот аспект их работы подробнее рассматриваются в главе 6):
$field_value = $key;
global $$field_value;
Затем используются Case+блоки оператора switch для выделения типов провер+ ки. Внутри блока выполняются различные проверки переданных значений:
switch ($value) { Case "string";
if ((strlen($$field_value) < 1) or (strlen($$field_value) > 99)) {
throw new Exception("Пожалуйста, введите в поле <b>$key</b> строку от 1 до 100 символов");
}
break; Case "date";
if (!ereg("^[0-9]{4}\-([1-9]|(0[1-9])|(1[0-2]))\-([1-9]|(0[1-9])|([1-2]
[0-9])|3[0-1])$",$$field_value)) {
throw new Exception("Пожалуйста, введите в поле <b>$key</b> корректную дату в формате ГГГГ-ММ-ДД");
}
break;
Case "integer";
if (!is_numeric($$field_value)) {
throw new Exception("Пожалуйста, введите в поле <b>$key</b> число без десятичных разделителей или алфавитных символов.");
}
break;
default;
break;
}
Обратите внимание, что оператор throw new Exception выполняется, когда значение не проходит какую+либо проверку. Например, если длина строкового значе+ ния не попадает в пределы 1++++99 символов, то генерируется исключение. Во время создания исключения ему назначается сообщение ++++++ строковое значение в круглых скобках после ключевого слова Exception.
262 Глава 5
Чтобы функция обрабатывала переданные значения, используются блоки try/catch. Функция выполняется внутри блока try, а в блоке catch перехватывается каждое исключение и выводится соответствующее сообщение. (В блоке catch при желании можно реализовать любой вид обработки ошибок.) Кроме того, если возни+ кает исключение, то работа функции прекращается и создается объект Exception (исключение) со своим сообщением:
try
{
form_validate($field_names); } catch (Exception $e)
{
echo $e -> getMessage(); echo "<br>";
}
Последняя особенность заключается в том, что если возникает ошибка, то ото+ бражаются введенное пользователем значение и сообщение об ошибке, чтобы поль+ зователь мог исправить проблему, не вводя все данные заново. Если ошибок не было, то выводится сообщение с благодарностью. Поля формы заполняются существующи+ ми данными, потому что в атрибутах value всех полей вызывается оператор echo, отображающий переданные данные (когда форма отображается впервые, атрибут value, естественно, не содержит никаких данных). В следующем блоке if выполня+ ется проверка того, возвращается ли пользователю сообщение с благодарностью. Для этого необходимо проверить, является ли исключение ($e) объектом, а также была ли форма отправлена:
//если ошибок не было, то поблагодарить пользователя
if (!is_object($e) and isset($posted)) {
echo "Спасибо за информацию, мы свяжемся с Вами."; } else {
//вернуть пользователю заполненную форму и предложить ввести исправленные данные
?>
Резюме
В данной главе рассматривались базовые функции тестирования, локализации не+ исправностей, отладки и обработки ошибок, а также проверка строк (для перехвата некорректных данных до того, как они нарушат работу программы), регулярные вы+ ражения (для улучшенной проверки) и новые функции PHP5 try/catch.
Дефекты возникают в каждой программе. Некоторые дефекты являются результа+ том неправильного синтаксиса, другие ++++++ результатом логических ошибок (когда об+ работка выполняется, но при этом правильный ответ не генерируется, или обработка выполняется верно, но генерирует неверный ответ из+за ввода неправильных дан+ ных). Найти и исправить ошибки любого типа можно с помощью поэтапного процес+ са тестирования, локализации неисправностей и отладки, который повторяется снова и снова до тех пор, пока программа не будет работать верно.
Организуя обработку ошибок в приложении, разработчик ограничен лишь рамка+ ми имеющегося времени и навыками программирования. Как было показано в этой главе, проверка ошибок не только помогает избежать неприглядных и потенциально опасных сообщений об ошибках, но и дает возможность убедиться, что там, где это требуется, пользователи вводят соответствующие данные. Способ обработки входных
Надежный и понятный код 263
данных полностью определяется разработчиком. Чем ‘‘умнее’’ будет сценарий при обнаружении и обработке неправильного ввода, тем лучше будут генерируемые им данные. Чтобы добиться этого, можно использовать множество функций проверки строк и регулярных выражений, описанных в этой главе.
Планирование (и желательно разумное использование регулярных выражений) при обработке ошибок может значительно улучшить Web+сайт. Не забывайте закон Мерфи: если какая+либо ошибка может возникнуть, то она обязательно возникнет. Помня об этом, можно избежать неприятностей ++++++ проверить все места, где возникно+ вение ошибки наиболее вероятно, и соответствующим образом исправить сценарии. Предусмотрительность поможет сократить большое количество проблем в будущем.
Упражнение
Создайте не менее трех регулярных выражений и вставьте их в соответствующие места последнего примера в данной главе. Примерные регулярные выражения:
семи+ и десятизначные номера телефонов в США;
номера социального обеспечения;
буква для обозначения пола пользователя (М ++++++ для мужчин и Ж ++++++ для женщин).