mirror of
https://github.com/php/doc-es.git
synced 2026-03-24 07:22:16 +01:00
1456 lines
47 KiB
XML
1456 lines
47 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- EN-Revision: 9598935f21bc472f22383fb989625f0b22785331 Maintainer: PhilDaiguille Status: ready -->
|
|
<!-- Reviewed: no -->
|
|
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="mysqlnd.plugin">
|
|
<title>API del plugin del controlador nativo MySQL</title>
|
|
<simpara>
|
|
La API del plugin del controlador nativo MySQL es una funcionalidad
|
|
del controlador nativo MySQL, o <literal>mysqlnd</literal>.
|
|
El plugin <literal>Mysqlnd</literal> opera en la capa entre
|
|
las aplicaciones PHP y el servidor MySQL. Es comparable
|
|
a un proxy MySQL. Un proxy MySQL opera en una capa entre todas
|
|
las aplicaciones cliente MySQL, por ejemplo, una aplicación PHP
|
|
y un servidor MySQL. El plugin <literal>Mysqlnd</literal>
|
|
puede realizar tareas típicas de proxy MySQL como
|
|
el equilibrio de carga, así como el seguimiento y la optimización
|
|
de las prestaciones. Debido a una arquitectura y una localización
|
|
diferente, el plugin <literal>mysqlnd</literal> no tiene todos los
|
|
inconvenientes de un proxy MySQL. Por ejemplo, con el plugin, no
|
|
hay un único punto de fallo, no hay un servidor proxy dedicado
|
|
que desplegar, y no hay un nuevo lenguaje que aprender (Lua).
|
|
</simpara>
|
|
<simpara>
|
|
Un plugin <literal>mysqlnd</literal> puede ejecutarse como una extensión
|
|
a <literal>mysqlnd</literal>. Un plugin puede interceptar la mayoría de
|
|
las funciones <literal>mysqlnd</literal>. Las funciones <literal>mysqlnd</literal>
|
|
son llamadas por la extensión PHP MySQL como
|
|
<literal>ext/mysql</literal>, <literal>ext/mysqli</literal>, y
|
|
<literal>PDO_MYSQL</literal>. Como resultado, es posible para un
|
|
plugin <literal>mysqlnd</literal> interceptar todas las llamadas realizadas
|
|
por estas extensiones desde una aplicación cliente.
|
|
</simpara>
|
|
<simpara>
|
|
Las llamadas a las funciones internas <literal>mysqlnd</literal> pueden
|
|
también ser interceptadas o reemplazadas. No hay ninguna restricción
|
|
sobre la manipulación de las tablas de funciones internas <literal>mysqlnd</literal>.
|
|
Es posible definir acciones para que cuando
|
|
ciertas funciones <literal>mysqlnd</literal> sean llamadas
|
|
por la extensión que utiliza <literal>mysqlnd</literal>, la llamada
|
|
sea redirigida hacia la función apropiada del plugin
|
|
<literal>mysqlnd</literal>. La posibilidad de manipular las tablas
|
|
de funciones internas <literal>mysqlnd</literal> de este modo permite
|
|
un máximo de flexibilidad.
|
|
</simpara>
|
|
<simpara>
|
|
El plugin <literal>Mysqlnd</literal> es, en realidad, una extensión PHP,
|
|
escrita en C, que utiliza la API del plugin <literal>mysqlnd</literal>
|
|
(que está compilada en el controlador nativo MySQL, <literal>mysqlnd</literal>).
|
|
El plugin puede ser 100% transparente para las aplicaciones PHP. No se requiere
|
|
ninguna modificación a las aplicaciones ya que el plugin opera
|
|
en una capa diferente. El plugin <literal>mysqlnd</literal>
|
|
puede ser utilizado en una capa por debajo de <literal>mysqlnd</literal>.
|
|
</simpara>
|
|
<simpara>
|
|
La lista siguiente representa algunas aplicaciones posibles
|
|
del plugin <literal>mysqlnd</literal>.
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
El equilibrio de carga.
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Separación de lecturas y escrituras. Un ejemplo de esta funcionalidad
|
|
es la extensión PECL/mysqlnd_ms (Maestro/esclavo). Esta extensión separa
|
|
las consultas de lectura y escritura para una configuración de replicación.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Conmutación por error
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Round-Robin, el menos cargado
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Supervisión
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Registro de consultas
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Análisis de consultas
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Auditoría de consultas. Un ejemplo de esto es la extensión
|
|
PECL/mysqlnd_sip (Protección contra Inyección SQL). Esta extensión
|
|
inspecciona las consultas y ejecuta únicamente aquellas que son
|
|
permitidas siguiendo conjuntos de reglas.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Rendimiento
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
La caché. Un ejemplo de esto es la extensión
|
|
PECL/mysqlnd_qc (Query Cache).
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Limitación
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Fragmentación. Un ejemplo de esto es la extensión
|
|
PECL/mysqlnd_mc (Multi Connect). Esta extensión intenta
|
|
separar una consulta SELECT en n partes, utilizando
|
|
consultas del tipo SELECT ... LIMIT part_1, SELECT LIMIT part_n.
|
|
La extensión envía las consultas a servidores MySQL distintos
|
|
y luego fusiona el resultado hacia el cliente.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>
|
|
<emphasis role="bold">Plugins del controlador nativo MySQL disponibles</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
Ya hay varios plugins mysqlnd disponibles.
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
<emphasis role="bold">PECL/mysqlnd_mc</emphasis> - Plugin Multi Conexión.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<emphasis role="bold">PECL/mysqlnd_ms</emphasis> - Plugin Maestro Esclavo.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<emphasis role="bold">PECL/mysqlnd_qc</emphasis> - Plugin de caché de consultas.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<emphasis role="bold">PECL/mysqlnd_pscache</emphasis> - Plugin de caché de gestor
|
|
de consultas preparadas.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<emphasis role="bold">PECL/mysqlnd_sip</emphasis> - Plugin que permite la protección
|
|
contra inyecciones SQL.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<emphasis role="bold">PECL/mysqlnd_uh</emphasis> - Plugin de gestor de usuarios.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<section xml:id="mysqlnd.plugin.mysql-proxy">
|
|
<title>Comparación de los plugins mysqlnd con proxy MySQL</title>
|
|
<simpara>
|
|
Los plugins <literal>Mysqlnd</literal> y el proxy MySQL son
|
|
tecnologías diferentes que utilizan diferentes enfoques.
|
|
Ambos son herramientas válidas para resolver muchas
|
|
tareas clásicas, como el equilibrio de carga, la supervisión,
|
|
y la mejora de las prestaciones. Una diferencia importante es
|
|
que el proxy MySQL funciona con todos los clientes MySQL mientras que
|
|
los plugins <literal>mysqlnd</literal> son específicos para
|
|
las aplicaciones PHP.
|
|
</simpara>
|
|
<simpara>
|
|
Como una extensión PHP, un plugin <literal>mysqlnd</literal>
|
|
debe ser instalado en el servidor de aplicaciones PHP, además del
|
|
resto de PHP. Un proxy MySQL puede funcionar tanto en el servidor
|
|
de aplicaciones PHP como ser instalado en una máquina dedicada para
|
|
gestionar varios servidores de aplicaciones PHP.
|
|
</simpara>
|
|
<simpara>
|
|
El despliegue de un proxy MySQL en un servidor de aplicaciones
|
|
tiene 2 ventajas:
|
|
</simpara>
|
|
<orderedlist>
|
|
<listitem>
|
|
<simpara>
|
|
No hay un único punto de fallo
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Fácil de escalar (escalado horizontal,
|
|
escalado por el cliente)
|
|
</simpara>
|
|
</listitem>
|
|
</orderedlist>
|
|
<simpara>
|
|
Un proxy MySQL (y los plugins <literal>mysqlnd</literal>) puede
|
|
resolver problemas fácilmente, que de otro modo habrían requerido
|
|
modificaciones a las aplicaciones existentes.
|
|
</simpara>
|
|
<simpara>
|
|
Sin embargo, un proxy MySQL tiene algunos inconvenientes:
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Un proxy MySQL es un nuevo componente, una nueva tecnología a
|
|
aplicar al maestro y desplegar.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Un proxy MySQL requiere el conocimiento del lenguaje de script Lua.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>
|
|
Un proxy MySQL puede ser personalizado utilizando los lenguajes de
|
|
programación C y Lua. Lua es el lenguaje preferido para un proxy MySQL.
|
|
Para la mayoría de los expertos PHP, Lua es un nuevo lenguaje a aprender.
|
|
Un plugin <literal>mysqlnd</literal> puede ser escrito en C. También
|
|
es posible escribir un plugin en PHP utilizando
|
|
<link xlink:href="&url.pecl.package;mysqlnd_uh">PECL/mysqlnd_uh</link>.
|
|
</simpara>
|
|
<simpara>
|
|
Un proxy MySQL funciona como un demonio - un proceso en segundo plano.
|
|
Un proxy MySQL puede recordar decisiones tomadas anteriormente,
|
|
ya que todos los estados pueden ser conservados. Sin embargo, un plugin
|
|
<literal>mysqlnd</literal> está ligado al ciclo de vida de una consulta PHP.
|
|
Un proxy MySQL puede compartir también resultados calculados una sola vez
|
|
entre varios servidores de aplicaciones. Un plugin <literal>mysqlnd</literal>
|
|
puede necesitar almacenar datos en un medio persistente.
|
|
Otro demonio puede ser utilizado con este propósito, como por ejemplo,
|
|
Memcache. Este mecanismo da una ventaja al proxy MySQL.
|
|
</simpara>
|
|
<simpara>
|
|
Un proxy MySQL funciona por encima de la capa física. Con un
|
|
proxy MySQL, debe analizar y realizar ingeniería inversa
|
|
del protocolo cliente-servidor MySQL. Las acciones están limitadas a las
|
|
que pueden ser realizadas mediante la manipulación del protocolo
|
|
de comunicación. Si la capa física cambia (lo que ocurre muy raramente),
|
|
los scripts del proxy MySQL pueden necesitar ser adaptados.
|
|
</simpara>
|
|
<simpara>
|
|
Los plugins <literal>Mysqlnd</literal> funcionan por encima de la API C,
|
|
reflejando así las APIs cliente <literal>libmysqlclient</literal>.
|
|
Esta API C es esencialmente una envoltura del protocolo Servidor Cliente MySQL,
|
|
o de la capa física, ya que es llamada algunas veces. Puede
|
|
interceptar todas las llamadas a la API C. PHP utiliza la API C, sin embargo,
|
|
puede conectar todas las llamadas PHP, sin necesidad de programar
|
|
a nivel de la capa física.
|
|
</simpara>
|
|
<simpara>
|
|
<literal>Mysqlnd</literal> implementa la capa física. Los plugins
|
|
pueden analizar, realizar ingeniería inversa, manipular
|
|
y siempre reemplazar el protocolo de comunicación. Sin embargo, esto no es
|
|
generalmente necesario.
|
|
</simpara>
|
|
<simpara>
|
|
Dado que los plugins permiten crear implementaciones que
|
|
utilizan los 2 niveles (API C y capa física), tienen más flexibilidad
|
|
que el proxy MySQL. Si un plugin <literal>mysqlnd</literal> es implementado
|
|
utilizando la API C, todos los cambios posteriores a la capa
|
|
física no requerirán modificaciones al plugin en sí.
|
|
</simpara>
|
|
</section>
|
|
<section xml:id="mysqlnd.plugin.obtaining">
|
|
<title>Obtener la API del plugin mysqlnd</title>
|
|
<simpara>
|
|
La API del plugin <literal>mysqlnd</literal> es simplemente una parte
|
|
de la extensión del controlador PHP Nativo MySQL, <literal>ext/mysqlnd</literal>.
|
|
El desarrollo de la API del plugin <literal>mysqlnd</literal> comenzó
|
|
en Diciembre de 2009. Se desarrolla como parte del repositorio fuente de PHP,
|
|
y por lo tanto, está disponible desde el repositorio público Git, o desde
|
|
la descarga de las fuentes.
|
|
</simpara>
|
|
<simpara>
|
|
Los desarrolladores de plugins pueden determinar la versión de
|
|
<literal>mysqlnd</literal> a través de la variable
|
|
<literal>MYSQLND_VERSION</literal>,
|
|
en el formato <quote>mysqlnd 8.3.17</quote>,
|
|
o a través de <literal>MYSQLND_VERSION_ID</literal>, que es un entero
|
|
como por ejemplo 50007. Los desarrolladores pueden calcular el número
|
|
de versión de la siguiente manera:
|
|
</simpara>
|
|
<table xml:id="mysqlnd.plugin.version-id">
|
|
<title>Tabla de cálculo de MYSQLND_VERSION_ID</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Versión (parte)</entry>
|
|
<entry>Ejemplo</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Mayor*10000</entry>
|
|
<entry>5*10000 = 50000</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Menor*100</entry>
|
|
<entry>0*100 = 0</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Parche</entry>
|
|
<entry>7 = 7</entry>
|
|
</row>
|
|
<row>
|
|
<entry>MYSQLND_VERSION_ID</entry>
|
|
<entry>50007</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<simpara>
|
|
Durante el desarrollo, los desarrolladores deben referirse
|
|
al número de versión <literal>mysqlnd</literal> para pruebas
|
|
de compatibilidad y de versión, sabiendo que varias
|
|
versiones de <literal>mysqlnd</literal> pueden ocurrir durante
|
|
un ciclo de vida de la rama de desarrollo de PHP.
|
|
</simpara>
|
|
</section>
|
|
<section xml:id="mysqlnd.plugin.architecture">
|
|
<title>Arquitectura del plugin del controlador nativo</title>
|
|
<simpara>
|
|
Esta sección proporciona una visión general de la arquitectura del plugin
|
|
<literal>mysqlnd</literal>.
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">Visión general del controlador nativo MySQL</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
Antes de desarrollar plugins <literal>mysqlnd</literal>,
|
|
es útil tener un conocimiento mínimo sobre la organización
|
|
de <literal>mysqlnd</literal>. <literal>Mysqlnd</literal> está compuesto
|
|
por los siguientes módulos:
|
|
</simpara>
|
|
<table xml:id="mysqlnd.plugin.orgchart">
|
|
<title>Esquema de la organización mysqlnd, por módulo</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Módulos de estadísticas</entry>
|
|
<entry>mysqlnd_statistics.c</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Conexión</entry>
|
|
<entry>mysqlnd.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Juego de resultados</entry>
|
|
<entry>mysqlnd_result.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Datos méta del juego de resultados</entry>
|
|
<entry>mysqlnd_result_meta.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Consulta</entry>
|
|
<entry>mysqlnd_ps.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Red</entry>
|
|
<entry>mysqlnd_net.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Capa física</entry>
|
|
<entry>mysqlnd_wireprotocol.c</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<simpara>
|
|
<emphasis role="bold">Objeto C orientado a paradigma</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
A nivel de código, <literal>mysqlnd</literal> utiliza una máscara C
|
|
para implementar la orientación al objeto.
|
|
</simpara>
|
|
<simpara>
|
|
En C, se utiliza una estructura (<literal>struct</literal>)
|
|
para representar un objeto. Los miembros de esta estructura
|
|
representan las propiedades del objeto. Los miembros de la
|
|
estructura que apuntan hacia funciones representan los métodos.
|
|
</simpara>
|
|
<simpara>
|
|
A diferencia de otros lenguajes como C++ o Java, no hay
|
|
reglas fijas sobre la herencia en los objetos C orientados a paradigma.
|
|
Sin embargo, hay algunas convenciones que deben seguirse
|
|
que serán abordadas posteriormente.
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">El ciclo de vida PHP</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
El ciclo de vida de PHP consta de 2 ciclos básicos:
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
El ciclo de inicio y parada del motor PHP
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
El ciclo de una petición
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>
|
|
Cuando el motor PHP se inicia, llama a la función de inicialización
|
|
del módulo (MINIT) de cada extensión registrada. Esto
|
|
permite a cada módulo definir las variables y asignar los
|
|
recursos que deben existir durante la vida del proceso
|
|
correspondiente al motor PHP. Cuando el motor PHP se detiene,
|
|
llama a la función de parada del módulo (MSHUTDOWN) para cada extensión.
|
|
</simpara>
|
|
<simpara>
|
|
Durante la vida del motor PHP, recibirá peticiones.
|
|
Cada petición constituye otro ciclo de vida. Para cada
|
|
petición, el motor PHP llamará a la función de inicialización
|
|
de cada extensión. La extensión puede realizar todas las definiciones
|
|
de variables así como las asignaciones de recursos necesarias para
|
|
procesar la petición. Cuando el ciclo de la petición termina, el motor
|
|
llama a la función de parada (RSHUTDOWN) para cada extensión,
|
|
por lo que la extensión puede lanzar toda la limpieza necesaria.
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">Cómo funciona un plugin</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
Un plugin <literal>mysqlnd</literal> funciona interceptando las llamadas
|
|
realizadas a <literal>mysqlnd</literal> por las extensiones que utilizan
|
|
<literal>mysqlnd</literal>. Esto es posible obteniendo la tabla
|
|
de función <literal>mysqlnd</literal>, guardándola, y reemplazándola por una tabla
|
|
de función personalizada, que llama a las funciones del plugin.
|
|
</simpara>
|
|
<simpara>
|
|
El código siguiente muestra cómo se reemplaza la tabla de función
|
|
<literal>mysqlnd</literal>:
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* un lugar para almacenar la tabla de función original */
|
|
struct st_mysqlnd_conn_methods org_methods;
|
|
|
|
void minit_register_hooks(TSRMLS_D) {
|
|
/* tabla de función activa */
|
|
struct st_mysqlnd_conn_methods * current_methods
|
|
= mysqlnd_conn_get_methods();
|
|
|
|
/* guardar la tabla de función original */
|
|
memcpy(&org_methods, current_methods,
|
|
sizeof(struct st_mysqlnd_conn_methods));
|
|
|
|
/* instalación de las nuevas métodos */
|
|
current_methods->query = MYSQLND_METHOD(my_conn_class, query);
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
Las manipulaciones de la tabla de función de conexión deben
|
|
realizarse durante la inicialización del módulo (MINIT).
|
|
La tabla de función es un recurso global compartido. En
|
|
un entorno multihilo, con una compilación TSRM, la
|
|
manipulación de un recurso global compartido durante un proceso
|
|
de petición generalmente resultará en conflictos.
|
|
</simpara>
|
|
<note>
|
|
<simpara>
|
|
No utilice ninguna lógica de tamaño fijo al manipular
|
|
la tabla de función <literal>mysqlnd</literal>: las nuevas
|
|
funciones pueden ser añadidas al final de la tabla de función.
|
|
La tabla de función puede ser modificada en cualquier momento después.
|
|
</simpara>
|
|
</note>
|
|
<simpara>
|
|
<emphasis role="bold">Llamada a métodos padres</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
Si la tabla de función original se guarda, siempre
|
|
es posible llamar a las entradas de la tabla de función original -
|
|
los métodos padres.
|
|
</simpara>
|
|
<simpara>
|
|
En este caso, al igual que <literal>Connection::stmt_init()</literal>,
|
|
es vital llamar al método padre antes de cualquier otra actividad
|
|
en el método derivado.
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
MYSQLND_METHOD(my_conn_class, query)(MYSQLND *conn,
|
|
const char *query, unsigned int query_len TSRMLS_DC) {
|
|
|
|
php_printf("my_conn_class::query(query = %s)\n", query);
|
|
|
|
query = "SELECT 'query rewritten' FROM DUAL";
|
|
query_len = strlen(query);
|
|
|
|
return org_methods.query(conn, query, query_len); /* retorno con llamada al padre */
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
<emphasis role="bold">Extender propiedades</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
Un objeto <literal>mysqlnd</literal> está representado por una estructura C.
|
|
No es posible añadir un miembro a una estructura C en tiempo de ejecución.
|
|
Los usuarios de objetos <literal>mysqlnd</literal>
|
|
no pueden simplemente añadir propiedades a los objetos.
|
|
</simpara>
|
|
<simpara>
|
|
Datos arbitrarios (propiedades) pueden ser añadidos a los objetos
|
|
<literal>mysqlnd</literal> utilizando una función apropiada de la
|
|
familia <literal>mysqlnd_plugin_get_plugin_<object>_data()</literal>.
|
|
Durante la asignación de un objeto, <literal>mysqlnd</literal> reserva
|
|
un espacio al final del objeto para alojar un puntero
|
|
<literal>void *</literal> hacia datos arbitrarios.
|
|
<literal>mysqlnd</literal> reserva un espacio para un puntero
|
|
<literal>void *</literal> por plugin.
|
|
</simpara>
|
|
<simpara>
|
|
La tabla siguiente muestra cómo calcular la posición de un puntero
|
|
para un plugin específico:
|
|
</simpara>
|
|
<table xml:id="mysqlnd.plugin.pointercalc">
|
|
<title>Cálculo de punteros para mysqlnd</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Dirección de memoria</entry>
|
|
<entry>Contenido</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>0</entry>
|
|
<entry>Inicio de la estructura C del objeto mysqlnd</entry>
|
|
</row>
|
|
<row>
|
|
<entry>n</entry>
|
|
<entry>Fin de la estructura C del objeto mysqlnd</entry>
|
|
</row>
|
|
<row>
|
|
<entry>n + (m x sizeof(void*))</entry>
|
|
<entry>void* hacia los datos del objeto del plugin m-ésimo</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<simpara>
|
|
Si planea hacer subclases de los constructores
|
|
de los objetos <literal>mysqlnd</literal>, lo cual está permitido,
|
|
debe tener esto en cuenta.
|
|
</simpara>
|
|
<simpara>
|
|
El código siguiente muestra cómo se extienden propiedades:
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* todos los datos que queremos asociar */
|
|
typedef struct my_conn_properties {
|
|
unsigned long query_counter;
|
|
} MY_CONN_PROPERTIES;
|
|
|
|
/* id del plugin */
|
|
unsigned int my_plugin_id;
|
|
|
|
void minit_register_hooks(TSRMLS_D) {
|
|
/* obtenemos un ID único para el plugin */
|
|
my_plugin_id = mysqlnd_plugin_register();
|
|
/* snip - ver la extensión de la conexión: métodos */
|
|
}
|
|
|
|
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->persistent);
|
|
(*props)->query_counter = 0;
|
|
}
|
|
return props;
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
El desarrollador del plugin es responsable de la gestión de la memoria
|
|
asociada a los datos del plugin.
|
|
</simpara>
|
|
<simpara>
|
|
Se recomienda el uso del asignador de memoria <literal>mysqlnd</literal>
|
|
para los datos del plugin. Estas funciones son nombradas
|
|
utilizando la siguiente convención: <literal>mnd_*loc()</literal>.
|
|
El asignador <literal>mysqlnd</literal> tiene algunas características muy útiles,
|
|
como la posibilidad de utilizar un asignador de depuración en una compilación
|
|
no depurada.
|
|
</simpara>
|
|
<table xml:id="mysqlnd.plugin.subclass">
|
|
<title>Cuándo y cómo hacer una subclase</title>
|
|
<tgroup cols="4">
|
|
<thead>
|
|
<row>
|
|
<entry/>
|
|
<entry>¿Cuándo hacer una subclase?</entry>
|
|
<entry>¿Cada instancia tiene su propia tabla de funciones privada?</entry>
|
|
<entry>¿Cómo hacer una subclase?</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Conexión (MYSQLND)</entry>
|
|
<entry>MINIT</entry>
|
|
<entry>No</entry>
|
|
<entry>mysqlnd_conn_get_methods()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Juego de resultados (MYSQLND_RES)</entry>
|
|
<entry>MINIT o después</entry>
|
|
<entry>Sí</entry>
|
|
<entry>mysqlnd_result_get_methods() o método del objeto de manipulación de la tabla de funciones</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Meta del juego de resultados (MYSQLND_RES_METADATA)</entry>
|
|
<entry>MINIT</entry>
|
|
<entry>No</entry>
|
|
<entry>mysqlnd_result_metadata_get_methods()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Consulta (MYSQLND_STMT)</entry>
|
|
<entry>MINIT</entry>
|
|
<entry>No</entry>
|
|
<entry>mysqlnd_stmt_get_methods()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Red (MYSQLND_NET)</entry>
|
|
<entry>MINIT o después</entry>
|
|
<entry>Sí</entry>
|
|
<entry>mysqlnd_net_get_methods() o método del objeto de manipulación de la tabla de funciones</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Capa física (MYSQLND_PROTOCOL)</entry>
|
|
<entry>MINIT o después</entry>
|
|
<entry>Sí</entry>
|
|
<entry>mysqlnd_protocol_get_methods() o método del objeto de manipulación de la tabla de funciones</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<simpara>
|
|
No debe manipular las tablas de funciones después de MINIT si
|
|
no está permitido según la tabla anterior.
|
|
</simpara>
|
|
<simpara>
|
|
Algunas clases contienen un puntero hacia un método de la tabla
|
|
de funciones. Todas las instancias de una clase de este tipo compartirán
|
|
la misma tabla de funciones. Para evitar el caos, en particular
|
|
en entornos multihilo, este tipo de tablas de funciones solo debe ser
|
|
manipulado durante el MINIT.
|
|
</simpara>
|
|
<simpara>
|
|
Las otras clases utilizan una copia de la tabla de funciones
|
|
global compartida. Esta copia se crea al mismo tiempo que el objeto.
|
|
Cada objeto utiliza su propia tabla de funciones. Esto le da
|
|
2 opciones: puede manipular la tabla de funciones por defecto
|
|
de un objeto durante el MINIT, y también puede afinar métodos de un objeto
|
|
sin afectar a las otras instancias de la misma clase.
|
|
</simpara>
|
|
<simpara>
|
|
La ventaja del enfoque con una tabla de funciones compartida es
|
|
el rendimiento. No es necesario copiar una tabla de funciones
|
|
para cada objeto.
|
|
</simpara>
|
|
<table xml:id="mysqlnd.plugin.constatus">
|
|
<title>Estado del constructor</title>
|
|
<tgroup cols="4">
|
|
<thead>
|
|
<row>
|
|
<entry>Tipo</entry>
|
|
<entry>Asignación, construcción, reinicialización</entry>
|
|
<entry>¿Puede ser modificado?</entry>
|
|
<entry>Llamante</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Conexión (MYSQLND)</entry>
|
|
<entry>mysqlnd_init()</entry>
|
|
<entry>No</entry>
|
|
<entry>mysqlnd_connect()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Juego de resultados (MYSQLND_RES)</entry>
|
|
<entry><simpara>
|
|
Asignación:
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Connection::result_init()
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>
|
|
Reinicio y reinicialización durante:
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Result::use_result()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Result::store_result
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist></entry>
|
|
<entry>Sí, pero llamada al padre.</entry>
|
|
<entry><itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Connection::list_fields()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Statement::get_result()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Statement::prepare() (Meta-datos únicamente)
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Statement::resultMetaData()
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist></entry>
|
|
</row>
|
|
<row>
|
|
<entry>Meta del juego de resultados (MYSQLND_RES_METADATA)</entry>
|
|
<entry>Connection::result_meta_init()</entry>
|
|
<entry>Sí, pero llamada al padre.</entry>
|
|
<entry>Result::read_result_metadata()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Consulta (MYSQLND_STMT)</entry>
|
|
<entry>Connection::stmt_init()</entry>
|
|
<entry>Sí, pero llamada al padre.</entry>
|
|
<entry>Connection::stmt_init()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Red (MYSQLND_NET)</entry>
|
|
<entry>mysqlnd_net_init()</entry>
|
|
<entry>No</entry>
|
|
<entry>Connection::init()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Capa física (MYSQLND_PROTOCOL)</entry>
|
|
<entry>mysqlnd_protocol_init()</entry>
|
|
<entry>No</entry>
|
|
<entry>Connection::init()</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<simpara>
|
|
Se recomienda encarecidamente no reemplazar completamente un constructor.
|
|
Los constructores realizan asignaciones de memoria. Las asignaciones
|
|
de memoria son vitales para la API del plugin <literal>mysqlnd</literal>
|
|
así como para la lógica del objeto <literal>mysqlnd</literal>. Si
|
|
no se preocupa por las advertencias e insiste en
|
|
reemplazar los constructores, debería al menos llamar
|
|
al constructor padre antes de hacer cualquier cosa en su
|
|
constructor.
|
|
</simpara>
|
|
<simpara>
|
|
A nivel de todas las advertencias, puede ser útil hacer subclases de los
|
|
constructores. Los constructores son los lugares perfectos para modificar
|
|
las tablas de funciones de los objetos con tablas de objetos no compartidas,
|
|
como los juegos de resultados, la red o la capa física.
|
|
</simpara>
|
|
<table xml:id="mysqlnd.plugin.deststatus">
|
|
<title>Estado del destructor</title>
|
|
<tgroup cols="3">
|
|
<thead>
|
|
<row>
|
|
<entry>Tipo</entry>
|
|
<entry>¿El método derivado debe llamar al padre?</entry>
|
|
<entry>Destructor</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Conexión</entry>
|
|
<entry>Sí, después de la ejecución del método</entry>
|
|
<entry>free_contents(), end_psession()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Juego de resultados</entry>
|
|
<entry>Sí, después de la ejecución del método</entry>
|
|
<entry>free_result()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Meta del juego de resultados</entry>
|
|
<entry>Sí, después de la ejecución del método</entry>
|
|
<entry>free()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Consulta</entry>
|
|
<entry>Sí, después de la ejecución del método</entry>
|
|
<entry>dtor(), free_stmt_content()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Red</entry>
|
|
<entry>Sí, después de la ejecución del método</entry>
|
|
<entry>free()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Capa física</entry>
|
|
<entry>Sí, después de la ejecución del método</entry>
|
|
<entry>free()</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<simpara>
|
|
Los destructores son los lugares perfectos para liberar propiedades,
|
|
<literal>mysqlnd_plugin_get_plugin_<replaceable><object></replaceable>_data()</literal>.
|
|
</simpara>
|
|
<simpara>
|
|
Los destructores listados pueden no ser los equivalentes a
|
|
los métodos actuales <literal>mysqlnd</literal> que liberan el objeto mismo.
|
|
Sin embargo, son los mejores lugares para liberar
|
|
los datos de su plugin. Al igual que los constructores, puede reemplazar
|
|
métodos completos pero no se recomienda. Si varios métodos están listados
|
|
en la tabla anterior, debe modificar todos los métodos listados y liberar
|
|
los datos de su plugin en el método llamado primero por <literal>mysqlnd</literal>.
|
|
</simpara>
|
|
<simpara>
|
|
El método recomendado para los plugins es simplemente modificar los métodos,
|
|
liberar su memoria y llamar a la implementación del padre inmediatamente después.
|
|
</simpara>
|
|
</section>
|
|
<section xml:id="mysqlnd.plugin.api">
|
|
<title>La API del plugin mysqlnd</title>
|
|
<simpara>
|
|
A continuación se presenta la lista de funciones proporcionadas en la API plugin
|
|
<literal>mysqlnd</literal>:
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_plugin_register()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_plugin_count()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_plugin_get_plugin_connection_data()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_plugin_get_plugin_result_data()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_plugin_get_plugin_stmt_data()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_plugin_get_plugin_net_data()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_plugin_get_plugin_protocol_data()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_conn_get_methods()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_result_get_methods()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_result_meta_get_methods()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_stmt_get_methods()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_net_get_methods()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_protocol_get_methods()
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>
|
|
No hay una definición formal de qué es un plugin
|
|
ni de cómo funciona un plugin.
|
|
</simpara>
|
|
<simpara>
|
|
Los componentes más frecuentemente encontrados en los mecanismos de plugin son:
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Un gestor de plugin
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Una API del plugin
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Los servicios aplicativos (o módulos)
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Las APIs de los servicios aplicativos (o APIs del módulo)
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>
|
|
El concepto de un plugin <literal>mysqlnd</literal> utiliza estas características,
|
|
así como otras joyas de arquitectura abierta.
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">Sin restricciones</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
Un plugin tiene acceso total a los trabajos internos de
|
|
<literal>mysqlnd</literal>. No hay límites de seguridad
|
|
ni restricciones. Todo puede ser sobrescrito para implementar
|
|
algoritmos útiles o hostiles. Se recomienda desplegar
|
|
solo plugins desde fuentes de confianza.
|
|
</simpara>
|
|
<simpara>
|
|
Tal como se ha discutido anteriormente, los plugins pueden utilizar libremente
|
|
punteros. Estos punteros no están restringidos de ninguna manera,
|
|
por lo que puede apuntar hacia los datos de otro plugin.
|
|
Una simple posición aritmética puede ser utilizada para leer
|
|
los datos de otro plugin.
|
|
</simpara>
|
|
<simpara>
|
|
Se recomienda escribir plugins cooperativos, y por lo tanto, siempre llamar
|
|
al método padre. Los plugins deben cooperar siempre con
|
|
<literal>mysqlnd</literal>.
|
|
</simpara>
|
|
<table xml:id="mysqlnd.plugin.chaining">
|
|
<title>Problemas: un ejemplo de encadenamiento y cooperación</title>
|
|
<tgroup cols="3">
|
|
<thead>
|
|
<row>
|
|
<entry>Extensión</entry>
|
|
<entry>Puntero mysqlnd.query()</entry>
|
|
<entry>Pila de llamadas si se llama al padre</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>ext/mysqlnd</entry>
|
|
<entry>mysqlnd.query()</entry>
|
|
<entry>mysqlnd.query</entry>
|
|
</row>
|
|
<row>
|
|
<entry>ext/mysqlnd_cache</entry>
|
|
<entry>mysqlnd_cache.query()</entry>
|
|
<entry><orderedlist>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_cache.query()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd.query
|
|
</simpara>
|
|
</listitem>
|
|
</orderedlist></entry>
|
|
</row>
|
|
<row>
|
|
<entry>ext/mysqlnd_monitor</entry>
|
|
<entry>mysqlnd_monitor.query()</entry>
|
|
<entry><orderedlist>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_monitor.query()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd_cache.query()
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
mysqlnd.query
|
|
</simpara>
|
|
</listitem>
|
|
</orderedlist></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<simpara>
|
|
En este escenario, un plugin de caché (<literal>ext/mysqlnd_cache</literal>) y
|
|
un plugin de supervisión (<literal>ext/mysqlnd_monitor</literal>)
|
|
están cargados. Ambos tienen una subclase de <literal>Connection::query()</literal>.
|
|
El registro del plugin ocurre durante el <literal>MINIT</literal>
|
|
utilizando la lógica mencionada anteriormente. PHP llama a las extensiones
|
|
en un orden alfabético por defecto. Los plugins no están al tanto
|
|
unos de otros y no pueden fijar dependencias.
|
|
</simpara>
|
|
<simpara>
|
|
Por defecto, los plugins llaman a la implementación del padre de la
|
|
función de consulta en su versión de la función derivada.
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">Resumen de la extensión PHP</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
A continuación se presenta un resumen de lo que ocurre al utilizar
|
|
un plugin de ejemplo, <literal>ext/mysqlnd_plugin</literal>,
|
|
que expone la API C del plugin <literal>mysqlnd</literal> a PHP:
|
|
</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Todas las aplicaciones PHP MySQL intentan establecer una conexión
|
|
a la dirección 192.168.2.29
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
La aplicación PHP utilizará <literal>ext/mysql</literal>,
|
|
<literal>ext/mysqli</literal> o <literal>PDO_MYSQL</literal>.
|
|
Estas 3 extensiones PHP MySQL utilizan <literal>mysqlnd</literal> para
|
|
establecer la conexión a la dirección 192.168.2.29.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<literal>Mysqlnd</literal> llama a su método de conexión, que ha sido subclaseado
|
|
por <literal>ext/mysqlnd_plugin</literal>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<literal>ext/mysqlnd_plugin</literal> llama al método del espacio de usuario
|
|
<literal>proxy::connect()</literal> registrado por el usuario.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
El espacio de usuario modifica el host de conexión de 192.168.2.29
|
|
a 127.0.0.1 y devuelve la conexión establecida por
|
|
<literal>parent::connect()</literal>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<literal>ext/mysqlnd_plugin</literal> ejecuta el equivalente de
|
|
<literal>parent::connect(127.0.0.1)</literal> llamando al método
|
|
original de <literal>mysqlnd</literal> para establecer una conexión.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
<literal>ext/mysqlnd</literal> establece una conexión y devuelve el control
|
|
a <literal>ext/mysqlnd_plugin</literal>.
|
|
<literal>ext/mysqlnd_plugin</literal> también devuelve.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Cualquier extensión PHP MySQL utilizada por la aplicación,
|
|
recibe una conexión a 127.0.0.1. La extensión PHP MySQL
|
|
devuelve el control a la aplicación PHP. El ciclo está cerrado.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
<section xml:id="mysqlnd.plugin.developing">
|
|
<title>Cómo comenzar a compilar un plugin mysqlnd</title>
|
|
<simpara>
|
|
Es importante recordar que un plugin <literal>mysqlnd</literal>
|
|
es en sí mismo una extensión PHP.
|
|
</simpara>
|
|
<simpara>
|
|
El código siguiente muestra la estructura básica de una función MINIT
|
|
utilizada en un plugin típico <literal>mysqlnd</literal>:
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* my_php_mysqlnd_plugin.c */
|
|
|
|
static PHP_MINIT_FUNCTION(mysqlnd_plugin) {
|
|
/* globales, entradas ini, recursos, clases */
|
|
|
|
/* registro del plugin 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->query = MYSQLND_METHOD(mysqlnd_plugin_conn, query);
|
|
conn_m->connect = MYSQLND_METHOD(mysqlnd_plugin_conn, connect);
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* my_mysqlnd_plugin.c */
|
|
|
|
enum_func_status MYSQLND_METHOD(mysqlnd_plugin_conn, query)(/* ... */) {
|
|
/* ... */
|
|
}
|
|
enum_func_status MYSQLND_METHOD(mysqlnd_plugin_conn, connect)(/* ... */) {
|
|
/* ... */
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
<emphasis role="bold">Tarea de análisis: desde C hacia el espacio de usuario</emphasis>
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
class proxy extends mysqlnd_plugin_connection {
|
|
public function connect($host, ...) { .. }
|
|
}
|
|
mysqlnd_plugin_set_conn_proxy(new proxy());
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
Proceso:
|
|
</simpara>
|
|
<orderedlist>
|
|
<listitem>
|
|
<simpara>
|
|
PHP: el usuario registra una función de devolución de llamada para el plugin
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
PHP: el usuario llama a un método de la API PHP MySQL para conectarse a MySQL
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
C: ext/*mysql* llama al método mysqlnd
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
C: mysqlnd termina en ext/mysqlnd_plugin
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
C: ext/mysqlnd_plugin
|
|
<orderedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Llamada a la función de devolución de llamada del espacio de usuario
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
O la función original <literal>mysqlnd</literal>, si el espacio
|
|
de usuario no ha definido una función de devolución de llamada
|
|
</simpara>
|
|
</listitem>
|
|
</orderedlist>
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
<simpara>
|
|
Debe realizar las siguientes operaciones:
|
|
</simpara>
|
|
<orderedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Escribir una clase "mysqlnd_plugin_connection" en C
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Aceptar y registrar el objeto proxy a través de
|
|
"mysqlnd_plugin_set_conn_proxy()"
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Llamar a los métodos de proxy del espacio de usuario
|
|
desde C (optimización - zend_interfaces.h)
|
|
</simpara>
|
|
</listitem>
|
|
</orderedlist>
|
|
<simpara>
|
|
Los métodos del objeto del espacio de usuario pueden ser llamados
|
|
utilizando <literal>call_user_function()</literal>,
|
|
o puede operar a un nivel por debajo del motor Zend y
|
|
utilizar <literal>zend_call_method()</literal>.
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">Optimización: llamada a métodos desde C utilizando
|
|
zend_call_method</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
El código siguiente muestra un prototipo para la función
|
|
<literal>zend_call_method</literal>, obtenido de
|
|
<filename>zend_interfaces.h</filename>.
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
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
|
|
);
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
La API Zend soporta 2 argumentos. Puede necesitar más, por ejemplo:
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
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
|
|
);
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
Para solucionar este problema, deberá hacer una copia
|
|
de <literal>zend_call_method()</literal> y añadir funcionalidad para añadir parámetros.
|
|
Puede lograr esto creando un conjunto de macros
|
|
<literal>MY_ZEND_CALL_METHOD_WRAPPER</literal>.
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">Llamada al espacio de usuario PHP</emphasis>
|
|
</simpara>
|
|
<simpara>
|
|
El código siguiente muestra el método optimizado para realizar
|
|
una llamada a una función del espacio de usuario desde C:
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[ /* 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) {
|
|
/* llamada al proxy del espacio de usuario */
|
|
ret = MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, host, /*...*/);
|
|
} else {
|
|
/* o el método original mysqlnd = no hacer nada, ser transparente */
|
|
ret = org_methods.connect(conn, host, user, passwd,
|
|
passwd_len, db, db_len, port,
|
|
socket, mysql_flags TSRMLS_CC);
|
|
}
|
|
return ret;
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
<emphasis role="bold">Llamada al espacio de usuario: argumentos simples</emphasis>
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* 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(&zv_host);
|
|
/* ... */
|
|
}
|
|
/* ... */
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
<emphasis role="bold">Llamada al espacio de usuario: estructuras como argumentos</emphasis>
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* 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(&zv_conn);
|
|
/* ... */
|
|
}
|
|
/* ... */
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
El primer argumento de todas las funciones <literal>mysqlnd</literal>
|
|
es un objeto C. Por ejemplo, el primer argumento de la función
|
|
connect() es un puntero hacia <literal>MYSQLND</literal>.
|
|
La estructura MYSQLND representa un objeto de conexión
|
|
<literal>mysqlnd</literal>.
|
|
</simpara>
|
|
<simpara>
|
|
El puntero del objeto de conexión <literal>mysqlnd</literal>
|
|
puede ser comparado a un puntero de archivo estándar I/O.
|
|
Al igual que un puntero de archivo estándar I/O, un objeto de
|
|
conexión <literal>mysqlnd</literal> debe ser vinculado al espacio
|
|
de usuario utilizando una variable PHP de tipo recurso.
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">Desde C hacia el espacio de usuario, y luego de vuelta</emphasis>
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
class proxy extends mysqlnd_plugin_connection {
|
|
public function connect($conn, $host, ...) {
|
|
/* "pre" hook */
|
|
printf("Conexión al host = '%s'\n", $host);
|
|
debug_print_backtrace();
|
|
return parent::connect($conn);
|
|
}
|
|
|
|
public function query($conn, $query) {
|
|
/* "post" hook */
|
|
$ret = parent::query($conn, $query);
|
|
printf("Consulta = '%s'\n", $query);
|
|
return $ret;
|
|
}
|
|
}
|
|
mysqlnd_plugin_set_conn_proxy(new proxy());
|
|
]]>
|
|
</programlisting>
|
|
<simpara>
|
|
Los usuarios PHP deben poder llamar a la implementación
|
|
del padre de un método sobrescrito.
|
|
</simpara>
|
|
<simpara>
|
|
Como resultado de una subclase, es posible
|
|
redefinir únicamente los métodos seleccionados, y puede elegir tener
|
|
acciones "pre" o "post".
|
|
</simpara>
|
|
<simpara>
|
|
<emphasis role="bold">Construcción de una clase: mysqlnd_plugin_connection::connect()</emphasis>
|
|
</simpara>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* my_mysqlnd_plugin_classes.c */
|
|
|
|
PHP_METHOD("mysqlnd_plugin_connection", connect) {
|
|
/* ... simplificado! ... */
|
|
zval* mysqlnd_rsrc;
|
|
MYSQLND* conn;
|
|
char* host; int host_len;
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",
|
|
&mysqlnd_rsrc, &host, &host_len) == FAILURE) {
|
|
RETURN_NULL();
|
|
}
|
|
ZEND_FETCH_RESOURCE(conn, MYSQLND* conn, &mysqlnd_rsrc, -1,
|
|
"Mysqlnd Connection", le_mysqlnd_plugin_conn);
|
|
if (PASS == org_methods.connect(conn, host, /* simplificado! */ TSRMLS_CC))
|
|
RETVAL_TRUE;
|
|
else
|
|
RETVAL_FALSE;
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</section>
|
|
</chapter>
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|