<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/mysqlnd.plugin.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'mysqlnd.plugin.architecture.php',
    1 => 'Архитектура плагинов MySQL Native Driver',
    2 => 'Архитектура плагинов MySQL Native Driver',
  ),
  'up' => 
  array (
    0 => 'mysqlnd.plugin.php',
    1 => 'API интеграции плагинов в драйвер MySQL Native Driver',
  ),
  'prev' => 
  array (
    0 => 'mysqlnd.plugin.obtaining.php',
    1 => 'Получение API плагинов mysqlnd',
  ),
  'next' => 
  array (
    0 => 'mysqlnd.plugin.api.php',
    1 => 'API плагинов mysqlnd',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'reference/mysqlnd/plugin.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="mysqlnd.plugin.architecture" class="section">
  <h2 class="title">Архитектура плагинов MySQL Native Driver </h2>
  <p class="simpara">
   В секции рассматривается архитектура плагинов
   драйвера <code class="literal">mysqlnd</code>.
  </p>
  <p class="simpara">
   <strong>Обзор MySQL Native Driver</strong>
  </p>
  <p class="simpara">
   Перед началом разработки плагинов для драйвера
   <code class="literal">mysqlnd</code> полезно узнать,
   как организован сам драйвер <code class="literal">mysqlnd</code>.
   <code class="literal">Mysqlnd</code> состоит из следующих модулей:
  </p>
  <table id="mysqlnd.plugin.orgchart" class="doctable table">
   <caption><strong>Организационная схема mysqlnd, помодульно</strong></caption>
   
    <thead>
     <tr>
      <th>Модули статистики</th>
      <th>mysqlnd_statistics.c</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Соединение</td>
      <td>mysqlnd.c</td>
     </tr>

     <tr>
      <td>Результирующий набор</td>
      <td>mysqlnd_result.c</td>
     </tr>

     <tr>
      <td>Метаданные результирующего набора</td>
      <td>mysqlnd_result_meta.c</td>
     </tr>

     <tr>
      <td>Оператор</td>
      <td>mysqlnd_ps.c</td>
     </tr>

     <tr>
      <td>Сеть</td>
      <td>mysqlnd_net.c</td>
     </tr>

     <tr>
      <td>Протокол обмена</td>
      <td>mysqlnd_wireprotocol.c</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   <strong>Объектно-ориентированная парадигма C</strong>
  </p>
  <p class="simpara">
   На уровне кода, <code class="literal">mysqlnd</code> использует
   паттерн C для реализации объектно-ориентированного подхода.
  </p>
  <p class="simpara">
   В C объекты описывают используя <code class="literal">struct</code>.
   Члены структуры являются свойствами объекта.
   Члены структуры, указывающие на функции, являются
   методами.
  </p>
  <p class="simpara">
   В отличие от таких языков как C++ или Java, в C нет фиксированных правил наследования.
   Однако существуют некоторые договорённости, которым необходимо следовать,
   но это мы обсудим позже.
  </p>
  <p class="simpara">
   <strong>Жизненный цикл PHP</strong>
  </p>
  <p class="simpara">
   При рассмотрении жизненного цикла PHP существует два основных цикла:
  </p>
  <ul class="itemizedlist">
   <li class="listitem">
    <span class="simpara">
     Цикл старта и остановки движка PHP
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     Цикл обработки запроса
    </span>
   </li>
  </ul>
  <p class="simpara">
   При старте движка PHP, первым делом вызывается функция
   инициализации модулей (MINIT) для каждого зарегистрированного модуля. Это
   позволяет каждому модулю установить переменные и выделить ресурсы,
   которые будут задействованы все время жизни процесса
   движка PHP. Когда движок PHP выключается, он вызывает
   функцию остановки модулей (MSHUTDOWN) для каждого модуля.
  </p>
  <p class="simpara">
   На протяжении жизненного цикла движка PHP, он принимает некоторое количество
   запросов. Каждый запрос порождает новый жизненный цикл. На каждый запрос, движок PHP
   вызывает функцию инициализации для каждого модуля. Модуль может предпринять
   выставление переменных и выделение ресурсов, требуемых для обслуживания запроса.
   По окончании жизни запроса, движок вызывает функцию остановки запроса (RSHUTDOWN)
   для каждого модуля, что позволяет им произвести необходимые чистки.
  </p>
  <p class="simpara">
   <strong>Как работает плагин</strong>
  </p>
  <p class="simpara">
   Плагин <code class="literal">mysqlnd</code> работает перехватывая
   вызовы модулей, использующих <code class="literal">mysqlnd</code>,
   к <code class="literal">mysqlnd</code>. Это достигается подменой
   таблицы функций <code class="literal">mysqlnd</code> на созданную
   плагином.
  </p>
  <p class="simpara">
   Следующий код демонстрирует замену таблицы функций
   <code class="literal">mysqlnd</code>:
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* хранилище оригинальной таблицы */
struct st_mysqlnd_conn_methods org_methods;

void minit_register_hooks(TSRMLS_D) {
  /* активная таблица функций */
  struct st_mysqlnd_conn_methods * current_methods
    = mysqlnd_conn_get_methods();

  /* бэкап оригинальной таблицы */
  memcpy(&amp;org_methods, current_methods,
    sizeof(struct st_mysqlnd_conn_methods);

  /* установка новых методов */
  current_methods-&gt;query = MYSQLND_METHOD(my_conn_class, query);
}
</pre></div>
  </div>

  <p class="simpara">
   Манипуляцией с таблицей функций соединения необходимо
   заниматься на этапе инициализации модуля (MINIT). Таблица
   функций — глобальный разделяемый ресурс.
   В многопоточном окружении, со сборкой TSRM, манипуляция
   глобальным разделяемым ресурсом на этапе обработки
   запроса вызовет конфликты.
  </p>
  <blockquote class="note"><p><strong class="note">Замечание</strong>: 
   <span class="simpara">
    Не используйте логику, которая связана
    с фиксированным размером при манипуляции с таблицей
    функций <code class="literal">mysqlnd</code>. Всегда добавляйте
    новые методы в конец таблицы, так как сама таблица может
    в будущем в любой момент измениться.
   </span>
  </p></blockquote>
  <p class="simpara">
   <strong>Вызов родительских методов</strong>
  </p>
  <p class="simpara">
   Если записи оригинальной таблицы функций сохранились,
   остаётся возможность вызвать оригинальный метод — родительский.
  </p>
  <p class="simpara">
   В отдельных случаях, например, для <code class="literal">Connection::stmt_init()</code>, жизненно
   важно сначала вызвать родительский метод, и только потом делать
   что-либо в новом методе.
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
MYSQLND_METHOD(my_conn_class, query)(MYSQLND *conn,
  const char *query, unsigned int query_len TSRMLS_DC) {

  php_printf(&quot;my_conn_class::query(query = %s)\n&quot;, query);

  query = &quot;SELECT &#039;query rewritten&#039; FROM DUAL&quot;;
  query_len = strlen(query);

  return org_methods.query(conn, query, query_len); /* возврат с вызовом родителя */
}
</pre></div>
  </div>

  <p class="simpara">
   <strong>Расширение свойств</strong>
  </p>
  <p class="simpara">
   Объекты <code class="literal">mysqlnd</code> представлены как C struct.
   Невозможно добавить члена в C struct во время исполнения.
   Пользователи объектов <code class="literal">mysqlnd</code> не могут
   просто добавить свойства объекту.
  </p>
  <p class="simpara">
   Произвольные данные (свойства) могут быть добавлены к
   объекту <code class="literal">mysqlnd</code> с использованием
   соответствующей функции из семейства
   <code class="literal">mysqlnd_plugin_get_plugin_&lt;object&gt;_data()</code>.
   При размещении объекта <code class="literal">mysqlnd</code>
   резервируется место в конце объекта для удержания
   <code class="literal">void *</code> указателя на произвольные данные.
   <code class="literal">mysqlnd</code> резервирует место для одного
   <code class="literal">void *</code> указателя на плагин.
  </p>
  <p class="simpara">
   В следующей таблице показано, как вычислить положение указателя для конкретного плагина:
  </p>
  <table id="mysqlnd.plugin.pointercalc" class="doctable table">
   <caption><strong>Расчёт указателя для mysqlnd</strong></caption>
   
    <thead>
     <tr>
      <th>Адрес памяти</th>
      <th>Содержимое</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>0</td>
      <td>Начало объекта mysqlnd (C struct)</td>
     </tr>

     <tr>
      <td>n</td>
      <td>Конец объекта mysqlnd (C struct)</td>
     </tr>

     <tr>
      <td>n + (m x sizeof(void*))</td>
      <td>void* для данных объекта плагина номер m</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Если вы планируете делать подкласс от одного из конструкторов объекта
   <code class="literal">mysqlnd</code>, которые разрешены, имейте это в виду!
  </p>
  <p class="simpara">
   Следующий код демонстрирует расширение свойств:
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* Любые данные, которые мы хотим добавить */
typedef struct my_conn_properties {
  unsigned long query_counter;
} MY_CONN_PROPERTIES;

/* идентификатор плагина */
unsigned int my_plugin_id;

void minit_register_hooks(TSRMLS_D) {
  /* получаем уникальный идентификатор плагина */
  my_plugin_id = mysqlnd_plugin_register();
  /* snip - see Extending Connection: methods */
}

static MY_CONN_PROPERTIES** get_conn_properties(const MYSQLND *conn TSRMLS_DC) {
  MY_CONN_PROPERTIES** props;
  props = (MY_CONN_PROPERTIES**)mysqlnd_plugin_get_plugin_connection_data(
    conn, my_plugin_id);
  if (!props || !(*props)) {
    *props = mnd_pecalloc(1, sizeof(MY_CONN_PROPERTIES), conn-&gt;persistent);
    (*props)-&gt;query_counter = 0;
  }
  return props;
}
</pre></div>
  </div>

  <p class="simpara">
   Разработчик плагина отвечает за управление памятью данных плагина.
  </p>
  <p class="simpara">
   Рекомендуется использовать управление памятью <code class="literal">mysqlnd</code> для данных
   плагина. Эти функции именуются используя такие соглашения:
   <code class="literal">mnd_*loc()</code>. Управление памятью <code class="literal">mysqlnd</code> имеет
   ряд полезных свойств, таких как использование отладочного
   модуля управления памятью в неотладочных сборках.
  </p>
  <table id="mysqlnd.plugin.subclass" class="doctable table">
   <caption><strong>Когда и как создавать подкласс</strong></caption>
   
    <thead>
     <tr>
      <th class="empty">&nbsp;</th>
      <th>Когда создавать подкласс?</th>
      <th>Каждый экземпляр имеет свою собственную таблицу функций?</th>
      <th>Как создавать подкласс?</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Соединение (MYSQLND)</td>
      <td>MINIT</td>
      <td>Нет</td>
      <td>mysqlnd_conn_get_methods()</td>
     </tr>

     <tr>
      <td>Результирующий набор (MYSQLND_RES)</td>
      <td>MINIT or later</td>
      <td>Да</td>
      <td>mysqlnd_result_get_methods() или методом объекта,
       манипулирующим таблицей функций</td>
     </tr>

     <tr>
      <td>Результирующий набор (MYSQLND_RES_METADATA)</td>
      <td>MINIT</td>
      <td>Нет</td>
      <td>mysqlnd_result_metadata_get_methods()</td>
     </tr>

     <tr>
      <td>Оператор (MYSQLND_STMT)</td>
      <td>MINIT</td>
      <td>Нет</td>
      <td>mysqlnd_stmt_get_methods()</td>
     </tr>

     <tr>
      <td>Сеть (MYSQLND_NET)</td>
      <td>MINIT или позже</td>
      <td>Да</td>
      <td>mysqlnd_net_get_methods() или методом объекта,
       манипулирующим таблицей функций</td>
     </tr>

     <tr>
      <td>Протокол обмена (MYSQLND_PROTOCOL)</td>
      <td>MINIT или позже</td>
      <td>Да</td>
      <td>mysqlnd_protocol_get_methods() или методом
       объекта, манипулирующим таблицей функций</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Вы не должны манипулировать таблицей функций после
   MINIT, если это прямо не разрешено в таблице выше.
  </p>
  <p class="simpara">
   Некоторые классы содержат указатель на таблицу функций методов. Все экземпляры
   подобных классов должны делить одну и ту же таблицу функций. Для того, чтобы избежать
   хаоса, особенно в многопоточном окружении, управлять такими таблицами функций стоит
   только во время MINIT.
  </p>
  <p class="simpara">
   Прочие классы используют копии глобально разделённых таблиц функций. Таблица функций
   создаётся одновременно с объектом. Каждый объект использует свою таблицу. Это даёт вам
   две возможности: вы можете управлять таблицей функций по умолчанию для объекта во
   время MINIT, а также вы можете изменять методы объекта не затрагивая другие
   экземпляры этого же класса.
  </p>
  <p class="simpara">
   Преимущество разделяемой таблицы функций в производительности, так как нет нужды
   копировать таблицу функций отдельно для каждого объекта.
  </p>
  <table id="mysqlnd.plugin.constatus" class="doctable table">
   <caption><strong>Статус конструктора</strong></caption>
   
    <thead>
     <tr>
      <th>Тип</th>
      <th>Размещение, создание, сброс</th>
      <th>Может быть изменено?</th>
      <th>Вызывающий</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Connection (MYSQLND)</td>
      <td>mysqlnd_init()</td>
      <td>Нет</td>
      <td>mysqlnd_connect()</td>
     </tr>

     <tr>
      <td>Результирующий набор (MYSQLND_RES)</td>
      <td><span class="simpara">
       Размещение:
       </span>
       <ul class="itemizedlist">
        <li class="listitem">
         <span class="simpara">
          Connection::result_init()
         </span>
        </li>
       </ul>
       <span class="simpara">
        Сброс и повторная инициализация во время:
       </span>
       <ul class="itemizedlist">
        <li class="listitem">
         <span class="simpara">
          Result::use_result()
         </span>
        </li>
        <li class="listitem">
         <span class="simpara">
          Result::store_result
         </span>
        </li>
       </ul></td>
      <td>Да, но вызовите родителя!</td>
      <td><ul class="itemizedlist">
       <li class="listitem">
        <span class="simpara">
         Connection::list_fields()
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Statement::get_result()
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Statement::prepare() (Только метаданные)
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Statement::resultMetaData()
        </span>
       </li>
       </ul></td>
     </tr>

     <tr>
      <td>Метаданные результирующего набора (MYSQLND_RES_METADATA)</td>
      <td>Connection::result_meta_init()</td>
      <td>Да, но вызовите родителя!</td>
      <td>Result::read_result_metadata()</td>
     </tr>

     <tr>
      <td>Оператор (MYSQLND_STMT)</td>
      <td>Connection::stmt_init()</td>
      <td>Да, но вызовите родителя!</td>
      <td>Connection::stmt_init()</td>
     </tr>

     <tr>
      <td>Сеть (MYSQLND_NET)</td>
      <td>mysqlnd_net_init()</td>
      <td>Нет</td>
      <td>Connection::init()</td>
     </tr>

     <tr>
      <td>Протокол обмена (MYSQLND_PROTOCOL)</td>
      <td>mysqlnd_protocol_init()</td>
      <td>Нет</td>
      <td>Connection::init()</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Настоятельно рекомендуется не заменять конструктор
   целиком. Конструкторы производят выделение памяти.
   Выделение памяти жизненно необходимо для API плагинов
   <code class="literal">mysqlnd</code> и для логики объекта
   <code class="literal">mysqlnd</code>. Если вам не страшны
   предупреждения и хотите сильно поменять конструктор, то
   хотя бы вызовите родительский конструктор прежде, чем что-либо делать.
  </p>
  <p class="simpara">
   Несмотря на предупреждения, это бывает полезным для конструктора подкласса.
   Конструкторы — отличное место для изменения таблицы функций для объектов,
   которые не работают с разделённой таблицу, например,
   результирующий набор, сеть, протокол обмена.
  </p>
  <table id="mysqlnd.plugin.deststatus" class="doctable table">
   <caption><strong>Статус уничтожения</strong></caption>
   
    <thead>
     <tr>
      <th class="empty">&nbsp;</th>
      <th>Производный метод должен вызвать родительский?</th>
      <th>Деструктор</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Соединение</td>
      <td>да, после выполнения метода</td>
      <td>free_contents(), end_psession()</td>
     </tr>

     <tr>
      <td>Результирующий набор</td>
      <td>да, после выполнения метода</td>
      <td>free_result()</td>
     </tr>

     <tr>
      <td>Метаданные результирующего набора</td>
      <td>да, после выполнения метода</td>
      <td>free()</td>
     </tr>

     <tr>
      <td>Оператор</td>
      <td>да, после выполнения метода</td>
      <td>dtor(), free_stmt_content()</td>
     </tr>

     <tr>
      <td>Сеть</td>
      <td>да, после выполнения метода</td>
      <td>free()</td>
     </tr>

     <tr>
      <td>Протокол обмена</td>
      <td>да, после выполнения метода</td>
      <td>free()</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Деструкторы являются подходящим местом, чтобы освободить ресурсы, занимаемые
   свойствами
   <code class="literal">mysqlnd_plugin_get_plugin_<span class="replaceable">&lt;object&gt;</span>_data()</code>.
  </p>
  <p class="simpara">
   Перечисленные деструкторы могут не совпадать с актуальными методами
   <code class="literal">mysqlnd</code> для очистки самого объекта. Однако они являются самым лучшим
   местом, куда вы можете вклиниться для очистки данных своего плагина. Так же как и
   с конструкторами, вы можете полностью переопределить эти методы, но делать это не
   рекомендуется. Если вам необходимо вставить в каждый из перечисленных методов
   очистку данных своего плагина, то необходимо обеспечить запуск родительских
   методов <code class="literal">mysqlnd</code>.
  </p>
  <p class="simpara">
   Для плагинов рекомендуют метод — выполнить код очистки данных плагина
   и сразу после этого вызывать родительский метод.
  </p>
 </div><?php manual_footer($setup); ?>