Логические ошибки могут оказаться наиболее трудными для обнаружения и устранения. К ним относятся случаи, когда вполне допустимый код выполняется как запрограммировано, но автор кода имел другие намерения.
Логические ошибки могут вызываться простыми опечатками, как в следующем примере:
for ($i = 0; $i <10; $i++);
{
echo ' Что-то происходит . . .<br>';
}
Этот фрагмент кода вполне допустим. Синтаксис РНР здесь не нарушается. Какие либо внешние службы не задействуются, поэтому ошибки времени выполнения мало вероятны. Код выполняет не те действия, которые могут показаться заданными на первый взгляд.
Может показаться, что строка ' Что-то происходит . . .<br>' должна выводиться в цикле for 10 раз. Наличие лишней точки с запятой в конце первой строки означает, что цикл не распространяется на последующие строки. Цикл for будет выполнен 10 раз безрезультатно, а затем один раз будет выполнен оператор echo.
Поскольку этот код вполне допустим, хотя и не дает эффекта, программа анализа не выведет никаких сообщений. Компьютеры неплохо справляются с определенными задачами, но они не обладают здравым смыслом или интеллектом. Машина в точности выполняет то, что ей указывают. Необходимо добиться, чтобы инструкции точно соответствовали замыслу разработчика.
Логические ошибки не означают невозможность выполнения кода, а вызываются лишь неуспешной попыткой программиста точно выразить посредством кода свои намерения. Поэтому они не могут обнаруживаться автоматически. Ни код ошибки, ни сообщение о ней не выводятся. Логические ошибки выявляются только в результате тщательного тестирования.
Ошибку, подобную рассмотренной в предыдущем примере, легко допустить, однако столь же легко исправить. Дело в том, что при первом же выполнении кода вывод будет отличаться от ожидаемого. Большинство логических ошибок гораздо более коварно.
Логические ошибки, вызывающие затруднения, обычно получаются в результате неверных предположений разработчиков. Мы говорили уже о том, что хорошо бы задействовать других программистов для проверки кода и подсказки дополнительных тестовых вариантов, а также привлекать на тестирование представителей конечных пользователей вместо разработчиков. Когда разработчик выполняет тестирование самостоятельно, весьма вероятно, что код останется построенным в предположении, что пользователи будут вводить лишь данные определенного типа.
Предположим, что коммерческий сайт содержит текстовое поле Order Quantity (размер заказа). Можно ли предположить, что пользователи будут вводить только положительные числа? Если посетитель введет "минус десять", будет ли программа увеличивать остаток кредитной карточки на сумму десятикратной стоимости данного товара?
Предположим, форма содержит поле ввода суммы в долларах. Допускается ли ввод суммы со знаком доллара, либо без него? Допускается ли разделение тысяч запятыми? Некоторые проверки можно выполнять на стороне клиента (например, с помощью JavaScript), чтобы несколько разгрузить сервер.
Если информация передается другой странице, возможна ли ситуация, когда переваваемая строка содержит недопустимые символы для URL-адреса, такие как пробелы?
Количество возможных логических ошибок не ограничено. Не существует автоматизированного метода их выявления. Единственное решение — во-первых, избегать в коде сценария явных предположений о вводе пользователя и, во-вторых, тщательно проверять все возможные типы допустимого и недопустимого ввода, чтобы в любом случае получался ожидаемый результат.
С возрастанием сложности проектов возникает необходимость в утилите, которая помогает выявлять причины ошибок. Листинг, показанный ниже, содержит фрагмент кода, который может оказаться полезным. Этот код выводит содержимое переменных, передаваемых странице.
dump_variables.php — этот код может быть включен в страницы для вывода содержимого переменных для целей отладки
<?
// Эти строки форматируют выводимую информацию в виде
// HTML-комментариев и последовательно вызывают функцию dump_array()
echo "\n<!-- НАЧАЛО ДАМПА ПЕРЕМЕННЫХ -->\n\n";
echo "<!-- НАЧАЛО ПЕРЕМЕННЫХ GET -->\n";
echo '<!-- '.dump_array($HTTP_GET_VARS)." -->\n";
echo "<!-- НАЧАЛО ПЕРЕМЕННЫХ POST -->\n";
echo '<!-- '.dump_array($HTTP_POST_VARS)." -->\n";
echo "<!-- НАЧАЛО ПЕРЕМЕННЫХ СЕАНСА -->\n";
echo '<!-- '.dump_array($HTTP_SESSION_VARS)." -->\n";
echo "<!-- НАЧАЛО ПЕРЕМЕННЫХ COOKIE-НАБОРА -->\n";
echo '<!-- '.dump_array($HTTP_COOKIE_VARS)." -->\n";
echo "\n<!-- КОНЕЦ ДАМПА ПЕРЕМЕННЫХ -->\n";
// Функция dump_array() использует встроенную функцию print_r()
// и отменяет все завершающие HTML-дескрипторы комментариев
function dump_array($array)
{
$output = print_r($array, true);
$output = str_replace('-->', '-- >', $output);
return $output;
}
?>
Показанный в листинге код выводит четыре массива переменных, принимаемых страницей. Если страница вызывается с GET-переменными, POST-переменными, cookie-наборами либо переменными сеанса, все они будут выведены.
Выводимые значения заключены в HTML-дескрипторы комментариев, чтобы они были доступными для просмотра, но не конфликтовали с методами обработки браузером видимых элементов страницы. Сокрытие отладочной информации в комментариях, как было сделано в листинге, позволяет не удалять код отладки вплоть до последнего момента. Функция dump_array () представляет собой оболочку для функции print_r(), дополнительно выполняя лишь отмену всех завершающих HTML-дескрипторов комментариев.
РНР позволяет устанавливать степень тщательности обработки ошибок. Можно задавать типы событий, генерирующие сообщения. По умолчанию РНР выводит сообщения обо всех ошибках, которые не являются уведомлениями.
Для установки уровня сообщений используется набор предопределенных констант, перечисленных в таблице.
Каждая константа представляет тип ошибок, генерирующих сообщение либо игнорируемых. Например, если указать уровень ошибок E_ERROR, будут выводиться сообщения только о неисправимых ошибках. Допускается объединение констант методами двоичной арифметики для получения различных уровней сообщений об ошибках.
Устанавливаемый по умолчанию уровень (все сообщения кроме уведомлений) указывается следующим образом:
E_ALL & ~E_NOTICE
Это выражение включает две предопределенных константы, объединенных с помощью операторов побитовых арифметических действий. Амперсанд (&) задает битовую операцию AND, а тильда (~) — битовую операцию NOT. Выражение можно прочитать так: Е ALL AND NOT E NOTICE.
Таблица. Константы, определяющие уровень сообщений об ошибках
Значение | Имя | Описание |
1 | E_ERROR | Сообщения о неисправимых ошибках времени выполнения |
2 | E_WARNING | Сообщения об исправимых ошибках времени выполнения |
4 | E_PARSE | Сообщения об ошибках синтактисического анализатора |
8 | E_NOTICE | Уведомления и предупреждения, что выполненные действия могут быть ошибочными |
16 | E_CODE_ERROR | Сообщения о сбоях запуска механизма РНР |
32 | E_CODE_WARNING | Сообщения об исправимых ошибках в процессе запуска механизма РНР |
64 | E_COMPILE_ERROR | Сообщения об ошибках компиляции |
128 | E_COMPILE_WARNING | Сообщения об исправимых ошибках компиляции |
256 | E_USER_ERROR | Сообщения о сгенерированных пользователем ошибках |
512 | E_USER_WARNING | Сообщения о сгенерированных пользователем предупреждениях |
1024 | E_USER_NOTICE | Сообщения о сгенерированных пользователем уведомлениях |
2047 | E_ALL | Сообщения обо всех ошибках и предупреждениях |
2048 | E_STRICT | Сообщения об использовании устаревшихи нерекомендованных функциях; не включено в E_ALL, однако исключительно полезно при просмотре кода |
Константа E_ALL представляет собой комбинацию всех типов ошибок. Ее можно заменить, связав все остальные константы битовыми операциями ИЛИ (OR,|):
E_ERROR | E_WARNING | EJPARSE | E_NOTICE | E_CORE_ERROR | E_CORE_KARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_OSER_ERROR | E_USER_WARNING | E_USER_NOTICE
Подобным же образом реализуется устанавливаемый по умолчанию уровень сообщений за исключением того, что отсутствует уровень уведомлений (константа E_NOTICE):
E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_OSER WARNING | E USER NOTICE