mirror of
https://github.com/macintoshplus/doc-fr.git
synced 2026-03-24 08:52:09 +01:00
1465 lines
47 KiB
XML
1465 lines
47 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- $Revision$ -->
|
|
<!-- EN-Revision: e50e79746736dbdfbabe9bd3566793b3ddf38f58 Maintainer: yannick Status: ready -->
|
|
<!-- Reviewed: no -->
|
|
<chapter xml:id="mysqlnd.plugin" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
<title>API du plugin du driver natif MySQL</title>
|
|
<para>
|
|
L'API du plugin du driver natif MySQL est une fonctionnalité
|
|
du driver natif MySQL, ou <literal>mysqlnd</literal>.
|
|
Le plugin <literal>Mysqlnd</literal> opère sur la couche entre
|
|
les applications PHP et le serveur MySQL. Il est comparable
|
|
à un proxy MySQL. Un proxy MySQL opère sur une couche entre toutes
|
|
les applications clientes MySQL, par exemple, une application PHP
|
|
et un serveur MySQL. Le plugin <literal>Mysqlnd</literal>
|
|
peut entreprendre des tâches typiques de proxy MySQL comme
|
|
l'équilibrage de charge, ainsi que le suivi et l'optimisation
|
|
des performances. En raison d'une architecture et d'une localisation
|
|
différente, le plugin <literal>mysqlnd</literal> n'a pas tous les
|
|
inconvénients d'un proxy MySQL. Par exemple, avec le plugin, il
|
|
n'y a pas qu'un seul point d'échec, pas de serveur de proxy dédié
|
|
à déployer, et pas de nouveau langage à apprendre (Lua).
|
|
</para>
|
|
<para>
|
|
Un plugin <literal>mysqlnd</literal> peut être exécuté comme une extension
|
|
à <literal>mysqlnd</literal>. Un plugin peut intercepter la majorité des
|
|
fonctions <literal>mysqlnd</literal>. Les fonctions <literal>mysqlnd</literal>
|
|
sont appelées par l'extension PHP MySQL comme
|
|
<literal>ext/mysql</literal>, <literal>ext/mysqli</literal>, et
|
|
<literal>PDO_MYSQL</literal>. Comme résultat, il est possible pour un
|
|
plugin <literal>mysqlnd</literal> d'intercepter tous les appels effectués
|
|
par ces extensions depuis une application cliente.
|
|
</para>
|
|
<para>
|
|
Les appels aux fonctions internes <literal>mysqlnd</literal> peuvent
|
|
également être interceptés ou remplacés. Il n'y a aucune restriction
|
|
sur la manipulation des tables de fonctions internes <literal>mysqlnd</literal>.
|
|
Il est possible de définir des actions pour faire que lorsque
|
|
certaines fonctions <literal>mysqlnd</literal> sont appelées
|
|
par l'extension qui utilise <literal>mysqlnd</literal>, l'appel
|
|
est redirigé vers la fonction appropriée du plugin
|
|
<literal>mysqlnd</literal>. La possibilité de manipuler les tables
|
|
de fonctions internes <literal>mysqlnd</literal> dans ce sens permet
|
|
un maximum de flexibilité.
|
|
</para>
|
|
<para>
|
|
Le plugin <literal>Mysqlnd</literal> est en faite, une extension PHP,
|
|
écrit en C, qui utilise l'API du plugin <literal>mysqlnd</literal>
|
|
(qui est compilé dans le driver natif MySQL, <literal>mysqlnd</literal>).
|
|
Le plugin peut être à 100% transparent pour les applications PHP. Aucune
|
|
modification aux applications n'est nécessaire car le plugin opère
|
|
sur une couche différente. Le plugin <literal>mysqlnd</literal>
|
|
peut être utilisé dans une couche en dessous de <literal>mysqlnd</literal>.
|
|
</para>
|
|
<para>
|
|
La liste suivante représente quelques applications possibles
|
|
du plugin <literal>mysqlnd</literal>.
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
L'équilibrage de charge.
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Séparation des lectures et des écritures. Un exemple de cette fonctionnalité
|
|
est l'extension PECL/mysqlnd_ms (Maître/esclave). Cette extension sépare
|
|
les requêtes de lecture et d'écriture pour une configuration de réplication.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Basculement
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Round-Robin, le moins chargé
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Surveillance
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Journalisation des requêtes
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Analyse de requêtes
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Audite de requêtes. Un exemple de ceci est l'extension
|
|
PECL/mysqlnd_sip (SQL Injection Protection). Cette extension
|
|
inspecte les requêtes et exécute uniquement celles qui sont
|
|
autorisées suivants des ensembles de règles.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Performance
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
La mise en cache. Un exemple de ceci est l'extension
|
|
PECL/mysqlnd_qc (Query Cache).
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Étranglement
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Fragmentation. Un exemple de ceci est l'extension
|
|
PECL/mysqlnd_mc (Multi Connect). Cette extension tente
|
|
de séparer une requête SELECT en n parties, en utilisant
|
|
des requêtes du type SELECT ... LIMIT part_1, SELECT LIMIT part_n.
|
|
L'extension envoie les requêtes à des serveurs MySQL distincts
|
|
et fusionne ensuite le résultat à destination du client.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
<emphasis role="bold">PLugins du driver natif MySQL disponibles</emphasis>
|
|
</para>
|
|
<para>
|
|
Il y a déjà plusieurs plugins mysqlnd de disponible.
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
<emphasis role="bold">PECL/mysqlnd_mc</emphasis> -Plugin Multi Connexion.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<emphasis role="bold">PECL/mysqlnd_ms</emphasis> - Plugin Maître Esclave.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<emphasis role="bold">PECL/mysqlnd_qc</emphasis> - Plugin de mise en cache de requêtes.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<emphasis role="bold">PECL/mysqlnd_pscache</emphasis> - Plugin de mise en cache de gestionnaire
|
|
de requêtes préparées.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<emphasis role="bold">PECL/mysqlnd_sip</emphasis> - Plugin permettant la protection
|
|
contre les injections SQL.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<emphasis role="bold">PECL/mysqlnd_uh</emphasis> - Plugin de gestionnaire d'utilisateurs.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<section xml:id="mysqlnd.plugin.mysql-proxy">
|
|
<title>Comparaison des plugins mysqlnd avec proxy MySQL</title>
|
|
<para>
|
|
Les plugins <literal>Mysqlnd</literal> et le proxy MySQL sont
|
|
des technologies différentes utilisant différentes approches.
|
|
Les deux sont des outils valides pour résoudre bon nombre
|
|
de tâches classiques, comme l'équilibrage de charge, la surveillance,
|
|
et l'amélioration des performances. Une importante différence est
|
|
que le proxy MySQL fonctionne avec tous les clients MySQL alors que
|
|
les plugins <literal>mysqlnd</literal> sont spécifiques aux
|
|
applications PHP.
|
|
</para>
|
|
<para>
|
|
Comme une extension PHP, un plugin <literal>mysqlnd</literal>
|
|
doit être installé sur le serveur d'applications PHP, en plus du
|
|
reste de PHP. Un proxy MySQL peut soit fonctionner sur le serveur
|
|
d'applications PHP, soit être installé sur une machine dédiée pour
|
|
gérer plusieurs serveurs d'applications PHP.
|
|
</para>
|
|
<para>
|
|
Le déploiement d'un proxy MySQL sur un serveur d'applications
|
|
a 2 avantages :
|
|
</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
Pas un seul point d'échec
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Facile à redimensionner (redimensionnement horizontal,
|
|
redimensionnement par le client)
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
<para>
|
|
Un proxy MySQL (et les plugins <literal>mysqlnd</literal>) peut
|
|
résoudre des problèmes facilement, qui sinon, auraient dû nécessiter
|
|
des modifications aux applications existantes.
|
|
</para>
|
|
<para>
|
|
Cependant, un proxy MySQL a quelques désavantages :
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Un proxy MySQL est un nouveau composant, une nouvelle technologie à
|
|
appliquer au maître et à déployer.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Un proxy MySQL nécessite la connaissance du langage de script Lua.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
Un proxy MySQL peut être personnalisé en utilisant les langages de
|
|
programmation C et Lua. Lua est le langage préféré pour un proxy MySQL.
|
|
Pour la plupart des experts PHP, Lua est un nouveau langage à apprendre.
|
|
Un plugin <literal>mysqlnd</literal> peut être écrit en C. Il est également
|
|
possible d'écrire un plugin en PHP en utilisant
|
|
<link xlink:href="http://pecl.php.net/package/mysqlnd_uh">PECL/mysqlnd_uh</link>.
|
|
</para>
|
|
<para>
|
|
Un proxy MySQL fonctionne comme un démon - un processus en arrière-plan.
|
|
Un proxy MySQL peut rappeler des décisions prises antérieurement,
|
|
vu que tous les états peuvent être conservés. Cependant, un plugin
|
|
<literal>mysqlnd</literal> est lié au cycle de vie d'une requête PHP.
|
|
Un proxy MySQL peut également partager des résultats calculés une seule fois
|
|
sur plusieurs serveurs d'applications. Un plugin <literal>mysqlnd</literal>
|
|
peut donc avoir besoin de stocker des données dans un médium persistant.
|
|
Un autre démon peut être utilisé dans ce but, comme par exemple,
|
|
Memcache. Ce mécanisme donne un avantage au proxy MySQL.
|
|
</para>
|
|
<para>
|
|
Un proxy MySQL fonctionne au dessus de la couche physique. Avec un
|
|
proxy MySQL, vous devez analyser et effectuer du "reverse engineering"
|
|
du protocole client serveur MySQL. Les actions sont limitées à celles
|
|
qui peuvent être effectuées par la manipulation du protocole
|
|
de communication. Si la couche physique change (ce qui arrive très rarement),
|
|
les scripts du proxy MySQL peut devoir être adaptés.
|
|
</para>
|
|
<para>
|
|
Les plugins <literal>Mysqlnd</literal> fonctionnent au dessus de l'API C,
|
|
reflétant ainsi les APIs client <literal>libmysqlclient</literal>.
|
|
Cette API C est essentiellement une enveloppe du protocole Serveur Client MySQL,
|
|
ou de la couche physique, vu qu'elle est appelée quelques fois. Vous pouvez
|
|
intercepter tous les appels à l'API C. PHP utilise l'API C, toutefois,
|
|
vous pouvez connecter tous les appels PHP, sans avoir besoin de programmer
|
|
au niveau de la couche physique.
|
|
</para>
|
|
<para>
|
|
<literal>Mysqlnd</literal> implémente la couche physique. Les plugins
|
|
peuvent toutefois analyser, effectuer un "reverse engineering", manipuler
|
|
et toujours remplacer le protocole de communication. Cependant, ce n'est
|
|
généralement pas nécessaire.
|
|
</para>
|
|
<para>
|
|
Vu que les plugins vous autorisent à créer des implémentations qui
|
|
utilisent les 2 niveaux (API C et couche physique), ils ont plus de flexibilité
|
|
que le proxy MySQL. Si un plugin <literal>mysqlnd</literal> est implémenté
|
|
en utilisant l'API C, toutes les modifications ultérieures à la couche
|
|
physique ne nécessiteront pas de modification au plugin en tant que tel.
|
|
</para>
|
|
</section>
|
|
<section xml:id="mysqlnd.plugin.obtaining">
|
|
<title>Obtenir l'API du plugin mysqlnd</title>
|
|
<para>
|
|
L'API du plugin <literal>mysqlnd</literal> est simplement une partie
|
|
de l'extension du driver PHP Natif MySQL, <literal>ext/mysqlnd</literal>.
|
|
Le développement de l'API du plugin <literal>mysqlnd</literal> commença
|
|
en Décembre 2009. Il est développé comme une partie du dépôt source de PHP,
|
|
et ainsi, est disponible depuis soit le dépôt public Git, soit depuis
|
|
le téléchargement des sources.
|
|
</para>
|
|
<para>
|
|
Les développeurs de plugin peuvent déterminer la version de
|
|
<literal>mysqlnd</literal> via la variable
|
|
<literal>MYSQLND_VERSION</literal>, au format
|
|
<quote>mysqlnd 5.0.7-dev - 091210 - $Revision: 300535</quote>,
|
|
ou via <literal>MYSQLND_VERSION_ID</literal>, qui est un entier
|
|
comme par exemple 50007. Les développeurs peuvent calculer le numéro
|
|
de version comme ceci :
|
|
</para>
|
|
<table xml:id="mysqlnd.plugin.version-id">
|
|
<title>Table de calcul des MYSQLND_VERSION_ID</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Version (partie)</entry>
|
|
<entry>Exemple</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Majeur*10000</entry>
|
|
<entry>5*10000 = 50000</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Mineur*100</entry>
|
|
<entry>0*100 = 0</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Patch</entry>
|
|
<entry>7 = 7</entry>
|
|
</row>
|
|
<row>
|
|
<entry>MYSQLND_VERSION_ID</entry>
|
|
<entry>50007</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<para>
|
|
Lors du développement, les développeurs doivent se référer
|
|
au numéro de version <literal>mysqlnd</literal> pour des tests
|
|
de compatibilité et de version, sachant que plusieurs
|
|
versions de <literal>mysqlnd</literal> peuvent survenir durant
|
|
un cycle de vie de la branche de développement de PHP.
|
|
</para>
|
|
</section>
|
|
<section xml:id="mysqlnd.plugin.architecture">
|
|
<title>Architecture du plugin du driver natif</title>
|
|
<para>
|
|
Cette section fournit un aperçu de l'architecture du plugin
|
|
<literal>mysqlnd</literal>.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Aperçu du driver natif MySQL</emphasis>
|
|
</para>
|
|
<para>
|
|
Avant de développer des plugins <literal>mysqlnd</literal>,
|
|
il est utile d'avoir une connaissance minimale sur l'organisation
|
|
de <literal>mysqlnd</literal>. <literal>Mysqlnd</literal> est composé
|
|
des modules suivants :
|
|
</para>
|
|
<table xml:id="mysqlnd.plugin.orgchart">
|
|
<title>Schéma de l'organisation mysqlnd, par module</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Modules de statistiques</entry>
|
|
<entry>mysqlnd_statistics.c</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Connexion</entry>
|
|
<entry>mysqlnd.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Jeu de résultats</entry>
|
|
<entry>mysqlnd_result.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Données méta du jeu de résultats</entry>
|
|
<entry>mysqlnd_result_meta.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Requête</entry>
|
|
<entry>mysqlnd_ps.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Réseau</entry>
|
|
<entry>mysqlnd_net.c</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Couche physique</entry>
|
|
<entry>mysqlnd_wireprotocol.c</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<para>
|
|
<emphasis role="bold">Objet C orienté paradigme</emphasis>
|
|
</para>
|
|
<para>
|
|
Au niveau du code, <literal>mysqlnd</literal> utilise un masque C
|
|
pour implémenter l'orientation de l'objet.
|
|
</para>
|
|
<para>
|
|
En C, vous utilisez une structure (<literal>struct</literal>)
|
|
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.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Le cycle de vie PHP</emphasis>
|
|
</para>
|
|
<para>
|
|
Le cycle de vie de PHP comporte 2 cycles basiques :
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Le cycle de démarrage et d'arrêt du moteur PHP
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Le cycle d'une demande
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Comment fonctionne un plugin</emphasis>
|
|
</para>
|
|
<para>
|
|
Un plugin <literal>mysqlnd</literal> fonctionne en interceptant les appels
|
|
effectués à <literal>mysqlnd</literal> par les extensions qui utilisent
|
|
<literal>mysqlnd</literal>. Ceci est possible en obtenant la table
|
|
de fonction <literal>mysqlnd</literal>, en la sauvegardant, et en la
|
|
remplaçant par une table de fonction personnalisé, qui appelle les fonctions
|
|
du plugin.
|
|
</para>
|
|
<para>
|
|
Le code suivant montre la façon dont la table de fonction
|
|
<literal>mysqlnd</literal> est remplacée :
|
|
</para>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* 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);
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<note>
|
|
<para>
|
|
N'utilisez aucune logique de taille fixe lors de la manipulation
|
|
de la table de fonction <literal>mysqlnd</literal> : 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.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
<emphasis role="bold">Appel des méthodes parents</emphasis>
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
Dans ce cas, tout comme pour <literal>Connection::stmt_init()</literal>,
|
|
il est vital d'appeler la méthode parent avant toute autre activité
|
|
dans la méthode dérivée.
|
|
</para>
|
|
<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); /* retour avec appel du parent */
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
<emphasis role="bold">Étendre des propriétés</emphasis>
|
|
</para>
|
|
<para>
|
|
Un objet <literal>mysqlnd</literal> 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 <literal>mysqlnd</literal>
|
|
ne peuvent pas ajouter simplement des propriétés aux objets.
|
|
</para>
|
|
<para>
|
|
Les données arbitraires (propriétés) peuvent être ajoutées aux objets
|
|
<literal>mysqlnd</literal> en utilisant une fonction appropriée de la
|
|
famille <literal>mysqlnd_plugin_get_plugin_<object>_data()</literal>.
|
|
Lors de l'allocation d'un objet, <literal>mysqlnd</literal> réserve
|
|
un espace à la fin de l'objet pour accueillir un pointeur
|
|
<literal>void *</literal> vers des données arbitraires.
|
|
<literal>mysqlnd</literal> réserve un espace pour un pointeur
|
|
<literal>void *</literal> par plugin.
|
|
</para>
|
|
<para>
|
|
La table suivante montre comment calculer la position d'un pointeur
|
|
pour un plugin spécifique :
|
|
</para>
|
|
<table xml:id="mysqlnd.plugin.pointercalc">
|
|
<title>Calcul des pointeurs pour mysqlnd</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Adresse mémoire</entry>
|
|
<entry>Contenus</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>0</entry>
|
|
<entry>Début de la structure C de l'objet mysqlnd</entry>
|
|
</row>
|
|
<row>
|
|
<entry>n</entry>
|
|
<entry>Fin de la structure C de l'objet mysqlnd</entry>
|
|
</row>
|
|
<row>
|
|
<entry>n + (m x sizeof(void*))</entry>
|
|
<entry>void* vers les données de l'objet du m-ème plugin</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<para>
|
|
Si vous prévoyez de faire des sous-classes des constructeurs
|
|
des objets <literal>mysqlnd</literal>, ce qui est autorisé,
|
|
vous devez conserver ceci en mémoire !
|
|
</para>
|
|
<para>
|
|
Le code suivant montre la façon dont on étend des propriétés :
|
|
</para>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* 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;
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
Le développeur du plugin est responsable de la gestion de la mémoire
|
|
associée aux données du plugin.
|
|
</para>
|
|
<para>
|
|
L'utilisation de l'allocateur de mémoire <literal>mysqlnd</literal>
|
|
est recommandée pour les données du plugin. Ces fonctions sont nommées
|
|
en utilisant la convention suivante : <literal>mnd_*loc()</literal>.
|
|
L'allocateur <literal>mysqlnd</literal> a quelques fonctionnalités bien utiles,
|
|
comme la possibilité d'utiliser un allocateur de débogage dans une compilation
|
|
non-débogue.
|
|
</para>
|
|
<table xml:id="mysqlnd.plugin.subclass">
|
|
<title>Quand et comment faire une sous-classe</title>
|
|
<tgroup cols="4">
|
|
<thead>
|
|
<row>
|
|
<entry></entry>
|
|
<entry>Quand faire une sous-classe ?</entry>
|
|
<entry>Est-ce que chaque instance a sa table de fonction privée ?</entry>
|
|
<entry>Comment faire une sous-classe ?</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Connexion (MYSQLND)</entry>
|
|
<entry>MINIT</entry>
|
|
<entry>Non</entry>
|
|
<entry>mysqlnd_conn_get_methods()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Jeu de résultats (MYSQLND_RES)</entry>
|
|
<entry>MINIT ou après</entry>
|
|
<entry>Oui</entry>
|
|
<entry>mysqlnd_result_get_methods() ou méthode de l'objet de manipulation de la table de fonction</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Méta du jeu de résultats (MYSQLND_RES_METADATA)</entry>
|
|
<entry>MINIT</entry>
|
|
<entry>Non</entry>
|
|
<entry>mysqlnd_result_metadata_get_methods()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Requête (MYSQLND_STMT)</entry>
|
|
<entry>MINIT</entry>
|
|
<entry>Non</entry>
|
|
<entry>mysqlnd_stmt_get_methods()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Réseau (MYSQLND_NET)</entry>
|
|
<entry>MINIT ou après</entry>
|
|
<entry>Oui</entry>
|
|
<entry>mysqlnd_net_get_methods() ou méthode de l'objet de manipulation de la table de fonction</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Couche physique (MYSQLND_PROTOCOL)</entry>
|
|
<entry>MINIT ou après</entry>
|
|
<entry>Oui</entry>
|
|
<entry>mysqlnd_protocol_get_methods() ou méthode de l'objet de manipulation de la table de fonction</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<para>
|
|
Vous ne devez pas manipuler les tables de fonction après MINIT si
|
|
ce n'est pas autorisé suivant la table ci-dessus.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<table xml:id="mysqlnd.plugin.constatus">
|
|
<title>Statut du constructeur</title>
|
|
<tgroup cols="4">
|
|
<thead>
|
|
<row>
|
|
<entry>Type</entry>
|
|
<entry>Allocation, construction, réinitialisation</entry>
|
|
<entry>Peut-être modifié ?</entry>
|
|
<entry>Appelant</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Connexion (MYSQLND)</entry>
|
|
<entry>mysqlnd_init()</entry>
|
|
<entry>Non</entry>
|
|
<entry>mysqlnd_connect()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Jeu de résultats(MYSQLND_RES)</entry>
|
|
<entry><para>
|
|
Allocation :
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Connection::result_init()
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
Reset et ré-initialisation lors de :
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Result::use_result()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Result::store_result
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist></entry>
|
|
<entry>Oui, mais appel du parent !</entry>
|
|
<entry><itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Connection::list_fields()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Statement::get_result()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Statement::prepare() (Méta-données uniquement)
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Statement::resultMetaData()
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist></entry>
|
|
</row>
|
|
<row>
|
|
<entry>Méta du jeu de résultats (MYSQLND_RES_METADATA)</entry>
|
|
<entry>Connection::result_meta_init()</entry>
|
|
<entry>Oui, mais appel du parent !</entry>
|
|
<entry>Result::read_result_metadata()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Statement (MYSQLND_STMT)</entry>
|
|
<entry>Connection::stmt_init()</entry>
|
|
<entry>Oui, mais appel du parent !</entry>
|
|
<entry>Connection::stmt_init()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Réseau (MYSQLND_NET)</entry>
|
|
<entry>mysqlnd_net_init()</entry>
|
|
<entry>Non</entry>
|
|
<entry>Connection::init()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Couche physique (MYSQLND_PROTOCOL)</entry>
|
|
<entry>mysqlnd_protocol_init()</entry>
|
|
<entry>Non</entry>
|
|
<entry>Connection::init()</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<para>
|
|
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 <literal>mysqlnd</literal>
|
|
ainsi que pour la logique de l'objet <literal>mysqlnd</literal>. 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.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<table xml:id="mysqlnd.plugin.deststatus">
|
|
<title>Statut du destructeur</title>
|
|
<tgroup cols="3">
|
|
<thead>
|
|
<row>
|
|
<entry>Type</entry>
|
|
<entry>La méthode dérivée doit appeler le parent ?</entry>
|
|
<entry>Destructeur</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Connexion</entry>
|
|
<entry>oui, après l'exécution de la méthode</entry>
|
|
<entry>free_contents(), end_psession()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Jeu de résultats</entry>
|
|
<entry>oui, après l'exécution de la méthode</entry>
|
|
<entry>free_result()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Méta du jeu de résultats</entry>
|
|
<entry>oui, après l'exécution de la méthode</entry>
|
|
<entry>free()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Requête</entry>
|
|
<entry>oui, après l'exécution de la méthode</entry>
|
|
<entry>dtor(), free_stmt_content()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Réseau</entry>
|
|
<entry>oui, après l'exécution de la méthode</entry>
|
|
<entry>free()</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Couche physique</entry>
|
|
<entry>oui, après l'exécution de la méthode</entry>
|
|
<entry>free()</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<para>
|
|
Les destructeurs sont les endroits parfaits pour libérer les propriétés,
|
|
<literal>mysqlnd_plugin_get_plugin_<replaceable><object></replaceable>_data()</literal>.
|
|
</para>
|
|
<para>
|
|
Les destructeurs listés peuvent ne pas être les équivalents aux
|
|
méthodes actuelles <literal>mysqlnd</literal> 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 <literal>mysqlnd</literal>.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
</section>
|
|
<section xml:id="mysqlnd.plugin.api">
|
|
<title>L'API du plugin mysqlnd</title>
|
|
<para>
|
|
Voici la liste des fonctions fournies dans l'API plugin
|
|
<literal>mysqlnd</literal> :
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_plugin_register()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_plugin_count()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_plugin_get_plugin_connection_data()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_plugin_get_plugin_result_data()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_plugin_get_plugin_stmt_data()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_plugin_get_plugin_net_data()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_plugin_get_plugin_protocol_data()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_conn_get_methods()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_result_get_methods()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_result_meta_get_methods()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_stmt_get_methods()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_net_get_methods()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_protocol_get_methods()
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
Il n'y a pas de définition formelle de ce qu'est un plugin
|
|
ainsi de la façon dont fonctionne un plugin.
|
|
</para>
|
|
<para>
|
|
Les composants les plus souvent trouvés dans les mécanismes de plugin sont :
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Un gestionnaire de plugin
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Une API du plugin
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Les services applicatifs (ou modules)
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Les APIs des services applicatifs (ou APIs du module)
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
Le concept d'un plugin <literal>mysqlnd</literal> utilise ces fonctionnalités,
|
|
ainsi que d'autres joyeusetés d'architecture ouverte.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Aucune restriction</emphasis>
|
|
</para>
|
|
<para>
|
|
Un plugin a un accès total aux travaux internes de
|
|
<literal>mysqlnd</literal>. Il n'y a aucune limite de sécurité
|
|
ou de restrictions. Tout peut être écrasé pour implémenter des
|
|
algorithmes utiles ou hostiles. Il est recommandé de ne déployer
|
|
que des plugins depuis des sources de confiance.
|
|
</para>
|
|
<para>
|
|
Tel que discuté précédemment, les plugins peuvent utiliser librement
|
|
des pointeurs. Ces pointeurs ne sont restreints en aucune manière,
|
|
aussi, vous pouvez pointer vers les données d'un autre plugin.
|
|
Une simple position arithmétique peut être utilisée pour lire
|
|
les données d'un autre plugin.
|
|
</para>
|
|
<para>
|
|
Il est recommandé d'écrire des plugins coopératifs, et ainsi, appeler
|
|
toujours la méthode parent. Les plugins devraient toujours coopérer avec
|
|
<literal>mysqlnd</literal>.
|
|
</para>
|
|
<table xml:id="mysqlnd.plugin.chaining">
|
|
<title>Enjeux : un exemple de chaînage et de coopération</title>
|
|
<tgroup cols="3">
|
|
<thead>
|
|
<row>
|
|
<entry>Extension</entry>
|
|
<entry>Pointeur mysqlnd.query()</entry>
|
|
<entry>Pile d'appel si on appelle le parent</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>
|
|
<para>
|
|
mysqlnd_cache.query()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd.query
|
|
</para>
|
|
</listitem>
|
|
</orderedlist></entry>
|
|
</row>
|
|
<row>
|
|
<entry>ext/mysqlnd_monitor</entry>
|
|
<entry>mysqlnd_monitor.query()</entry>
|
|
<entry><orderedlist>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_monitor.query()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd_cache.query()
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
mysqlnd.query
|
|
</para>
|
|
</listitem>
|
|
</orderedlist></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
<para>
|
|
Dans ce scénario, un plugin cache (<literal>ext/mysqlnd_cache</literal>) et
|
|
un plugin de surveillance (<literal>ext/mysqlnd_monitor</literal>)
|
|
sont chargés. Les 2 ont une sous-classe de <literal>Connection::query()</literal>.
|
|
L'enregistrement du plugin survient lors du <literal>MINIT</literal>
|
|
en utilisant la logique évoquée précédemment. PHP appelle les extensions
|
|
dans un ordre alphabétique par défaut. Les plugins ne sont pas au courant
|
|
les uns les autres et ne peuvent fixer de dépendances.
|
|
</para>
|
|
<para>
|
|
Par défaut, les plugins appellent l'implémentation du parent de la
|
|
méthode de requête dans leur version de la méthode dérivée.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Récapitulatif de l'extension PHP</emphasis>
|
|
</para>
|
|
<para>
|
|
Voici un récapitulatif de ce qui survient lors de l'utilisation
|
|
d'un plugin d'exemple, <literal>ext/mysqlnd_plugin</literal>,
|
|
qui expose l'API C du plugin <literal>mysqlnd</literal> à PHP :
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Toutes les applications PHP MySQL tente d'établir une connexion
|
|
à l'adresse 192.168.2.29
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
L'application PHP utilisera <literal>ext/mysql</literal>,
|
|
<literal>ext/mysqli</literal> ou <literal>PDO_MYSQL</literal>.
|
|
Ces 3 extensions PHP MySQL utilisent <literal>mysqlnd</literal> pour
|
|
établir la connexion à l'adresse 192.168.2.29.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>Mysqlnd</literal> appelle sa méthode de connexion, qui a été sous-classé
|
|
par <literal>ext/mysqlnd_plugin</literal>.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>ext/mysqlnd_plugin</literal> appelle la méthode de l'espace utilisateur
|
|
<literal>proxy::connect()</literal> enregistrée par l'utilisateur.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
L'espace utilisateur modifie l'hôte de connexion de 192.168.2.29
|
|
à 127.0.0.1 et retourne la connexion établie par
|
|
<literal>parent::connect()</literal>.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>ext/mysqlnd_plugin</literal> exécute l'équivalent de
|
|
<literal>parent::connect(127.0.0.1)</literal> en appelant la méthode
|
|
originale de <literal>mysqlnd</literal> pour établir une connexion.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>ext/mysqlnd</literal> établit une connexion et redonne la main
|
|
à <literal>ext/mysqlnd_plugin</literal>.
|
|
<literal>ext/mysqlnd_plugin</literal> retourne également.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Quelque soit l'extension PHP MySQL utilisée par l'application,
|
|
elle reçoit une connexion à 127.0.0.1. L'extension PHP MySQL
|
|
redonne la main à l'application PHP. Le cycle est clos.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
<section xml:id="mysqlnd.plugin.developing">
|
|
<title>Bien commencer la compilation d'un plugin mysqlnd</title>
|
|
<para>
|
|
Il est important de se souvenir qu'un plugin <literal>mysqlnd</literal>
|
|
est lui-même une extension PHP.
|
|
</para>
|
|
<para>
|
|
Le code suivant montre la structure basique d'une fonction MINIT
|
|
utilisée dans un plugin typique <literal>mysqlnd</literal> :
|
|
</para>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* my_php_mysqlnd_plugin.c */
|
|
|
|
static PHP_MINIT_FUNCTION(mysqlnd_plugin) {
|
|
/* globales, entrées ini, ressources, classes */
|
|
|
|
/* enregistrement du 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>
|
|
<para>
|
|
<emphasis role="bold">Tâche d'analyse : depuis C vers l'espace utilisateur</emphasis>
|
|
</para>
|
|
<programlisting>
|
|
<![CDATA[
|
|
class proxy extends mysqlnd_plugin_connection {
|
|
public function connect($host, ...) { .. }
|
|
}
|
|
mysqlnd_plugin_set_conn_proxy(new proxy());
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
Process:
|
|
</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
PHP : l'utilisateur enregistre une fonction de rappel pour le plugin
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
PHP : l'utilisateur appelle une méthode de l'API PHP MySQL pour se connecter à MySQL
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
C : ext/*mysql* appelle la méthode mysqlnd
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
C : mysqlnd se termine dans ext/mysqlnd_plugin
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
C : ext/mysqlnd_plugin
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
Appel de la fonction de rappel de l'espace utilisateur
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Ou la méthode originale <literal>mysqlnd</literal>, si l'espace
|
|
utilisateur n'a pas défini de fonction de rappel
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
<para>
|
|
Vous devez effectuer les opérations suivantes :
|
|
</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
Écrire une classe "mysqlnd_plugin_connection" en C
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Accepter et enregistrer l'objet proxy via
|
|
"mysqlnd_plugin_set_conn_proxy()"
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Appeler les méthodes de proxy de l'espace utilisateur
|
|
depuis C (optimisation - zend_interfaces.h)
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
<para>
|
|
Les méthodes de l'objet de l'espace utilisateur peuvent soit être
|
|
appelées en utilisant <literal>call_user_function()</literal>,
|
|
soit vous pouvez opérer à un niveau en dessous du moteur Zend et
|
|
utiliser <literal>zend_call_method()</literal>.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Optimisation : appel des méthodes depuis C en utilisant
|
|
zend_call_method</emphasis>
|
|
</para>
|
|
<para>
|
|
Le code suivant montre un prototype pour la fonction
|
|
<literal>zend_call_method</literal>, issue de
|
|
<filename>zend_interfaces.h</filename>.
|
|
</para>
|
|
<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>
|
|
<para>
|
|
L'API Zend supporte 2 arguments. Vous pouvez en avoir besoin de plus, par
|
|
exemple :
|
|
</para>
|
|
<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>
|
|
<para>
|
|
Pour contourner ce problème, vous devrez faire une copie
|
|
de <literal>zend_call_method()</literal> et ajouter une
|
|
fonctionnalité pour ajouter des paramètres. Vous pouvez
|
|
réaliser ceci en créant un jeu de macros
|
|
<literal>MY_ZEND_CALL_METHOD_WRAPPER</literal>.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Appel de l'espace utilisateur PHP</emphasis>
|
|
</para>
|
|
<para>
|
|
Le code ci-dessous montre la méthode optimisée pour effectuer un
|
|
appel à une fonction de l'espace utilisateur depuis C :
|
|
</para>
|
|
<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) {
|
|
/* appel du proxy de l'espace utilisateur */
|
|
ret = MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, host, /*...*/);
|
|
} else {
|
|
/* ou la méthode originale mysqlnd = ne rien faire, être transparent */
|
|
ret = org_methods.connect(conn, host, user, passwd,
|
|
passwd_len, db, db_len, port,
|
|
socket, mysql_flags TSRMLS_CC);
|
|
}
|
|
return ret;
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
<emphasis role="bold">Appel de l'espace utilisateur: arguments simples</emphasis>
|
|
</para>
|
|
<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>
|
|
<para>
|
|
<emphasis role="bold">Appel de l'espace utilisateur : structures comme arguments</emphasis>
|
|
</para>
|
|
<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>
|
|
<para>
|
|
Le premier argument de toutes les méthodes <literal>mysqlnd</literal>
|
|
est un objet C. Par exemple, le premier argument de la méthode
|
|
connect() est un pointeur vers <literal>MYSQLND</literal>.
|
|
La structure MYSQLND représente un objet de connexion
|
|
<literal>mysqlnd</literal>.
|
|
</para>
|
|
<para>
|
|
Le pointeur de l'objet de connexion <literal>mysqlnd</literal>
|
|
peut être comparé à un pointeur de fichier standard I/O.
|
|
Tout comme un pointeur de fichier standard I/O, un objet de
|
|
connexion <literal>mysqlnd</literal> doit être lié à l'espace
|
|
utilisateur en utilisant une variable PHP de type ressource.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Depuis C vers l'espace utilisateur, puis, retour</emphasis>
|
|
</para>
|
|
<programlisting>
|
|
<![CDATA[
|
|
class proxy extends mysqlnd_plugin_connection {
|
|
public function connect($conn, $host, ...) {
|
|
/* "pre" hook */
|
|
printf("Connexion à l'hôte = '%s'\n", $host);
|
|
debug_print_backtrace();
|
|
return parent::connect($conn);
|
|
}
|
|
|
|
public function query($conn, $query) {
|
|
/* "post" hook */
|
|
$ret = parent::query($conn, $query);
|
|
printf("Requête = '%s'\n", $query);
|
|
return $ret;
|
|
}
|
|
}
|
|
mysqlnd_plugin_set_conn_proxy(new proxy());
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
Les utilisateurs PHP doivent pouvoir appeler l'implémentation
|
|
du parent d'une méthode écrasée.
|
|
</para>
|
|
<para>
|
|
Comme résultat d'un sous-classement, il est possible de
|
|
redéfinir uniquement les méthodes sélectionnées, et vous
|
|
pouvez choisir d'avoir des actions "pre" ou "post".
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Construction d'une classe : mysqlnd_plugin_connection::connect()</emphasis>
|
|
</para>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* my_mysqlnd_plugin_classes.c */
|
|
|
|
PHP_METHOD("mysqlnd_plugin_connection", connect) {
|
|
/* ... simplifié ! ... */
|
|
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, /* simplifié! */ 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
|
|
-->
|