Cette section fournit un aperçu de l'architecture du plugin
mysqlnd
.
Aperçu du driver natif MySQL
Avant de développer des plugins mysqlnd
,
il est utile d'avoir une connaissance minimale sur l'organisation
de mysqlnd
. Mysqlnd
est composé
des modules suivants :
Modules de statistiques | mysqlnd_statistics.c |
---|---|
Connexion | mysqlnd.c |
Jeu de résultats | mysqlnd_result.c |
Données méta du jeu de résultats | mysqlnd_result_meta.c |
Requête | mysqlnd_ps.c |
Réseau | mysqlnd_net.c |
Couche physique | mysqlnd_wireprotocol.c |
Objet C orienté paradigme
Au niveau du code, mysqlnd
utilise un masque C
pour implémenter l'orientation de l'objet.
En C, vous utilisez une structure (struct
)
pour représenter un objet. Les membres de cette structure
représentent les propriétés de l'objet. Les membres de la
structure pointant vers des fonctions représentent les méthodes.
Contrairement aux autres langages comme C++ ou Java, il n'y a pas de règles fixes sur l'héritage dans les objets C orientés paradigme. Cependant, il y a quelques conventions qui doivent être suivies qui seront abordées ultérieurement.
Le cycle de vie PHP
Le cycle de vie de PHP comporte 2 cycles basiques :
Le cycle de démarrage et d'arrêt du moteur PHP
Le cycle d'une demande
Lorsque le moteur PHP démarre, il appelle la fonction d'initialisation du module (MINIT) de chaque extension enregistrée. Ceci permet à chaque module de définir les variables et d'allouer les ressources qui doivent exister pour la durée de vie du processus correspondant au moteur PHP. Lorsque le moteur PHP s'arrête, il appelle la fonction d'arrêt du module (MSHUTDOWN) pour chaque extension.
Pendant la durée de vie du moteur PHP, il recevra des demandes. Chaque demande constitue un autre cycle de vie. Pour chaque requête, le moteur PHP appellera la fonction d'initialisation de chaque extension. L'extension peut effectuer toutes les définitions de variables ainsi que les allocations de ressources nécessaires pour traiter la demande. Lorsque le cycle de la demande se termine, le moteur appelle la fonction d'arrêt (RSHUTDOWN) pour chaque extension, ainsi, l'extension peut lancer tout le nettoyage nécessaire.
Comment fonctionne un plugin
Un plugin mysqlnd
fonctionne en interceptant les appels
effectués à mysqlnd
par les extensions qui utilisent
mysqlnd
. Ceci est possible en obtenant la table
de fonction mysqlnd
, en la sauvegardant, et en la
remplaçant par une table de fonction personnalisé, qui appelle les fonctions
du plugin.
Le code suivant montre la façon dont la table de fonction
mysqlnd
est remplacée :
/* un endroit pour stocker la table de fonction originale */ struct st_mysqlnd_conn_methods org_methods; void minit_register_hooks(TSRMLS_D) { /* table de fonction active */ struct st_mysqlnd_conn_methods * current_methods = mysqlnd_conn_get_methods(); /* sauvegarde de la table de fonction originale */ memcpy(&org_methods, current_methods, sizeof(struct st_mysqlnd_conn_methods); /* installation des nouvelles méthodes */ current_methods->query = MYSQLND_METHOD(my_conn_class, query); }
Les manipulations de la table de fonction de connexion doivent être effectuées lors de l'initialisation du module (MINIT). La table de fonction est une ressource globale partagée. Dans un environnement multithread, avec une compilation TSRM, la manipulation d'une ressource globale partagée lors d'un processus de demande entraînera la plupart du temps des conflits.
Note:
N'utilisez aucune logique de taille fixe lors de la manipulation de la table de fonction
mysqlnd
: les nouvelles méthodes peuvent être ajoutées à la fin de la table de fonction. La table de fonction peut être modifiée à tout moment par la suite.
Appel des méthodes parents
Si la table de fonction originale est sauvegardée, il est toujours possible d'appeler les entrées de la table de fonction originale - les méthodes parents.
Dans ce cas, tout comme pour Connection::stmt_init()
,
il est vital d'appeler la méthode parent avant toute autre activité
dans la méthode dérivée.
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); /* retour avec appel du parent */ }
Étendre des propriétés
Un objet mysqlnd
est représenté par une structure C.
Il n'est pas possible d'ajouter un membre à une structure C au moment
de l'exécution. Les utilisateurs d'objets mysqlnd
ne peuvent pas ajouter simplement des propriétés aux objets.
Les données arbitraires (propriétés) peuvent être ajoutées aux objets
mysqlnd
en utilisant une fonction appropriée de la
famille mysqlnd_plugin_get_plugin_<object>_data()
.
Lors de l'allocation d'un objet, mysqlnd
réserve
un espace à la fin de l'objet pour accueillir un pointeur
void *
vers des données arbitraires.
mysqlnd
réserve un espace pour un pointeur
void *
par plugin.
La table suivante montre comment calculer la position d'un pointeur pour un plugin spécifique :
Adresse mémoire | Contenus |
---|---|
0 | Début de la structure C de l'objet mysqlnd |
n | Fin de la structure C de l'objet mysqlnd |
n + (m x sizeof(void*)) | void* vers les données de l'objet du m-ème plugin |
Si vous prévoyez de faire des sous-classes des constructeurs
des objets mysqlnd
, ce qui est autorisé,
vous devez conserver ceci en mémoire !
Le code suivant montre la façon dont on étend des propriétés :
/* toutes les données que nous voulons associer */ typedef struct my_conn_properties { unsigned long query_counter; } MY_CONN_PROPERTIES; /* id du plugin */ unsigned int my_plugin_id; void minit_register_hooks(TSRMLS_D) { /* on obtient un ID unique pour le plugin */ my_plugin_id = mysqlnd_plugin_register(); /* snip - voir l'extension de la connexion : méthodes */ } 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; }
Le développeur du plugin est responsable de la gestion de la mémoire associée aux données du plugin.
L'utilisation de l'allocateur de mémoire mysqlnd
est recommandée pour les données du plugin. Ces fonctions sont nommées
en utilisant la convention suivante : mnd_*loc()
.
L'allocateur mysqlnd
a quelques fonctionnalités bien utiles,
comme la possibilité d'utiliser un allocateur de débogage dans une compilation
non-débogue.
Quand faire une sous-classe ? | Est-ce que chaque instance a sa table de fonction privée ? | Comment faire une sous-classe ? | |
---|---|---|---|
Connexion (MYSQLND) | MINIT | Non | mysqlnd_conn_get_methods() |
Jeu de résultats (MYSQLND_RES) | MINIT ou après | Oui | mysqlnd_result_get_methods() ou méthode de l'objet de manipulation de la table de fonction |
Méta du jeu de résultats (MYSQLND_RES_METADATA) | MINIT | Non | mysqlnd_result_metadata_get_methods() |
Requête (MYSQLND_STMT) | MINIT | Non | mysqlnd_stmt_get_methods() |
Réseau (MYSQLND_NET) | MINIT ou après | Oui | mysqlnd_net_get_methods() ou méthode de l'objet de manipulation de la table de fonction |
Couche physique (MYSQLND_PROTOCOL) | MINIT ou après | Oui | mysqlnd_protocol_get_methods() ou méthode de l'objet de manipulation de la table de fonction |
Vous ne devez pas manipuler les tables de fonction après MINIT si ce n'est pas autorisé suivant la table ci-dessus.
Quelques classes contiennent un point vers une méthode de la table de fonction. Toutes les instances d'une telle classe partageront la même table de fonction. Pour éviter le chaos, en particulier dans les environnements threadés, ce genre de tables de fonction ne doit être manipulé que lors du MINIT.
Les autres classes utilisent une copie de la table de fonction globale partagée. Cette copie est créée en même temps que l'objet. Chaque objet utilise sa propre table de fonction. Ceci vous donne 2 options : vous pouvez manipuler la table de fonction par défaut d'un objet au moment du MINIT, et vous pouvez aussi affiner des méthodes d'un objet sans impacter les autres instances de la même classe.
L'avantage de l'approche avec une table de fonction partagée est la performance. Il n'est pas nécessaire de copier une table de fonction pour chaque objet.
Type | Allocation, construction, réinitialisation | Peut-être modifié ? | Appelant |
---|---|---|---|
Connexion (MYSQLND) | mysqlnd_init() | Non | mysqlnd_connect() |
Jeu de résultats(MYSQLND_RES) | Allocation :
Reset et ré-initialisation lors de :
|
Oui, mais appel du parent ! |
|
Méta du jeu de résultats (MYSQLND_RES_METADATA) | Connection::result_meta_init() | Oui, mais appel du parent ! | Result::read_result_metadata() |
Statement (MYSQLND_STMT) | Connection::stmt_init() | Oui, mais appel du parent ! | Connection::stmt_init() |
Réseau (MYSQLND_NET) | mysqlnd_net_init() | Non | Connection::init() |
Couche physique (MYSQLND_PROTOCOL) | mysqlnd_protocol_init() | Non | Connection::init() |
Il est vivement recommandé de ne pas remplacer entièrement un constructeur.
Les constructeurs effectuent les allocations mémoires. Les allocations
mémoires sont vitales pour l'API du plugin mysqlnd
ainsi que pour la logique de l'objet mysqlnd
. Si
vous ne vous souciez pas des alertes et que vous insistez pour
remplacer les constructeurs, vous devriez au moins appeler
le constructeur parent avant de faire quoi que ce soit dans votre
constructeur.
Au niveau de toutes les alertes, il peut être utile de faire des sous-classes des constructeurs. Les constructeurs sont les endroits parfaits pour modifier les tables de fonction des objets avec les tables d'objets non partagés, comme les jeux de résultats, le réseau ou encore la couche physique.
Type | La méthode dérivée doit appeler le parent ? | Destructeur |
---|---|---|
Connexion | oui, après l'exécution de la méthode | free_contents(), end_psession() |
Jeu de résultats | oui, après l'exécution de la méthode | free_result() |
Méta du jeu de résultats | oui, après l'exécution de la méthode | free() |
Requête | oui, après l'exécution de la méthode | dtor(), free_stmt_content() |
Réseau | oui, après l'exécution de la méthode | free() |
Couche physique | oui, après l'exécution de la méthode | free() |
Les destructeurs sont les endroits parfaits pour libérer les propriétés,
mysqlnd_plugin_get_plugin_<object>_data()
.
Les destructeurs listés peuvent ne pas être les équivalents aux
méthodes actuelles mysqlnd
libérant l'objet lui-même.
Cependant, ils sont les meilleurs endroits pour vous pour libérer
les données de votre plugin. Tout comme les constructeurs, vous
pouvez remplacer les méthodes entières mais ce n'est pas recommandé.
Si plusieurs méthodes sont listées dans la table ci-dessus, vous devez
modifier toutes les méthodes listées et libérer les données de votre
plugin dans la méthode appelée en premier par mysqlnd
.
La méthode recommandée pour les plugins est de modifier simplement les méthodes, libérer votre mémoire et appeler l'implémentation du parent immédiatement après.