Esta sección proporciona información general de la arquitectura de los
complementos de mysqlnd
.
Información general del Controlador Nativo de MySQL
Antes de desarrollar complementos de mysqlnd
, es útil
conocer un poco cómo mysqlnd
está organizado.
Mysqlnd
consiste en los siguientes módulos:
Módulos de estadísticas | mysqlnd_statistics.c |
Conexión | mysqlnd.c |
Conjunto de resultados | mysqlnd_result.c |
Metadatos de conjuntos de resultados | mysqlnd_result_meta.c |
Sentencia | mysqlnd_ps.c |
Red | mysqlnd_net.c |
Protocolo de cable | mysqlnd_wireprotocol.c |
Paradigma de la orientación a objetos de C
A nivel de código, mysqlnd
utiliza un patrón de C para
implementar la orientación a objetos.
En C se utiliza una estrucutra (struct
) para representar un objeto.
Los miembros de la estructura representan las propiedades del objeto. Los miembros de la estrucutra
que apuntan a funciones representan los métodos.
A diferencia de otros lenguajes como C++ o Java, no existen reglas fijas sobre herencia en el paradigma de la orientación a objetos de C. Sin embargo, existen algunas convenciones que se han de seguir y serán discutidas más tarde.
El ciclo de vida de PHP
Al considerar el ciclo de vida de PHP existen dos ciclos básicos:
El ciclo de inicio y cierre del motor de PHP
El ciclo de peticiones
Cuando se inicia el motor de PHP, éste llamará a la función de inicialización de módulos (MINIT) para cada extensión registrada. Esto permite a cada módulo establecer variables y asignar recursos que existirán durante el tiempo de vida del proceso del motor de PHP. Al cerrar el motor de PHP, éste llamará a las funciones de cierre de módulos (MSHUTDOWN) para cada extensión.
Durante el tiempo de vida del motor de PHP, éste recibirá varias peticiones. Cada petición constituye otro ciclo de vida. En cada petición, el motor de PHP llamará a la función de inicialización de peticiones para cada extensión. La extensión puede realizar cualquier establecimiento de variables y asignación de recursos necesarios para el proceso de petición. Al finalizar el ciclo de peticiones, el motor llama a la función de cierre de peticiones (RSHUTDOWN) de cada extensión, por lo que la extensión puede realizar cualquier limpieza necesaria.
Cómo funciona un complemento
Un complemento de mysqlnd
funciona interceptando llamada realizadas
a mysqlnd
por extensiones que utilizan
mysqlnd
. Esto se lleva a cabo obteniendo la
tabla de funciones de mysqlnd
, haciendo una copia de seguridad de ella, y
reemplazándola por una tabla de funciones personalizada, la cual llamará a las funciones del
complemento cuando sea necesario.
El siguiente código muestra cómo se reemplaza la tabla de
funciones de mysqlnd
:
/* un lugar para almacenar la tabla de funciones original */ struct st_mysqlnd_conn_methods org_methods; void minit_register_hooks(TSRMLS_D) { /* activar la tabla de funciones */ struct st_mysqlnd_conn_methods * current_methods = mysqlnd_conn_get_methods(); /* copiar la tabla de funciones original */ memcpy(&org_methods, current_methods, sizeof(struct st_mysqlnd_conn_methods); /* instalar nuevos métodos */ current_methods->query = MYSQLND_METHOD(my_conn_class, query); }
La manipulación de la tabla de funciones de conexión debe hacerse durante la Inicialización de Módulos (MINIT). La tabla de funciones es un recurso global compartido. En un entorno multihilo, con un TSRM construido, la manipulación de un recurso global compartido durante el proceso de peticiones resultará en conflictos casi con toda seguridad.
Nota:
No utilice cualquier lógica de tamaña fijo al manipular la tablas de funciones de
mysqlnd
: los métodos nuevos se pueden añadir al final de la tabla de funciones. La tabla de funciones puede cambiar en cualquier momento en el futuro.
Llamar a métodos padre
Si las entradas de la tabla de funciones original está copiada, aún es posible llamar a las entradas de la tabla de funciones original - los métodos padre.
En algunos casos, como en
Connection::stmt_init()
, es vital llamar al
método padre antes de realizar cualquier otra actividad en el método derivado.
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 */ }
Extender propiedades
Un objeto mysqlnd
está representado por una estrucutra de C.
No es posible añadir un miembro a una estructura de C en tiempo de ejecución. Los usuarios de
objetos mysqlnd
no pueden simplemente añadir propiedades a
los objetos.
Se pueden añadir datos arbitrarios (propiedades) a
objetos mysqlnd
usando la función apropiada
de la familia de
mysqlnd_plugin_get_plugin_<object>_data()
.
Cuando se asigna un objeto, mysqlnd
reserva
espacio al final del objeto para que contenga un puntero
void *
a datos arbitrarios. mysqlnd
reserva espacio
para un puntero void *
por complemento.
La siguiente tabla muestra cómo calcular la posición del puntero para un complemento específico:
Dirección de memoria | Contenido |
0 | Inicio de la estructura de C del objeto mysqlnd |
n | Fin de la estructura de C del objeto mysqlnd |
n + (m x sizeof(void*)) | void* a los datos del objeot del complemento m-ésimo |
Si se planea usar cualquier constructor de objetos mysqlnd
en subclases, lo cual está permitido, ¡se debe tener esto en cuenta!
El siguiente código muestra la extensión de propiedades:
/* cualquier dato que queramos asociar */ typedef struct my_conn_properties { unsigned long query_counter; } MY_CONN_PROPERTIES; /* id del complemento */ unsigned int my_plugin_id; void minit_register_hooks(TSRMLS_D) { /* obtener un ID único para el complemento */ my_plugin_id = mysqlnd_plugin_register(); /* recorte - véase Extender conexiones: 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; }
El desarrollador del complemento es responsable de la gestión de memoria de lo datos del complemento.
Se recomienda el uso del asignador de memoria de mysqlnd
para los datos del complemento. Estas funciones son nombradas usando la convención:
mnd_*loc()
. El asignador de mysqlnd
tiene algunas características útiles, como la capacidad de usar un
asignador de depuración en una construcción de no depuración.
¿Cuándo usar subclases? | ¿Cada instancia tiene su propia tabla de funciones privada? | ¿Cómo usar subclases? | |
Conexión (MYSQLND) | MINIT | No | mysqlnd_conn_get_methods() |
Conjunto de resultados (MYSQLND_RES) | MINIT o después | Sí | mysqlnd_result_get_methods() o manipulación de la tabla de funciones de métodos de objetos |
Metadatos de conjunto de resultados (MYSQLND_RES_METADATA) | MINIT | No | mysqlnd_result_metadata_get_methods() |
Sentencia (MYSQLND_STMT) | MINIT | No | mysqlnd_stmt_get_methods() |
Red (MYSQLND_NET) | MINIT o después | Sí | mysqlnd_net_get_methods() o manipulación de la tabla de funciones de métodos de objetos |
Protocolo de cable (MYSQLND_PROTOCOL) | MINIT o después | Sí | mysqlnd_protocol_get_methods() o manipulación de la tabla de funciones de métodos de objetos |
No se deben manipular las tablas de funciones en ningún momento posterior a MINIT si no está permitido según la tabla de arriba.
Algunas clases contienen un puntero a la tabla de funciones de métodos. Todas las instancias de esas clases compartirán la misma tabla de funciones. Para evitar el caos, en particular en entornos de hilos, tales tablas de funciones deben ser manipuladas solamente durante MINIT.
Otras clases usan copias de una tabla de funciones compartida globalmente. La copia de la tabla de funciones de la clase se crea junto con el objeto. Cada objeto usa su propia tabla de funciones. Esto proporciona dos opciones: se puede manipular la tabla de funciones predetermiada de un objeto durante MINIT, y además se pueden perfeccionar métodos de un objeto sin impactar otras instancias de la misma clase.
La ventaja del enfoque de la tabla de funciones compartida es el rendimiento. No hay necesidad de copiar una tabla de funciones para cada objeto.
Asignación, construcción, reinicio | ¿Se puede modificar? | Llamador | |
Conexión (MYSQLND) | mysqlnd_init() | No | mysqlnd_connect() |
Conjunto de resultados (MYSQLND_RES) | Asignación:
Reinicio y reinicialización durante:
|
Sí, ¡pero se ha de llamar al padre! |
|
Metadatos de conjunto de resultados (MYSQLND_RES_METADATA) | Connection::result_meta_init() | Sí, ¡pero se ha de llamar al padre! | Result::read_result_metadata() |
Sentecia (MYSQLND_STMT) | Connection::stmt_init() | Sí, ¡pero se ha de llamar al padre! | Connection::stmt_init() |
Red (MYSQLND_NET) | mysqlnd_net_init() | No | Connection::init() |
Protocolo de cable (MYSQLND_PROTOCOL) | mysqlnd_protocol_init() | No | Connection::init() |
Se recomienda encarecidamente que no se reemplace completamente un
constructor. Los constructores realizan asignaciones de memoria. Las asignaciones
de memoria son vitales para la API de complementos de mysqlnd
y la lógida de objetos de mysqlnd
. Si no se tiene
cuidado con las advertencias y se insiste en enganchar los constructores, se
debería, al menos, llamar al constructor padre antes de hacer nada en el
constructor derivado.
A pesar de todas las advertencias, puede ser útil usar subclases para los constructores. Éstos son el lugar perfecto para modificar las tablas de funciones de objetos con tablas de objetos no compartidas, como Conjunto de resultados, Red, Protocolo de cable.
¿Debe el método derivado llamar al padre? | Destructor | |
Conexión | sí, después de la ejecución del método | free_contents(), end_psession() |
Conjunto de resultados | sí, después de la ejecución del método | free_result() |
Metadatos del conjunto de resultados | sí, después de la ejecución del método | free() |
Sentencia | sí, después de la ejecución del método | dtor(), free_stmt_content() |
Red | sí, después de la ejecución del método | free() |
Protocolo de cable | sí, después de la ejecución del método | free() |
Los destructores son los lugares apropiados para liberar propiedades
mysqlnd_plugin_get_plugin_<objeto>_data()
.
Los destructores listados podrían no ser equivalentes a los métodos reales de
mysqlnd
que liberan el objeto en sí. Sin embargo,
son el mejor lugar posible para enganchar y liberar los datos
de los complementos. Como sucede con los constructores se pueden reemplazar los
métodos completamente, pero no se recomienda. Si se listan múltiles métodos
en la tabla de arriba, se necesitará enganchar todos esos métodos
y liberar los datos del complemento en el método correspondiente que sea llamado primero por
mysqlnd
.
El método recomendado para los complementos es simplemente enganchar los métodos, liberar la memoria y llamar a la implementación padre inmediatamente después de esto.
Debido a un error en las versiones de PHP 5.3.0 hasta 5.3.3, los complementos no
asocian los datos de los complementos con una conexión persistente. Esto es debido a que
ext/mysql
y ext/mysqli
no desencadenan las llamadas necesarias al método end_psession()
de mysqlnd
, y el complemento podría,
por lo tanto, perder memoria. Esto ha sido corregido en PHP 5.3.4.