web - tec / PHP 5 для начинающи
.pdf
Надежный и понятный код 253
if (isset($_POST['posted'])) { $path = $_POST['path'];
$outpath = ereg_replace("\.[\.]+", "", $path); $outpath = ereg_replace("^[\/]+", "", $outpath);
$outpath = ereg_replace("^[A-Za-z][:\|][\/]?", "", $outpath); echo "Старый путь: " . $path . " . Новый путь: " . $outpath;
}
?>
<form action="clean_path.php" method="POST">
<input type="hidden" name="posted" value="true">
Введите файловый путь для очистки:
<input type="text" name="path" size="30"> <input type="submit" value="Очистить">
</form>
</body>
</html>
2.Сохраните созданный файл clean_path.php и закройте его.
3.Запустите программу. Ее вывод должен выглядеть примерно так, как показано на рис. 5.6.
Рис. 5.6.
Очевидно, что PHP можно использовать для фильтрации вводимых пользователем данных, которые являются либо потенциально опасными, либо просто некорректными.
Как это работает
Первая строка программы удаляет комбинации ".." (которые используются для перехода на один уровень выше в дереве каталогов):
$outpath = ereg_replace("\.[\.]+", "", $inpath);
254 Глава 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.
Дефекты возникают в каждой программе. Некоторые дефекты являются результа+ том неправильного синтаксиса, другие ++++++ результатом логических ошибок (когда об+ работка выполняется, но при этом правильный ответ не генерируется, или обработка выполняется верно, но генерирует неверный ответ из+за ввода неправильных дан+ ных). Найти и исправить ошибки любого типа можно с помощью поэтапного процес+ са тестирования, локализации неисправностей и отладки, который повторяется снова и снова до тех пор, пока программа не будет работать верно.
Организуя обработку ошибок в приложении, разработчик ограничен лишь рамка+ ми имеющегося времени и навыками программирования. Как было показано в этой главе, проверка ошибок не только помогает избежать неприглядных и потенциально опасных сообщений об ошибках, но и дает возможность убедиться, что там, где это требуется, пользователи вводят соответствующие данные. Способ обработки входных
