<?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.developing.php',
    1 => 'Начинаем разработку плагина mysqlnd',
    2 => 'Начинаем разработку плагина mysqlnd',
  ),
  'up' => 
  array (
    0 => 'mysqlnd.plugin.php',
    1 => 'API интеграции плагинов в драйвер MySQL Native Driver',
  ),
  'prev' => 
  array (
    0 => 'mysqlnd.plugin.api.php',
    1 => 'API плагинов mysqlnd',
  ),
  'next' => 
  array (
    0 => 'book.oci8.php',
    1 => 'OCI8',
  ),
  '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.developing" class="section">
  <h2 class="title">Начинаем разработку плагина mysqlnd</h2>
  <p class="simpara">
   Важно помнить, что плагин <code class="literal">mysqlnd</code> сам по себе
   является модулем PHP.
  </p>
  <p class="simpara">
   Следующий пример показывает базовую структуру функции MINIT,
   использующуюся в типичном плагине <code class="literal">mysqlnd</code>:
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* my_php_mysqlnd_plugin.c */

 static PHP_MINIT_FUNCTION(mysqlnd_plugin) {
  /* глобальные переменные, ini-настройки, ресурсы, классы */

  /* регистрируем плагин mysqlnd */
  mysqlnd_plugin_id = mysqlnd_plugin_register();

  conn_m = mysqlnd_get_conn_methods();
  memcpy(org_conn_m, conn_m,
    sizeof(struct st_mysqlnd_conn_methods));

  conn_m-&gt;query = MYSQLND_METHOD(mysqlnd_plugin_conn, query);
  conn_m-&gt;connect = MYSQLND_METHOD(mysqlnd_plugin_conn, connect);
}
</pre></div>
  </div>

  <div class="example-contents">
<div class="cdata"><pre>
/* my_mysqlnd_plugin.c */

 enum_func_status MYSQLND_METHOD(mysqlnd_plugin_conn, query)(/* ... */) {
  /* ... */
}
enum_func_status MYSQLND_METHOD(mysqlnd_plugin_conn, connect)(/* ... */) {
  /* ... */
}
</pre></div>
  </div>

  <p class="simpara">
   <strong>Анализ задачи: от C до пользовательского пространства</strong>
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
 class proxy extends mysqlnd_plugin_connection {
  public function connect($host, ...) { .. }
}
mysqlnd_plugin_set_conn_proxy(new proxy());
</pre></div>
  </div>

  <p class="simpara">
   Процесс:
  </p>
  <ol type="1">
   <li class="listitem">
    <span class="simpara">
     PHP: пользователь регистрирует callback-функцию плагина
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     PHP: пользователь вызывает PHP MySQL API для соединения с MySQL
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     C: ext/*mysql* вызывает метод mysqlnd
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     C: mysqlnd обрывается в ext/mysqlnd_plugin
    </span>
   </li>
   <li class="listitem">
    <p class="para">
     C: ext/mysqlnd_plugin
     <ol type="1">
      <li class="listitem">
       <span class="simpara">
        Вызывает пользовательскую callback-функцию
       </span>
      </li>
      <li class="listitem">
       <span class="simpara">
        Или оригинальный метод <code class="literal">mysqlnd</code>, если она не задана
       </span>
      </li>
     </ol>
    </p>
   </li>
  </ol>
  <p class="simpara">
   Вам необходимо выполнить следующие действия:
  </p>
  <ol type="1">
   <li class="listitem">
    <span class="simpara">
     Создайте в С класс &quot;mysqlnd_plugin_connection&quot;
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     Примите и зарегистрируйте прокси объект с помощью
     &quot;mysqlnd_plugin_set_conn_proxy()&quot;
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     Вызовите прокси методы пространства пользователя из C (оптимизация -
     zend_interfaces.h)
    </span>
   </li>
  </ol>
  <p class="simpara">
   Методы объекта пространства пользователя должны быть вызваны с помощью
   <code class="literal">call_user_function()</code> или на уровень ниже, с помощью
   <code class="literal">zend_call_method()</code>.
  </p>
  <p class="simpara">
   <strong>Оптимизация: вызывайте методы из С с помощью
    zend_call_method </strong>
  </p>
  <p class="simpara">
   Следующий кусок кода демонстрирует прототип функции
   <code class="literal">zend_call_method</code>, взятый из
   <var class="filename">zend_interfaces.h</var>.
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
 ZEND_API zval* zend_call_method(
  zval **object_pp, zend_class_entry *obj_ce,
  zend_function **fn_proxy, char *function_name,
  int function_name_len, zval **retval_ptr_ptr,
  int param_count, zval* arg1, zval* arg2 TSRMLS_DC
);
</pre></div>
  </div>

  <p class="simpara">
   API движка Zend поддерживает только два аргумента. Иногда требуется больше. Например:
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
 enum_func_status (*func_mysqlnd_conn__connect)(
  MYSQLND *conn, const char *host,
  const char * user, const char * passwd,
  unsigned int passwd_len, const char * db,
  unsigned int db_len, unsigned int port,
  const char * socket, unsigned int mysql_flags TSRMLS_DC
);
</pre></div>
  </div>

  <p class="simpara">
   Для обхода этой проблемы вам необходимо сделать копию
   <code class="literal">zend_call_method()</code> и добавить необходимые параметры.
   Вы можете сделать это создав набор макросов
   <code class="literal">MY_ZEND_CALL_METHOD_WRAPPER</code>.
  </p>
  <p class="simpara">
   <strong>Обращение к пространству пользователя  PHP</strong>
  </p>
  <p class="simpara">
   Этот кусок кода демонстрирует оптимизированный метод вызова функций пространства
   пользователя из С:
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* my_mysqlnd_plugin.c */

