Для того, что бы программировать было легко и комфортно, первым делом нужно научиться диагностировать и исправлять ошибки. По этому, прежде чем двинуться дальше, пройдемся немного по дебаггингу.
Научиться отлаживать и оптимизировать скрипты - значит научиться программировать. Потому что мало написать рабочий код, нужно суметь написать хорошо работающий код. Без ошибок.
Вообще в программировании действует принцип 90-10-90. То есть на первые 90% работы уходит 10% времени, а на последние 10% работы - остальные 90. Написать программу можно довольно быстро, но вот на её отладку иногда (да почти всегда, что греха таить) уходит жутко много времени. По этому настоящий программист не станет расстраиваться, если что то не получилось с первого захода. Тут как раз и начинается самый интересный и творческий момент - дебаггинг.
По этому позволю себе несколько полезных советов в этом плане. Первый и самый важный. Никогда не загрубляйте уровень распознавания ошибок, не давите их собаками и прочей живностью. Даже наоборот. Одной из первых строчек скрипта должна быть такая:
<?php
error_reporting(E_ALL);
Это просьба к интерпретатору не скрывать от нас правду, какой горькой она бы не была. Ну а дальше по пунктам.
Синтаксические ошибки. Самое простое дело. Мой вам совет: не бойтесь нотисов и варнингов (сообщений об ошибках), бойтесь их отсутствия. Программа более чем из пяти строк, не вызвавшая предупреждение при первом запуске - повод для паники. Что то тут не чисто. И хотя это шутка, но доля правды все же в этом есть.
Нотисы и варнинги (предупреждения о ошибках) нужно любить - это наши помощники, а не злые враги, которые не дают запустить скрипт В КОТОРОМ ВСЕ ПРАВИЛЬНО!!!!! Ведь никак не хочется верить, что это ты ошибся, а не эта чертова груда железа.
Запомните:
Человек умнее компьютера, но компьютер умнее программиста.
К тому же он не умеет врать. Гнать - да, но врать нет. По этому если он написал:
Warning: Cannot modify header information - headers already sent by
(output started at Z:\home\test.den\www\index.php:2)
in Z:\home\test.den\www\index.php on line 19
не нужно бить в монитор мобилой, не нужно ломиться на форумы с криком - У МЕНЯ ВСЕ ПРАВИЛЬНО, НЕТ ПРОБЕЛОВ, НЕТ ВЫВОДА, А ОН КАЗЕЛ!!! Нужно еще раз внимательно все просмотреть.
Начать нужно с самого варнинга. Я почему то читаю их по арабски - справа на лево. Когда много кодишь, достаточно увидеть номер строки, как тут же приходит прояснение - "ай. Я же там забыл скобку закрыть!" Если не приходит - смотрю что за файл виноват в злоключениях. И та же схема - "блин, я же трассировку не убрал, шьёрт побьери!" Если и тут не торкает, смотрю что за беда стряслась. Нотисы нужно читать и понимать. А значит, если с английским слабо, - не лениться совать их в транcлатер. Их не так много на самом деле, через некоторое время все они будут известны наизусть.
Найти синтаксическую ошибку - плевое дело. Во первых помогает подсветка. А во вторых - синтаксис PHP очень несложен. И ошибки обычно бывают одни и те же. Так что не будем на них циклиться.
Логические ошибки
Тут дело гораздо сложнее.
Логическая ошибка, это когда все работает, но результат совсем не тот, который ожидался. Допустим переопределилась какая нибудь переменная или ветвление не так пошло. Помочь уже ни какой интерпретатор не сможет.
Самый действенный способ поймать логическую ошибку - трассировка. То есть поэтапный вывод результатов работы с остановкой скрипта.
Для трассировки очень хорошо подходят три функции - print_r() ,var_dump() и debug_print_backtrace(). А лучше написать свою, это будет проще и красивее. Вот допустим так:
<?php
/**
* dbg - Functions of diagnosing of logic errors (trace)
* NOTE: Requires PHP version 5 or later
* @package debug
* @author IT studio IRBIS-team
* @copyright © 2009 IRBIS-team
* @version 0.1
* @license http://www.opensource.org/licenses/rpl1.5.txt
*/
///////////////////////////////////////////////////////////////////
/**
* Function of definition of a name of a variable
* @param mixed $var,
* @param boolean $exit
* @return void
*/
function dbg(&$var, $exit = false)
{
if(IRB_TRACE)
{
static $num = 0;
++$num;
?><div style="background-color:#FFFF33;
width:50%;
padding-left:20px;
margin-left:5%;
border:1px solid"
>
<h3 style="color:#0000CC">TRACE № <?php echo $num; ?>.</h3><?php
$trace = debug_backtrace();
$old = $var;
$var = microtime();
$vname = false;
$vstring = '<b style="color:blue">';
foreach($GLOBALS as $key => $val)
if($val === $var)
$vname = $key;
if(empty($vname) && isset($_POST))
foreach($_POST as $key => $val)
if($val === $var)
$vname = '_POST[<span style="color:red" >"'. $key .'"</span>]';
elseif(empty($vname) && isset($_GET))
foreach($_GET as $key => $val)
if($val === $var)
$vname = '_GET[<span style="color:red" >"'. $key .'"</span>]';
elseif(empty($vname) && isset($_SESSION))
foreach($_SESSION as $key => $val)
if($val === $var)
$vname = '_SESSION[<span style="color:red" >"'. $key .'"</span>]';
elseif(empty($vname) && isset($_COOKIE))
foreach($_COOKIE as $key => $val)
if($val === $var)
$vname = '_COOKIE[<span style="color:red" >"'. $key .'"</span>]';
$var = $old;
if(empty($vname) && empty($var))
$vstring .= ' </b><b style="color:red">The variable is not defined in function</b><br>';
elseif(empty($var))
$vstring .= ' </b><b style="color:red">The variable is not defined or empty</b><br>';
elseif(empty($vname))
$vstring .= ' </b><b style="color:green">It was not possible to define a variable name</b><br>';
else
$vstring .= '$'. $vname .'</b><b style="color:green"> = </b>';
echo '<b>File: </b><b style="color:#CC0066">'. $trace[0]['file'] .'</b><br>';
echo !empty($trace[1]['class'])?'<b>Class: </b>
<b style="color:#CC0066">'. $trace[1]['class'] .'</b><br>':NULL;
echo (!empty($trace[1]['function'])?
'<b>Function: </b><b style="color:#CC0066">'. $trace[1]['function']:'<b>GLOBALS') .'</b><br>';
echo '<b>Line: </b><b style="color:#CC0066">'. $trace[0]['line'] .' </b><br><br>';
echo $vstring .'<pre>';
if(is_array($var))
print_r($var);
elseif(!empty($var))
var_dump($var);
?></pre></div><?php
if($exit)
exit();
}
}
Не стоит пока пытаться понять как она устроена. Это просто инструмент, облегчающий поиск логических ошибок. Пользоваться ей просто. Нужно положить её в рядом со скриптом и подключить в самом начале, первой строчкой после заголовков.
Вернее второй, первой нужно определить константу, которая будет включать и отключать трассировку.
Если кто то не знает, что такое "подключить первой строчкой", смотрим здесь.
Теперь в любом месте программы стоит написать три буквы и мы увидим, что находится в переменной. У нас есть уже первая страница, значит можно попробовать:
<?php
header('Content-Type: text/html; charset=utf-8');
/**
* TODO: To clean in release
* TODO: Убрать в релизе
*/
define('IRB_TRACE', true);
include './debug.php';
$var = 'Привет, Мир... тьфу, Hello World!';
?>
<h1>
<?php echo $var; ?>
</h1>
<?php dbg($var); ?>
Если константе IRB_TRACE поставить значение FALSE, то трассировка отключится.
Функцию можно взять здесь
Ну вот, теперь остается натыкать вызовов функции в проблемных местах и посмотреть, как ведет себя программа. А дальше только голова и... опять голова.
Семантические ошибки
Самая печальная история. Дело в том, что семантические ошибки крайне редко себя проявляют. Но при этом часто или тормозят скрипт, или повышают ресурсоемкость. К тому же они никак не диагностируются. Тут вся надежда только на себя. Главное правило - следить за типами данных. Если переменная объявлена массивом, а чуть позже в неё пишется строка:
$var = array();
$var = 'Строка';
то php конечно же сам справится с ситуацией, но ему придется приложить некоторые усилия.
Еще очень важно обращать внимание на область видимости переменных. Особенно при процедурном подходе, где большая часть переменных находится в глобальной области видимости. Неправильно расположенные переменные могут очень сильно изменить сценарий.
Неверное применение функций тоже относится к семантике.
Ну и конечно настройки. Нельзя писать скрипт под какой то определенным образом настроенный сервер. Весьма вероятно, что настройки могут измениться и тогда не избежать беды. Это касается тех же пресловутых магических кавычек к примеру или даже mod_rewrite. Скрипт должен быть написан так, что бы при переносе его на другой сервер потребовалось минимальное количество настроек.
Вообще семантические ошибки пропадают с накоплением опыта, так что все наживное.
Не забывайте, что главное оружие программиста - трезвый ум и холодный рассудок. Ни в коем случае не впадать в панику. Все очень просто налаживается, нужно только захотеть и постараться.
Самые распространенные ошибки
Ну не станем тратить время на точки с запятой и прочий синтаксис. Рассмотрим более серьёзные грабли.
1. Упомянутая уже тут ошибка отправки заголовков. Это
Warning: Cannot modify header information - headers already sent by
(output started at Z:\home\my-site.ru\www\index.php:1)
in Z:\home\my-site.ru\www\index.php on line 3
обозначает ни что иное, как вывод перед отправкой заголовка. Причем указано место этого вывода: index.php:1
и местоположение этого заголовка index.php on line 3 . Ничего в этом ни страшного ни сложного нет. Варианта всего два - либо стоит пробел перед открывающим дескриптором
2. Откуда то тянется мода писать переменные в кавычках. Вот так:
echo "$var";
Оно конечно работает, хотя порядочно тормозит. Но это пол беды. Увлекшись этим многие пишут переменные в апострофах, вот так:
echo '$var';
и потом сильно удивляются, что результат не тот. Запомните. Тарэлька пишэтся бэз мягкий знак, а сол с мягкий знак.
Переменные не нужно обрамлять ни кавычками, ни апострофами. А вот элементы массива (если они строковые), обязательно нужно.
Вот так правильно:
echo $var;
echo $array['var'];
echo $array[10];
3. Посеръёзнее вариант. Уже стал академическим примером, с подачи одного известного горе-кодера такой код:
$query = "SELECT * FROM table";
$result = mysql_query($query);
$my_row = mysql_fetch_array($result);
do
{
printf ("<div class='my_div'>%s</div>",$my_row["text"]);
}
while($my_row = mysql_fetch_array($result));
Узнаете? Тут целый букет заболеваний.
Самые грубые.
а)Цикл do... while тут некорректен, так как может сложится ситуация, когда не будет записей, удовлетворяющих условию, а цикл совершит интерацию независимо от.
б)Применение printf() тут не к месту, так как функция служит для форматирования значений, а не для организации разметки. Она сложна и непрозрачна в использовании для простого вывода.
в)Неоправдано применение mysql_fetch_array() Если и применять её, то с подобающим случаю флагом. А лучше воспользоваться mysql_fetch_assoc()
Этот код по хорошему должен выглядеть так:
$sql = "SELECT * FROM `table`";
$result = mysql_query($sql) or die(mysql_error() .'<br/>'. $sql);
while ($row = mysql_fetch_assoc($result))
{
echo '<div class="my_div">'. htmlspecialchars($my_row['text']) .'</div>';
}
Ну пока достаточно, в процессе накопятся.)