1
0
mirror of https://github.com/php/doc-es.git synced 2026-03-24 07:22:16 +01:00
Files
archived-doc-es/reference/mysqlnd/plugin.xml
2026-01-19 03:26:36 +00:00

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_&lt;object&gt;_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></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></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></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>&lt;object&gt;</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
-->