Architecture du plugin du driver natif

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 :

Schéma de l'organisation mysqlnd, par module
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, on utilise 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ée, 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'utiliser 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 :

Calcul des pointeurs pour mysqlnd
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 l'on prévoit de faire des sous-classes des constructeurs des objets mysqlnd, ce qui est autorisé, il faut 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 et comment faire une sous-classe
  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

Il ne faut pas manipuler les tables de fonction aprÚs MINIT si ce n'est pas autorisé suivant la table ci-dessus.

Quelques classes contiennent un pointeur 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 donne 2 options : il est possible de manipuler la table de fonction par dĂ©faut d'un objet au moment du MINIT, et il est Ă©galement possible d'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.

Statut du constructeur
Type Allocation, construction, rĂ©initialisation Peut-ĂȘtre modifiĂ© ? Appelant
Connexion (MYSQLND) mysqlnd_init() Non mysqlnd_connect()
Jeu de résultats(MYSQLND_RES) Allocation :
  • Connection::result_init()
Reset et réinitialisation lors de :
  • Result::use_result()
  • Result::store_result
Oui, mais appel du parent !
  • Connection::list_fields()
  • Statement::get_result()
  • Statement::prepare() (MĂ©ta-donnĂ©es uniquement)
  • Statement::resultMetaData()
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 l'on ne se soucie pas des alertes et que l'on insiste pour remplacer les constructeurs, il est recommandé d'au moins appeler le constructeur parent avant de faire quoi que ce soit dans le 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.

Statut du destructeur
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 libĂ©rer les donnĂ©es du plugin. Tout comme les constructeurs, il est possible de remplacer les mĂ©thodes entiĂšres mais ce n'est pas recommandĂ©. Si plusieurs mĂ©thodes sont listĂ©es dans la table ci-dessus, il faut modifier toutes les mĂ©thodes listĂ©es et libĂ©rer les donnĂ©es du 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 la mémoire et appeler l'implémentation du parent immédiatement aprÚs.

add a note

User Contributed Notes

There are no user contributed notes for this page.