Отладка кода в Cotonti

По старинке, но проще!

#1. Введение

Многих разработчиков выражение «отладка кода» вызывает неприятные чувства, как то, что мы не любим делать. Однако на деле это может оказаться даже увлекательной игрой, все дело в выбранных средствах. В конце концов на дворе XXI век и удобных средств разработки и инструментов можно найти уйму.

Конечно, оптимальным средством для отладки  будет специальное программное обеспечение, как например PHP отладчик XDebug. В сети можно найти массу статей на тему установки и работы с XDebug в своей любимой IDE. Вот несколько подобных:

Тем не менее в данной статье мы поговорим о случаях, когда у вас нет возможности установить дополнительное ПО, и вы можете пользоваться только средствами PHP и самого Cotonti. Зачем? — спросите вы... По разным причинам, например по причине, что XDebug это дополнительное расширение PHP и далеко не на каждом хостинге оно установлено или есть возможность его установить. Да и локальный сервер не всегда есть под рукой. Кроме того, существует группа людей, которые привыкли делать все по старинке — руками. Так давайте же облегчим им жизнь за счет встроенных в Cotonti инструментов отладки.

#1.1. Включение режима отладки

Для использования внутренних инструментов отладки необходимо, либо включить режим отладки в файле конфигурации datas/config.php:

$cfg['debug_mode'] = true;

либо подключить модуль с отладочными функциями самостоятельно:

require_once $cfg['system_dir'].'/debug.php';

#2. Отслеживание и вывод

Если что-то работает не так, то как правило это связано с тем, что в нашем коде реальные значения при выполнении расходятся с предполагаемыми при проектировании. Самый простой способ проверить значение переменной в определенном месте кода это вывести его на печать (в данном случае на монитор). Однако Cotonti использует буферизацию вывода и обработку шаблонов, что не позволяет на прямую выводить значение в произвольном месте. Если вам будет достаточно вывода значений на пустом экране, то достаточно использовать вывод через функцию cot_print(), для примера.

