Средства отладки
При написании собственных модулей Вы очень быстро придете к тому, что Вам будет необходимо контролировать их работу. Как быстро работает модуль? Какие SQL-запросы выполняются и как долго? И, наконец, правильно ли работает система кеширования? Специально для этих случаев, для упрощения отладки, мы добавили в систему инструментарий, который позволяет собирать и обрабатывать служебную информацию о том, что происходит "за кулисами" интернет-магазина.
Инструмент "Отладчик"
Чтобы включить отладчик, Вам необходимо обратиться к сайту, указав специальную переменную, а выглядит она следующим образом: "debug_on_кодовое_слово". Кодовое слово нужно для того, чтобы отладчик не мог быть включен кем угодно. Для назначения кодового слова Вам необходимо зайти в раздел "Система / Инсталляция" и в поле "Код для отладчика". Тут Вы указываете свое кодовое слово. Например "pass_code". Таким образом, команда включения будет выглядеть так: http://ms6.com/?debug_on_pass_code. После включения отладчик начнет собирать информацию и выведет ее возле каждого модуля (при условии, что время его выполнения превышает 0,01 секунды), а также в самом конце страницы. Например, так будет выглядеть страница с информацией возле модулей (указывается: имя модуля, T - время выполнения модуля, Q - количество SQL-запросов, QT - время, потраченное на SQL-запросы).
В самом конце страницы Вы получите еще три отчета:
- Краткая статистика
Отображаются суммарные данные: время сборки страницы, количество запросов к базе данных и т.п. - Статистика SQL-запросов
Отображается статистика по SQL-запросам для каждого модуля. Можно определить количество запросов, время выполнения (среднее и максимальное). Данный отчет хорошо использовать для случаев, когда необходимо оптимизировать SQL-запросы. - Статистика работы системы кеширования
В этом отчете выводится информация для каждого модуля о том, какие таблицы используются в нем, разрешен ли кеш и как давно он был сформирован. Зеленым цветом выделяется та таблица, которая изменялась позже всех, и по которой, собственно, и определяется время создания кеша для каждого конкретного модуля.

