Многих разработчиков выражение «отладка кода» вызывает неприятные чувства, как то, что мы не любим делать. Однако на деле это может оказаться даже увлекательной игрой, все дело в выбранных средствах. В конце концов на дворе XXI век и удобных средств разработки и инструментов можно найти уйму.
Конечно, оптимальным средством для отладки будет специальное программное обеспечение, как например PHP отладчик XDebug. В сети можно найти массу статей на тему установки и работы с XDebug в своей любимой IDE. Вот несколько подобных:
Тем не менее в данной статье мы поговорим о случаях, когда у вас нет возможности установить дополнительное ПО, и вы можете пользоваться только средствами PHP и самого Cotonti. Зачем? — спросите вы... По разным причинам, например по причине, что XDebug это дополнительное расширение PHP и далеко не на каждом хостинге оно установлено или есть возможность его установить. Да и локальный сервер не всегда есть под рукой. Кроме того, существует группа людей, которые привыкли делать все по старинке — руками. Так давайте же облегчим им жизнь за счет встроенных в Cotonti инструментов отладки.
Для использования внутренних инструментов отладки необходимо, либо включить режим отладки в файле конфигурации datas/config.php:
$cfg [ 'debug_mode' ] = true; |
либо подключить модуль с отладочными функциями самостоятельно:
require_once $cfg [ 'system_dir' ]. '/debug.php' ; |
Если что-то работает не так, то как правило это связано с тем, что в нашем коде реальные значения при выполнении расходятся с предполагаемыми при проектировании. Самый простой способ проверить значение переменной в определенном месте кода это вывести его на печать (в данном случае на монитор). Однако Cotonti использует буферизацию вывода и обработку шаблонов, что не позволяет на прямую выводить значение в произвольном месте. Если вам будет достаточно вывода значений на пустом экране, то достаточно использовать вывод через функцию cot_print(), для примера.
1 2 3 4 5 6 7 8 9 |
$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 указать путь к каталогу в который он будет записан:
1 2 3 4 5 |
/** * Путь для сохранения лог файлов. * Для соблюдения безопасности этот каталог НЕ ДОЛЖЕН быть доступен посторонним лицам (например по HTTP). Защитите log файлы с помощью .htaccess или используйте каталог доступный только по FTP/SSH. */ $cfg [ 'debug_logpath' ] = '/tmp' ; |
Замечание: имена файлов будут иметь вид `cot_debug_Ymd_His.log`, где Ymd_His
метка даты-времени в соответствующем формате. Поэтому на каждый вызов будет создан свой файл.
Теперь когда вам надо изучить значение переменных, просто вызывайте cot_watch(), указав необходимые переменные в качестве параметров функции:
1 |
cot_watch( $my_var , $my_array , $obj ->id, $arr [ 'item' ]); |
После выполнения скрипта вы можете открыть лог-файл для изучения.
Лог вызовов — это полный список подключаемых файлов и вызываемых функций (с указанием их аргументов) на момент вызова.
Такой лог может быть полезен, например, когда скрипт был завершен с ошибкой (например SQL) и вы не понимаете в каком месте произошла ошибка. Тогда вы включаете режим отладки в datas/config.php :
1 2 3 4 5 |
/** * Определяет выводить ли отладочную информацию в случае критических ошибок. * На рабочих сайтах РЕКОМЕНДУЕТСЯ выключить вывод (FALSE). */ $cfg [ 'debug_mode' ] = TRUE; |
Если режим отладки включен и произошла SQL ошибка, вы получите лог стека вызовов, что поможет найти проблемное место в коде:
Возможно вам понадобится получить лог вызовов, тогда просто вызовите cot_backtrace():
1 |
cot_backtrace(); // показать стек вызовов |
Некоторые функции отладки (типа cot_backtrace() ) используют аргумент $clear_screen , для предотвращения очистки экрана перед выводом. В таком случае вывод будет осуществлен прямо в потоке вашей страницы:
1 |
cot_backtrace(FALSE); // не очищать экран |
Иногда для полноценного анализа не достаточно отдельных наблюдений и стека вызовов. В таких случаях может помочь дамп переменных и контрольные точки.
1 |
cot_vardump(); |
напечатает всю структуру глобальных переменных и их значений на момент вызова cot_vardump(). Как правило это огромная HTML страница, на которой для поиска необходимой информации лучше использовать текстовый поиск (Ctrl+F).
Если вы отлаживаете функцию и вам интересны только локальные переменные доступные внутри функции, то используйте макрос COT_VARDUMP_LOCALS:
1 2 3 4 5 6 7 |
function foo( $arg1 , $arg2 ) { global $cfg ; $my_var = 'test' ; eval (COT_VARDUMP_LOCALS); } |
В результате будет выведены только определенные в функции переменные $arg1, $arg2, $cfg и $my_var.
Возможно вам покажется более удобным запись переменных в лог файл, чем вывод их на экран и прерывание выполнения скрипта. Полноценные отладчики используют механизм точек остановки: вы указываете точки прерывания и запускаете программу пошагово, с остановкой в каждой указанной точке, с возможностью отследить значения переменных. Используя голый PHP, у нас такой возможности нет, поэтому мы используем т.н. «контрольные точки».
Когда встречена контрольная точка, скрипт открывает лог файл и пишет в него где мы находимся и все глобальные и локальные переменные. Вы можете задать несколько контрольных точек для получения более подробной картины происходящего в скрипте.
Давайте на примере реального Плагина для Cotonti посмотрим как использовать этот механизм.
В данном примере я хочу найти проблему со значениями внутри функции, которая синхронизирует уровень пользователя с его счетом. Для этого используем макрос COT_CHECKPOINT_LOCAL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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():
1 2 3 4 5 6 7 8 9 |
$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). Поэтому лучше воспользоваться каким-либо редактором для поиска необходимых данных внутри файла:
Конечно данные функции не так удобны как специализированный отладчик (при условии, что вы умеете им пользоваться). Тем не менее они вам могут пригодится, если вы собираетесь писать в вашем скрипте, что-то типа "echo 'Что же здесь не так?'".
И запомните — отладка становится гораздо интереснее, если у вас есть правильное оружие!
Поблагодарили: 12 раз