MYSQLND_METHOD(my_conn_class, connect)
(
    MYSQLND *conn, const char *host /* ... */ TSRMLS_DC)
{
    enum_func_status ret = FAIL;
    zval *global_user_conn_proxy = fetch_userspace_proxy();
    if (global_user_conn_proxy)
    {
        /* Вызов прокси пространства пользователя */
        ret = MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, host, /*...*/);
    }
    else
    {
        /* или оригинальный метод mysqlnd = ничего не делать, быть прозрачным */
        ret = org_methods.connect(conn, host, user, passwd,
                                  passwd_len, db, db_len, port,
                                  socket, mysql_flags TSRMLS_CC);
    }
    return ret;
}
</pre></div>
  </div>

  <p class="simpara">
   <strong>Обращение к пространству пользователя: простые аргументы
   </strong>
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* my_mysqlnd_plugin.c */

 MYSQLND_METHOD(my_conn_class,connect)(
  /* ... */, const char *host, /* ...*/) {
  /* ... */
  if (global_user_conn_proxy) {
    /* ... */
    zval* zv_host;
    MAKE_STD_ZVAL(zv_host);
    ZVAL_STRING(zv_host, host, 1);
    MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, zv_retval, zv_host /*, ...*/);
    zval_ptr_dtor(&amp;zv_host);
    /* ... */
  }
  /* ... */
}
</pre></div>
  </div>

  <p class="simpara">
   <strong>Обращение к пространству пользователя: структуры как аргументы
   </strong>
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* my_mysqlnd_plugin.c */

MYSQLND_METHOD(my_conn_class, connect)
(
    MYSQLND *conn, /* ...*/)
{
    /* ... */
    if (global_user_conn_proxy)
    {
        /* ... */
        zval *zv_conn;
        ZEND_REGISTER_RESOURCE(zv_conn, (void *)conn, le_mysqlnd_plugin_conn);
        MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, zv_retval, zv_conn, zv_host /*, ...*/);
        zval_ptr_dtor(&amp;zv_conn);
        /* ... */
    }
    /* ... */
}
</pre></div>
  </div>

  <p class="simpara">
   первый аргумент многих методов <code class="literal">mysqlnd</code> — «объекты» С.
   Например, первый аргумент метода connect() — указатель
   на <code class="literal">MYSQLND</code>. Структура MYSQLND —
   объект соединения <code class="literal">mysqlnd</code>.
  </p>
  <p class="simpara">
   Указатель на объект соединения <code class="literal">mysqlnd</code> сравнивают со стандартным
   обработчиком файлового ввода-вывода. Так же как и он, объект соединения
   <code class="literal">mysqlnd</code> связывают с пространством пользователя
   через PHP-тип resource.
  </p>
  <p class="simpara">
   <strong>Из C в пространство пользователя и обратно</strong>
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
&lt;?php

class proxy extends mysqlnd_plugin_connection
{
    public function connect($conn, $host, ...)
    {
        /* До внедрения */
        printf(&quot;Подключение к = &#039;%s&#039;\n&quot;, $host);
        debug_print_backtrace();
        return parent::connect($conn);
    }

    public function query($conn, $query)
    {
        /* После внедрения */
        $ret = parent::query($conn, $query);
        printf(&quot;Запрос = &#039;%s&#039;\n&quot;, $query);
        return $ret;
    }
}

mysqlnd_plugin_set_conn_proxy(new proxy());
</pre></div>
  </div>

  <p class="simpara">
   Пользователи PHP должны иметь возможность вызывать родительские
   реализации переопределённых методов.
  </p>
  <p class="simpara">
   В результате наследования возможно «уточнить» только выбранные
   методы и вы можете выбрать, когда выполнять ваш код, до или после родительского.
  </p>
  <p class="simpara">
   <strong>Встроенный класс:
    mysqlnd_plugin_connection::connect() </strong>
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/*  my_mysqlnd_plugin_classes.c */

PHP_METHOD(&quot;mysqlnd_plugin_connection&quot;, connect)
{
    /* ... упрощённый! ... */
    zval *mysqlnd_rsrc;
    MYSQLND *conn;
    char *host;
    int host_len;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, &quot;rs&quot;,
                              &amp;mysqlnd_rsrc, &amp;host, &amp;host_len) == FAILURE)
    {
        RETURN_NULL();
    }
    ZEND_FETCH_RESOURCE(conn, MYSQLND * conn, &amp;mysqlnd_rsrc, -1,
                        &quot;Mysqlnd Connection&quot;, le_mysqlnd_plugin_conn);
    if (PASS == org_methods.connect(conn, host, /* simplified! */ TSRMLS_CC))
        RETVAL_TRUE;
    else
        RETVAL_FALSE;
}
</pre></div>
  </div>

 </div><?php manual_footer($setup); ?>