Инструмент "Сбор статистики"
В отличие от первого инструмента, сборщик статистики поможет Вам получить не только моментальную картину по модулям, но и произвести ее анализ прямо в скрипте. Это очень важный и полезный инструмент, и с его помощью Вы можете получить не только текущий срез работы модулей, но и отследить ее динамику. Сборщик статистики является расширением парсера, поэтому перечень и описание всех его методов Вы сможете найти в описании API Parser. Ниже мы с Вами рассмотрим пример того, как его можно использовать. Например, перед нами стоит задача отладить модуль melbis_web_test.php, который допустим периодически, при определенных параметрах, работает очень долго.
<?php
/***************************************************************************************************
*
* index.php - index page
*
***************************************************************************************************
* @version 6.3.0
* @copyright 2019 Melbis Company
* @link https://melbis.com
* @author Dmitriy Kasyanoff
**************************************************************************************************/
// Path to the root folder
define('FOLDER', './');
// Require classes and includes
require_once(FOLDER.'system/unit/melbis_inc.php');
// Create connect with DataBase
$gDb = new MySql(MELBIS_INC_halt);
$gDb->Connect(__FILE__, __LINE__);
// Create Parser
$gParser = new Parser(MELBIS_INC_halt, $gDb);
// Define self constants
$gParser->DefineSelfConst();
// Define session
$gSession = $gParser->DefineSession(DB_USER_NAME.'_MELBIS_SHOP');
// Define module
if ( isset($gGet['topic_id']) ) $gGet['mod'] = 'melbis_base_topic';
if ( !isset($gGet['mod']) ) $gGet['mod'] = 'melbis_base_index';
// Сообщаем парсеру о необходимости собирать статистику.
$gParser->LogOn();
// Parse
$gParser->Parse($gSitePath, $gTemplate, $gGet['mod'], [serialize($gGet), serialize($gPost)], $gUseCache, $gBuild);
// Получаем результаты работы модуля в переменную массива $res
$res = $gParser->LogUnitStat('melbis_web_test');
// Сохраняем значения параметров, с которыми работал модуль в переменную $param
$param = $gParser->LogUnitParamArray('melbis_web_test');
// Проверяем время работы модуля. Если оно более 1 секунды, то
// отправляем себе на e-mail сообщение, в котором указываем, при каких обстоятельствах это произошло.
if ( $res['runtime_total'] > 1 )
{
$mailer = new Emailer();
$mailer->SetCharset(SHOP_CHARSET);
$mailer->SetTypeText();
$mailer->AddMessage('Модуль melbis_web_test запускался с параметрами:'.print_r($param, true).' Результат: '.print_r($res, true));
$mailer->BuildMessage();
$mailer->Send('to@mail.com', 'from@mail.com', 'Внимание модуль melbis_web_test тормозит!');
}
// Publish
$gParser->Publish();
// Possible report
$gParser->Report();
?>
Кроме этого, можно реализовать еще более продвинутый способ анализа работы магазина, используя сборщик статистики. Для этого вначале потребуется создать дополнительную таблицу:
CREATE TABLE {DBNICK}_log (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
name CHAR(100) DEFAULT '' NOT NULL,
date_time DATETIME DEFAULT '2000-01-01 00:00:00' NOT NULL,
runtime DECIMAL(10,2) DEFAULT '0' NOT NULL,
sql_time DECIMAL(10,2) DEFAULT '0' NOT NULL,
sql_count INT UNSIGNED DEFAULT NULL,
sql_max DECIMAL(10,2) DEFAULT '0' NOT NULL,
sql_text TEXT DEFAULT '' NOT NULL,
params TEXT DEFAULT '' NOT NULL,
PRIMARY KEY (id),
KEY date_time (date_time)
) ENGINE = MYISAM DEFAULT CHARSET=UTF-8;
В этой таблице мы будем хранить собранные статистические данные, который потом сможем анализировать с помощью SQL-запросов. Теперь, нам остается в начале корневого скрипта разместить примерно такой код:
<?php
/***************************************************************************************************
*
* index.php - index page
*
***************************************************************************************************
* @version 6.3.0
* @copyright 2019 Melbis Company
* @link https://melbis.com
* @author Dmitriy Kasyanoff
**************************************************************************************************/
// Path to the root folder
define('FOLDER', './');
// Require classes and includes
require_once(FOLDER.'system/unit/melbis_inc.php');
// Create connect with DataBase
$gDb = new MySql(MELBIS_INC_halt);
$gDb->Connect(__FILE__, __LINE__);
// Create Parser
$gParser = new Parser(MELBIS_INC_halt, $gDb);
// Define self constants
$gParser->DefineSelfConst();
// Define session
$gSession = $gParser->DefineSession(DB_USER_NAME.'_MELBIS_SHOP');
// Define module
if ( isset($gGet['topic_id']) ) $gGet['mod'] = 'melbis_base_topic';
if ( !isset($gGet['mod']) ) $gGet['mod'] = 'melbis_base_index';
// Сообщаем парсеру о необходимости собирать статистику
$gParser->LogOn();
// Parse
$gParser->Parse($gSitePath, $gTemplate, $gGet['mod'], [serialize($gGet), serialize($gPost)], $gUseCache, $gBuild);
// получаем список вызываемых модулей, запрашиваем их показатели эффективности,
// а также входные параметры с которыми они запускались и заносим их в таблицу для анализа
$mods = $gParser->LogUnitsList();
foreach ( $mods as $mod => $tmp )
{
if ( $gParser->LogUnitLoadFromCache($mod) ) continue;
$res = $gParser->LogUnitStat($mod);
$params = json_encode($gParser->LogUnitParamArray($mod));
$command = "INSERT INTO {DBNICK}_log
SET name = '$mod',
date_time = NOW(),
runtime = '$res[runtime_total]',
sql_time = '$res[query_sum_time]',
sql_count = '$res[query_count]',
sql_max = '$res[query_max_time]',
sql_text = '".addslashes($res[query_max_query])."',
params = '".addslashes($params)."'
";
$gParser->SqlQuery(__LINE__, $command);
}
// Publish
$gParser->Publish();
// Possible report
$gParser->Report();
?>
Теперь остается дать поработать магазину некоторое время, пока созданная нами таблица log наполнится данными. Лучше конечно использовать большой период, хотя бы несколько дней, но вообще здесь многое зависит от посещаемости Вашего сайта. Главная задача для которой мы все это делаем, это определить те модули, которые нуждаются в оптимизации. Ведь работа магазина, точнее пользователей с магазином может быть не предсказуема. Возможно Вас беспокоит модуль, который выполняется 2 секунды, но оказывается что его работа происходит крайне редко, а вот модуль который, как Вам казалось, выполнялся в достаточно приемлемое время, скажем 0,5 секунды, на самом деле вызывался очень часто и если взять общую картину за день, то окажется что именно он потребил больше всего ресурсов и замедлил работу сайта в целом. Чтобы проанализировать это, мы приведем пример SQL-запроса, который Вы можете доработать на свое усмотрение:
SELECT l.*, SUM(runtime)/COUNT(*) AS rsum, SUM(sql_time) AS ssum
FROM MS_log l
GROUP BY name
ORDER BY ssum DESC
Запустив этот запрос, Вы получите в результате таблицу со списком модулей и двумя важными колонками: rsum и ssum. Эти колонки будут указывать усредненное время, за которое выполнялся этот модуль и суммарное время которое ушло на выполнение его SQL-запросов. Таким образом, Вы теперь понимаете какие модули требуется оптимизировать в первую очередь!
Инструмент "Дамп данных"
Данный инструмент также является расширением парсера, а его задача сводится к тому, чтобы выполнить моментальный снимок данных прямо в модуле и сохранить их в файл. Сохранение в файл является важной особенностью, благодаря которой данный инструмент может быть очень полезен при написании и отладке модулей, использующих технологию AJAX (например, веб-модулей которые вызываются платежными системами). Дело в том, что в этом случае обычная отладка модуля может быть затруднена из-за того, что мы не видим промежуточных результатов работы. Используя же метод парсера TplDumpVar(), мы можем сохранить тестовые данные в файл.
Итак, для того, чтобы задействовать этот инструмент, достаточно придумать имя файла (он будет сохранен рядом с HTML-шаблонами модуля) и указать, какую переменную необходимо сохранить. Например:
$command = "SELECT *
FROM {DBNICK}_topic
ORDER BY absindex
";
$query = $gParser->SqlQuery(__LINE__, $command);
$rows = $gParser->SqlNumRows($query);
for ( $i = 1; $i <= $rows; $i++ )
{
$hash = $gParser->SqlFetchHash($query);
// Save array for debuging
$gParser->TplDumpVar('dump', $hash);
}
Как видно из этого примера, мы сохраняем значения массива $hash в шаблон модуля, который будет называться dump.htm