<sect1 id="zend.db.profiler" xmlns:xi="http://www.w3.org/2001/XInclude">

    <title>Zend_Db_Profiler</title>

    <sect2 id="zend.db.profiler.introduction">

        <title>Введение</title>

        <para>
            <code>Zend_Db_Profiler</code> может быть включен
            для профилирования запросов.
            Профили включают в себя запросы, обработанные адаптером, а также
            время, затраченное на обработку запроса. Это позволяет исследовать
            выполненные запросы без добавления дополнительного отладочного
            кода в классы. Расширенное использование также позволяет
            разработчикам указывать, профилирование каких запросов
            производить.
        </para>

        <para>
            Включение профилировщика производится либо передачей директивы
            конструктору при создании адаптера, либо последующим обращением к
            адаптеру для включения.
        </para>

        <programlisting role="php"><![CDATA[
$params = array(
    'host'     => '127.0.0.1',
    'username' => 'webuser',
    'password' => 'xxxxxxxx',
    'dbname'   => 'test'
    'profiler' => true  // включение профилировщика;
                        // для отключения устанавливайте в false
                        // (значение по умолчанию)
);

$db = Zend_Db::factory('PDO_MYSQL', $params);

// отключение профилировщика:
$db->getProfiler()->setEnabled(false);

// включение профилировщика:
$db->getProfiler()->setEnabled(true);
]]>
        </programlisting>

        <para>
            Значение опции '<code>profiler</code>' является гибким. Оно
            интерпретируется по-разному в зависимости от его типа. В большинстве
            случаев достаточно использовать простое булево значение, но с
            помощью других типов можно управлять поведением профилировщика
        </para>

        <para>
            Аргумент булевого типа включает профилировщик, если имеет значение
            <code>true</code>, и выключает его, если имеет значение
            <code>false</code>. По умолчанию адаптер использует класс
            профилировщика <code>Zend_Db_Profiler</code>.
            <programlisting role="php"><![CDATA[
$params['profiler'] = true;
$db = Zend_Db::factory('PDO_MYSQL', $params);
]]>
            </programlisting>
        </para>

        <para>
            Передача объекта профилировщика заставляет адаптер использовать его.
            Объект должен принадлежать классу <code>Zend_Db_Profiler</code> или
            его производному.
            <programlisting role="php"><![CDATA[
$profiler = MyProject_Db_Profiler();
$profiler->setEnabled(true);
$params['profiler'] = $profiler;
$db = Zend_Db::factory('PDO_MYSQL', $params);
]]>
            </programlisting>
        </para>

        <para>
            Аргумент может быть ассоциативным массивом, содержащим
            ключи '<code>enabled</code>', '<code>instance</code>' и
            '<code>class</code>'. Ключи '<code>enabled</code>' и
            '<code>instance</code>' соответствуют булевому типу и объекту,
            описанным выше. Ключ '<code>class</code>' используется для имени
            класса профилировщика, который требуется установить. Класс должен
            быть <code>Zend_Db_Profiler</code> или его производным. Класс
            инстанцируется конструктором без передачи аргументов. Опция
            '<code>class</code>' игнорируется, если установлена опция
            '<code>instance</code>'.
            <programlisting role="php"><![CDATA[
$params['profiler'] = array(
    'enabled' => true,
    'class'   => 'MyProject_Db_Profiler'
);
$db = Zend_Db::factory('PDO_MYSQL', $params);
]]>
            </programlisting>
        </para>

        <para>
            И наконец, аргумент может быть объектом типа
            <code>Zend_Config</code>, содержащим свойства, аналогичные ключам
            массива, описанного выше. К примеру, файл "config.ini" может
            содержать следующие данные:
            <programlisting role="ini"><![CDATA[
[main]
db.profiler.class   = "MyProject_Db_Profiler"
db.profiler.enabled = true
]]>
            </programlisting>

            Эта конфигурация может быть применена так, как показано в коде ниже:

            <programlisting role="php"><![CDATA[
$config = new Zend_Config_Ini('config.ini', 'main');
$params['profiler'] = $config->db->profiler;
$db = Zend_Db::factory('PDO_MYSQL', $params);
]]>
            </programlisting>

            Свойство '<code>instance</code>' может быть использовано следующим
            образом:
            <programlisting role="php"><![CDATA[
$profiler = new MyProject_Db_Profiler();
$profiler->setEnabled(true);
$configData = array(
    'instance' => $profiler
    );
$config = new Zend_Config($configData);
$params['profiler'] = $config;
$db = Zend_Db::factory('PDO_MYSQL', $params);
]]>
            </programlisting>

        </para>

    </sect2>

    <sect2 id="zend.db.profiler.using">

        <title>Использование профилировщика</title>

        <para>
            Извлечение профилировщика производится в любой момент через
            метод <code>getProfiler()</code> адаптера:
        </para>

        <programlisting role="php"><![CDATA[
$profiler = $db->getProfiler();
]]>
        </programlisting>

        <para>
            Он вернет экземпляр класса <code>Zend_Db_Profiler</code>.
            С помощью этого экземпляра разработчик может изучать запросы,
            используя различные методы:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <code>getTotalNumQueries()</code> возвращает общее количество запросов, обработанных профилировщиком.
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>getTotalElapsedSecs()</code> возвращает общее
                    количество секунд, затраченное на все запросы, обработанные
                    профилировщиком.
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>getQueryProfiles()</code> возвращает массив всех профилей запросов.
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>getLastQueryProfile()</code> возвращает последний созданный (самый недавний) профиль запроса, безотносительно
                    того, был ли запрос завершен (если не был завершен, то
                    конечное время будет равно null).
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>clear()</code> удаляет все профили запросов из
                    стека.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Возвращаемое <code>getLastQueryProfile()</code> значение и
            отдельные элементы <code>getQueryProfiles()</code> являются
            объектами <code>Zend_Db_Profiler_Query</code>, которые дают
            возможность исследовать запросы по отдельности:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <code>getQuery()</code> возвращает SQL-текст запроса. SQL-текст подготовленного оператора с параметрами является текстом в то время, когда запрос подготавливается, поэтому он содержит метки заполнения, а не значения, используемые во время выполнения запроса.
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>getQueryParams()</code> возвращает массив значений
                    параметров, которые используются во время выполненения
                    подготовленного запроса. Этот массив включает в себя как
                    связанные параметры, так и аргументы для метода оператора
                    <code>execute()</code>. Ключами массива являются позиционные
                    (начинающиеся с 1) или именованные (строковые) индексы
                    параметров.
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>getElapsedSecs()</code> возвращает время выполнения
                    запроса в секундах.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Информация, предоставляемая <code>Zend_Db_Profiler</code>, полезна
            для выявления "узких мест" в приложениях и отладки запросов.
            Например, чтобы посмотреть, какой запрос выполнялся
            последним:
        </para>

        <programlisting role="php"><![CDATA[
$query = $profiler->getLastQueryProfile();

echo $query->getQuery();
]]>
        </programlisting>

        <para>
            Возможно, страница генерируется медленно. Используйте профилировщик
            для того, чтобы сначала определить общее количество секунд для
            всех запросов, затем выполните обход всех запросов, чтобы найти
            тот, который выполняется дольше всех:
        </para>

        <programlisting role="php"><![CDATA[
$totalTime    = $profiler->getTotalElapsedSecs();
$queryCount   = $profiler->getTotalNumQueries();
$longestTime  = 0;
$longestQuery = null;

foreach ($profiler->getQueryProfiles() as $query) {
    if ($query->getElapsedSecs() > $longestTime) {
        $longestTime  = $query->getElapsedSecs();
        $longestQuery = $query->getQuery();
    }
}

echo 'Executed ' . $queryCount . ' queries in ' . $totalTime .
     ' seconds' . "\n";
echo 'Average query length: ' . $totalTime / $queryCount .
     ' seconds' . "\n";
echo 'Queries per second: ' . $queryCount / $totalTime . "\n";
echo 'Longest query length: ' . $longestTime . "\n";
echo "Longest query: \n" . $longestQuery . "\n";
]]>
        </programlisting>

    </sect2>

    <sect2 id="zend.db.profiler.advanced">

        <title>Расширенное использование профилировщика</title>

        <para>
            Кроме исследования запросов, профилировщик также позволяет
            фильтровать запросы, для которых
            создаются профили. Следующие методы работают на экземпляре
            <code>Zend_Db_Profiler</code>:
        </para>

        <sect3 id="zend.db.profiler.advanced.filtertime">
            <title>Фильтрация по времени выполнения запроса</title>

            <para>
                <code>setFilterElapsedSecs()</code> дает возможность
                разработчику устанавливать минимальное время запроса, после
                которого будет проводиться профилирование запросов.
                Для того, чтобы убрать фильтрацию, передайте методу значение
                null.
            </para>

            <programlisting role="php"><![CDATA[
// Профилировать только те запросы, которые отнимают не менее 5 секунд:
$profiler->setFilterElapsedSecs(5);

// Профилировать все запросы безотносительно времени выполнения:
$profiler->setFilterElapsedSecs(null);
]]>
            </programlisting>
        </sect3>

        <sect3 id="zend.db.profiler.advanced.filtertype">
            <title>Фильтрация по типу запроса</title>

            <para>
                <code>setFilterQueryType()</code> дает разработчику возможность
                указывать, для каких типов запросов должны создаваться профили;
                для обработки нескольких типов запросов используйте логическое
                <code>OR</code>. Типы запросов определены в следующих константах
                <code>Zend_Db_Profiler</code>:
            </para>

            <itemizedlist>
                <listitem>
                    <para>
                        <code>Zend_Db_Profiler::CONNECT</code>: операции по
                        установке соединения или выбора базы данных.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <code>Zend_Db_Profiler::QUERY</code>: общие запросы к
                        базе данных, которые не соответствуют другим типам.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <code>Zend_Db_Profiler::INSERT</code>: любые запросы,
                        через которые добавляются новые данные в базу данных,
                        как правило, это команда INSERT.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <code>Zend_Db_Profiler::UPDATE</code>: любые запросы,
                        которые обновляют существующие данные, обычно это
                        команда UPDATE.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <code>Zend_Db_Profiler::DELETE</code>: любые запросы,
                        которые удаляют существующие данные, обычно это команда
                        DELETE.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <code>Zend_Db_Profiler::SELECT</code>: любые запросы,
                        через которые извлекаются существующие данные, обычно
                        это команда SELECT.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <code>Zend_Db_Profiler::TRANSACTION</code>: любые
                        операции с транзакциями, такие, как начало транзакции,
                        фиксация транзакции или откат.
                    </para>
                </listitem>
            </itemizedlist>

            <para>
                Как и в случае <code>setFilterElapsedSecs()</code>, вы можете
                удалить все фильтры, передав <code>null</code> в
                качестве единственного аргумента.
            </para>

            <programlisting role="php"><![CDATA[
// профилирование только запросов SELECT
$profiler->setFilterQueryType(Zend_Db_Profiler::SELECT);

// профилирование запросов SELECT, INSERT и UPDATE
$profiler->setFilterQueryType(Zend_Db_Profiler::SELECT |
                              Zend_Db_Profiler::INSERT |
                              Zend_Db_Profiler::UPDATE);

// профилирование запросов DELETE
$profiler->setFilterQueryType(Zend_Db_Profiler::DELETE);

// удалить все фильтры
$profiler->setFilterQueryType(null);
]]>
            </programlisting>

        </sect3>

        <sect3 id="zend.db.profiler.advanced.getbytype">
            <title>Получение профилей по типу запроса</title>

            <para>
                Использование метода <code>setFilterQueryType()</code> может
                сократить количество генерируемых профилей. Тем не менее,
                иногда может быть полезным хранить все профили, но просматривать
                только те, которые нужны в данный момент. Другой метод
                <code>getQueryProfiles()</code> может производить
                такую фильтрацию "на лету", ему передается тип запроса (или
                логическая комбинация типов запросов) в качестве первого
                аргумента; список констант типов запросов см.
                <xref linkend="zend.db.profiler.advanced.filtertype" />.
            </para>

            <programlisting role="php"><![CDATA[
// Получение только профилей запросов SELECT
$profiles = $profiler->getQueryProfiles(Zend_Db_Profiler::SELECT);

// Получение только профилей запросов SELECT, INSERT и UPDATE
$profiles = $profiler->getQueryProfiles(Zend_Db_Profiler::SELECT |
                                        Zend_Db_Profiler::INSERT |
                                        Zend_Db_Profiler::UPDATE);

// Получение профилей запросов DELETE
$profiles = $profiler->getQueryProfiles(Zend_Db_Profiler::DELETE);
]]>
            </programlisting>

        </sect3>

    </sect2>

    <sect2 id="zend.db.profiler.profilers">
        <title>Специализированные профилировщики</title>

        <para>
            Специализированный профилировщик - это объект, который наследует от
            <code>Zend_Db_Profiler</code>. Специализированные профилировщики
            предназначены для специальной обработки данных профилирования.
        </para>

        <xi:include href="Zend_Db_Profiler-Firebug.xml">
            <xi:fallback><xi:include href="../../en/module_specs/Zend_Db_Profiler-Firebug.xml" /></xi:fallback>
        </xi:include>

    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