$qa_abuse = ($usr['id'] == 0 || $row['fp_posterid'] == $usr['id'] ? ''
     : cot_rc_link(cot_url('plug', 'e=modcp&m=report&post='.$row['fp_id']), $L['qa_report_abuse'];

// <DEBUG>
cot_print($usr['id'], $row['fp_posterid'], $qa_question); // print these vars
// </DEBUG>

$qa_best = ($qa_question['q_state'] == QA_QUESTION_OPEN && $usr['id'] == $ft_firstposterid) ?
// ...

Вы можете передать не ограниченный список переменных. Вывод функции будет примерно следующим:


Минусом функции cot_print() является то, что она прекращает вывод скрипта в месте вызов и мы не можем отследить дальнейшее изменение значений. Для учета изменений мы можем с помощью Cotonti указать точки наблюдения. Это не позволит отследить непрерывную динамику, но позволит получить лог значений в заданных местах программы.

Перед тем, как использовать cot_watch() или прочие функции, которые пишут в лог файл, вы должны в файле настроек datas/config.php указать путь к каталогу в который он будет записан:

/**
 * Путь для сохранения лог файлов.
 * Для соблюдения безопасности этот каталог НЕ ДОЛЖЕН быть доступен посторонним лицам (например по HTTP). Защитите log файлы с помощью .htaccess или используйте каталог доступный только по FTP/SSH.
 */
$cfg['debug_logpath'] = '/tmp';

Замечание: имена файлов будут иметь вид `cot_debug_Ymd_His.log`, где Ymd_His метка даты-времени в соответствующем формате. Поэтому на каждый вызов будет создан свой файл.

Теперь когда вам надо изучить значение переменных, просто вызывайте cot_watch(), указав необходимые переменные в качестве параметров функции:

cot_watch($my_var, $my_array, $obj->id, $arr['item']);

После выполнения скрипта вы можете открыть лог-файл для изучения.
 

#3. Лог вызовов

Лог вызовов — это полный список подключаемых файлов и вызываемых функций (с указанием их аргументов) на момент вызова.

Такой лог может быть полезен, например, когда скрипт был завершен с ошибкой (например SQL) и вы не понимаете в каком месте произошла ошибка. Тогда вы включаете режим отладки в datas/config.php :

/**
 * Определяет выводить ли отладочную информацию в случае критических ошибок.  
* На рабочих сайтах РЕКОМЕНДУЕТСЯ выключить вывод (FALSE).
 */
$cfg['debug_mode'] = TRUE;

Если режим отладки включен и произошла SQL ошибка, вы получите лог стека вызовов, что поможет найти проблемное место в коде:


Возможно вам понадобится получить лог вызовов, тогда просто вызовите cot_backtrace():

cot_backtrace(); // показать стек вызовов

Некоторые функции отладки (типа cot_backtrace() ) используют аргумент $clear_screen , для предотвращения очистки экрана перед выводом. В таком случае вывод будет осуществлен прямо в потоке вашей страницы:

cot_backtrace(FALSE); // не очищать экран

#4. Дамп переменных и контрольные точки

Иногда для полноценного анализа не достаточно отдельных наблюдений и стека вызовов. В таких случаях может помочь дамп переменных и контрольные точки.

cot_vardump();

напечатает всю структуру глобальных переменных и их значений на момент вызова cot_vardump(). Как правило это огромная HTML страница, на которой для поиска необходимой информации лучше использовать текстовый поиск (Ctrl+F).

Если вы отлаживаете функцию и вам интересны только локальные переменные доступные внутри функции, то используйте макрос COT_VARDUMP_LOCALS:

function foo($arg1, $arg2)
{
	global $cfg;
	$my_var = 'test';
	
	eval(COT_VARDUMP_LOCALS);
}

В результате будет выведены только определенные в функции переменные $arg1, $arg2, $cfg и $my_var.

Возможно вам покажется более удобным запись переменных в лог файл, чем вывод их на экран и прерывание выполнения скрипта. Полноценные отладчики используют механизм точек остановки: вы указываете точки прерывания и запускаете программу пошагово, с остановкой в каждой указанной точке, с возможностью отследить значения переменных. Используя голый PHP, у нас такой возможности нет, поэтому мы используем т.н. «контрольные точки».

Когда встречена контрольная точка, скрипт открывает лог файл и пишет в него где мы находимся и  все глобальные и локальные переменные. Вы можете задать несколько контрольных точек для получения более подробной картины происходящего в скрипте.

Давайте на примере реального Плагина для Cotonti посмотрим как использовать этот механизм.

В данном примере я хочу найти проблему со значениями внутри функции, которая синхронизирует уровень пользователя с его счетом. Для этого используем макрос COT_CHECKPOINT_LOCAL:

function qa_sync_scorelvl($user_id = NULL)
{
	global $usr, $qa_levels, $db_users;
	if (is_null($user_id))
	{
		$user_id = $usr['id'];
		$urr['score'] = $usr['score'];
		$urr['scorelvl'] = $usr['scorelvl'];
	}
	else
	{
		$urr = qa_get_score($user_id);
	}
	
	// <DEBUG>
	eval(COT_CHECKPOINT_LOCALS); // dump locals
	// </DEBUG>
	
	if (isset($qa_levels[$urr['scorelvl'] + 1])
		&& $urr['score'] >= $qa_levels[$urr['scorelvl'] + 1]['minscore'])
	{
		// ...
	}
	// ...
	return $urr['scorelvl'];
}

Далее на страницах сайта выполняем действия, которые приведут к вызову этой функции. Затем открываем лог файла и наблюдаем примерно следующую картину:

/home/trustmaster/htdocs/answers/plugins/answers/inc/points.php, 184:
user_id = 2
usr = Array
(
    [id] => 1
... ETC.

    [score] => 4
    [scorelvl] => 6
    [logpoint] => 1241583533
    [isadmin] => 1
    [auth_write] => 1
    [auth_read] => 1
)

qa_levels = Array
(
    [0] => Array
        (
            [name] => None
            [minscore] => -1000
        )

    [1] => Array
        (
            [name] => One
            [minscore] => 0
        )

    [2] => Array
        (
            [name] => Two
            [minscore] => 50
        )
... ETC.
)

db_users = cot_users
urr = Array
(
    [0] => 76
    [1] => 2
)

----------------

Здесь внимание уделено массиву urr, который имеет числовые ключи, в то время, как в функции предполагается использование строковых ключей (ассоциативного массива). Теперь можно исправить недочет и функция заработает как надо.

В следующем примере мне необходимо отладить ту часть плагина, которая выполняется в общей (глобальной) области видимости. Для этого используем функцию cot_checkpoint():

$qa_abuse = ($usr['id'] == 0 || $row['fp_posterid'] == $usr['id'] ? ''
     : cot_rc_link(cot_url('plug', 'e=modcp&m=report&post='.$row['fp_id']), $L['qa_report_abuse'];

// <DEBUG>
cot_checkpoint(); // dump globals
// </DEBUG>

$qa_best = ($qa_question['q_state'] == QA_QUESTION_OPEN && $usr['id'] == $ft_firstposterid) ?
// ...


Полученный лог файл достаточно большой т.к. дамп всех перемекнных занимает около 1 Мб (тем не менее надо отметить, что это сильно меньше, чем используют большинство CMS). Поэтому лучше воспользоваться каким-либо редактором для поиска необходимых данных внутри файла:

#5. Заключение

Конечно данные функции не так удобны как специализированный отладчик (при условии, что вы умеете им пользоваться). Тем не менее они вам могут пригодится, если вы собираетесь писать в вашем скрипте, что-то типа "echo 'Что же здесь не так?'".

И запомните — отладка становится гораздо интереснее, если у вас есть правильное оружие! 



1. tensh  30.07.2009 05:39
Thanks! It will help me much. :)
Добавление комментариев доступно только зарегистрированным пользователям