<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/mysqlnd.plugin.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'fr',
  ),
  'this' => 
  array (
    0 => 'mysqlnd.plugin.architecture.php',
    1 => 'Architecture du plugin du driver natif',
    2 => 'Architecture du plugin du driver natif',
  ),
  'up' => 
  array (
    0 => 'mysqlnd.plugin.php',
    1 => 'API du plugin du driver natif MySQL',
  ),
  'prev' => 
  array (
    0 => 'mysqlnd.plugin.obtaining.php',
    1 => 'Obtenir l\'API du plugin mysqlnd',
  ),
  'next' => 
  array (
    0 => 'mysqlnd.plugin.api.php',
    1 => 'L\'API du plugin mysqlnd',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'fr',
    'path' => 'reference/mysqlnd/plugin.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="mysqlnd.plugin.architecture" class="section">
  <h2 class="title">Architecture du plugin du driver natif</h2>
  <p class="simpara">
   Cette section fournit un aperçu de l&#039;architecture du plugin
   <code class="literal">mysqlnd</code>.
  </p>
  <p class="simpara">
   <strong>Aperçu du driver natif MySQL</strong>
  </p>
  <p class="simpara">
   Avant de développer des plugins <code class="literal">mysqlnd</code>,
   il est utile d&#039;avoir une connaissance minimale sur l&#039;organisation
   de <code class="literal">mysqlnd</code>. <code class="literal">Mysqlnd</code> est composé
   des modules suivants :
  </p>
  <table id="mysqlnd.plugin.orgchart" class="doctable table">
   <caption><strong>Schéma de l&#039;organisation mysqlnd, par module</strong></caption>
   
    <thead>
     <tr>
      <th>Modules de statistiques</th>
      <th>mysqlnd_statistics.c</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Connexion</td>
      <td>mysqlnd.c</td>
     </tr>

     <tr>
      <td>Jeu de résultats</td>
      <td>mysqlnd_result.c</td>
     </tr>

     <tr>
      <td>Données méta du jeu de résultats</td>
      <td>mysqlnd_result_meta.c</td>
     </tr>

     <tr>
      <td>Requête</td>
      <td>mysqlnd_ps.c</td>
     </tr>

     <tr>
      <td>Réseau</td>
      <td>mysqlnd_net.c</td>
     </tr>

     <tr>
      <td>Couche physique</td>
      <td>mysqlnd_wireprotocol.c</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   <strong>Objet C orienté paradigme</strong>
  </p>
  <p class="simpara">
   Au niveau du code, <code class="literal">mysqlnd</code> utilise un masque C
   pour implémenter l&#039;orientation de l&#039;objet.
  </p>
  <p class="simpara">
   En C, on utilise une structure (<code class="literal">struct</code>)
   pour représenter un objet. Les membres de cette structure
   représentent les propriétés de l&#039;objet. Les membres de la
   structure pointant vers des fonctions représentent les méthodes.
  </p>
  <p class="simpara">
   Contrairement aux autres langages comme C++ ou Java, il n&#039;y a
   pas de règles fixes sur l&#039;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.
  </p>
  <p class="simpara">
   <strong>Le cycle de vie PHP</strong>
  </p>
  <p class="simpara">
   Le cycle de vie de PHP comporte 2 cycles basiques :
  </p>
  <ul class="itemizedlist">
   <li class="listitem">
    <span class="simpara">
     Le cycle de démarrage et d&#039;arrêt du moteur PHP
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     Le cycle d&#039;une demande
    </span>
   </li>
  </ul>
  <p class="simpara">
   Lorsque le moteur PHP démarre, il appelle la fonction d&#039;initialisation
   du module (MINIT) de chaque extension enregistrée. Ceci
   permet à chaque module de définir les variables et d&#039;allouer les
   ressources qui doivent exister pour la durée de vie du processus
   correspondant au moteur PHP. Lorsque le moteur PHP s&#039;arrête,
   il appelle la fonction d&#039;arrêt du module (MSHUTDOWN) pour chaque extension.
  </p>
  <p class="simpara">
   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&#039;initialisation
   de chaque extension. L&#039;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&#039;arrêt (RSHUTDOWN) pour chaque extension,
   ainsi, l&#039;extension peut lancer tout le nettoyage nécessaire.
  </p>
  <p class="simpara">
   <strong>Comment fonctionne un plugin</strong>
  </p>
  <p class="simpara">
   Un plugin <code class="literal">mysqlnd</code> fonctionne en interceptant les appels
   effectués à <code class="literal">mysqlnd</code> par les extensions qui utilisent
   <code class="literal">mysqlnd</code>. Ceci est possible en obtenant la table
   de fonction <code class="literal">mysqlnd</code>, en la sauvegardant, et en la
   remplaçant par une table de fonction personnalisée, qui appelle les fonctions
   du plugin.
  </p>
  <p class="simpara">
   Le code suivant montre la façon dont la table de fonction
   <code class="literal">mysqlnd</code> est remplacée :
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* 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(&amp;org_methods, current_methods,
    sizeof(struct st_mysqlnd_conn_methods);

  /* installation des nouvelles méthodes */
  current_methods-&gt;query = MYSQLND_METHOD(my_conn_class, query);
}
</pre></div>
  </div>

  <p class="simpara">
   Les manipulations de la table de fonction de connexion doivent
   être effectuées lors de l&#039;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&#039;une ressource globale partagée lors d&#039;un processus
   de demande entraînera la plupart du temps des conflits.
  </p>
  <blockquote class="note"><p><strong class="note">Note</strong>: 
   <span class="simpara">
    N&#039;utiliser aucune logique de taille fixe lors de la manipulation
    de la table de fonction <code class="literal">mysqlnd</code> : 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.
   </span>
  </p></blockquote>
  <p class="simpara">
   <strong>Appel des méthodes parents</strong>
  </p>
  <p class="simpara">
   Si la table de fonction originale est sauvegardée, il est toujours
   possible d&#039;appeler les entrées de la table de fonction originale -
   les méthodes parents.
  </p>
  <p class="simpara">
   Dans ce cas, tout comme pour <code class="literal">Connection::stmt_init()</code>,
   il est vital d&#039;appeler la méthode parent avant toute autre activité
   dans la méthode dérivée.
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
MYSQLND_METHOD(my_conn_class, query)(MYSQLND *conn,
  const char *query, unsigned int query_len TSRMLS_DC) {

  php_printf(&quot;my_conn_class::query(query = %s)\n&quot;, query);

  query = &quot;SELECT &#039;query rewritten&#039; FROM DUAL&quot;;
  query_len = strlen(query);

  return org_methods.query(conn, query, query_len); /* retour avec appel du parent */
}
</pre></div>
  </div>

  <p class="simpara">
   <strong>Étendre des propriétés</strong>
  </p>
  <p class="simpara">
   Un objet <code class="literal">mysqlnd</code> est représenté par une structure C.
   Il n&#039;est pas possible d&#039;ajouter un membre à une structure C au moment
   de l&#039;exécution. Les utilisateurs d&#039;objets <code class="literal">mysqlnd</code>
   ne peuvent pas ajouter simplement des propriétés aux objets.
  </p>
  <p class="simpara">
   Les données arbitraires (propriétés) peuvent être ajoutées aux objets
   <code class="literal">mysqlnd</code> en utilisant une fonction appropriée de la
   famille <code class="literal">mysqlnd_plugin_get_plugin_&lt;object&gt;_data()</code>.
   Lors de l&#039;allocation d&#039;un objet, <code class="literal">mysqlnd</code> réserve
   un espace à la fin de l&#039;objet pour accueillir un pointeur
   <code class="literal">void *</code> vers des données arbitraires.
   <code class="literal">mysqlnd</code> réserve un espace pour un pointeur
   <code class="literal">void *</code> par plugin.
  </p>
  <p class="simpara">
   La table suivante montre comment calculer la position d&#039;un pointeur
   pour un plugin spécifique :
  </p>
  <table id="mysqlnd.plugin.pointercalc" class="doctable table">
   <caption><strong>Calcul des pointeurs pour mysqlnd</strong></caption>
   
    <thead>
     <tr>
      <th>Adresse mémoire</th>
      <th>Contenus</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>0</td>
      <td>Début de la structure C de l&#039;objet mysqlnd</td>
     </tr>

     <tr>
      <td>n</td>
      <td>Fin de la structure C de l&#039;objet mysqlnd</td>
     </tr>

     <tr>
      <td>n + (m x sizeof(void*))</td>
      <td>void* vers les données de l&#039;objet du m-ème plugin</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Si l&#039;on prévoit de faire des sous-classes des constructeurs
   des objets <code class="literal">mysqlnd</code>, ce qui est autorisé,
   il faut conserver ceci en mémoire !
  </p>
  <p class="simpara">
   Le code suivant montre la façon dont on étend des propriétés :
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* 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&#039;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-&gt;persistent);
    (*props)-&gt;query_counter = 0;
  }
  return props;
}
</pre></div>
  </div>

  <p class="simpara">
   Le développeur du plugin est responsable de la gestion de la mémoire
   associée aux données du plugin.
  </p>
  <p class="simpara">
   L&#039;utilisation de l&#039;allocateur de mémoire <code class="literal">mysqlnd</code>
   est recommandée pour les données du plugin. Ces fonctions sont nommées
   en utilisant la convention suivante : <code class="literal">mnd_*loc()</code>.
   L&#039;allocateur <code class="literal">mysqlnd</code> a quelques fonctionnalités bien utiles,
   comme la possibilité d&#039;utiliser un allocateur de débogage dans une compilation
   non-débogue.
  </p>
  <table id="mysqlnd.plugin.subclass" class="doctable table">
   <caption><strong>Quand et comment faire une sous-classe</strong></caption>
   
    <thead>
     <tr>
      <th class="empty">&nbsp;</th>
      <th>Quand faire une sous-classe ?</th>
      <th>Est-ce que chaque instance a sa table de fonction privée ?</th>
      <th>Comment faire une sous-classe ?</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Connexion (MYSQLND)</td>
      <td>MINIT</td>
      <td>Non</td>
      <td>mysqlnd_conn_get_methods()</td>
     </tr>

     <tr>
      <td>Jeu de résultats (MYSQLND_RES)</td>
      <td>MINIT ou après</td>
      <td>Oui</td>
      <td>mysqlnd_result_get_methods() ou méthode de l&#039;objet de manipulation de la table de fonction</td>
     </tr>

     <tr>
      <td>Méta du jeu de résultats (MYSQLND_RES_METADATA)</td>
      <td>MINIT</td>
      <td>Non</td>
      <td>mysqlnd_result_metadata_get_methods()</td>
     </tr>

     <tr>
      <td>Requête (MYSQLND_STMT)</td>
      <td>MINIT</td>
      <td>Non</td>
      <td>mysqlnd_stmt_get_methods()</td>
     </tr>

     <tr>
      <td>Réseau (MYSQLND_NET)</td>
      <td>MINIT ou après</td>
      <td>Oui</td>
      <td>mysqlnd_net_get_methods() ou méthode de l&#039;objet de manipulation de la table de fonction</td>
     </tr>

     <tr>
      <td>Couche physique (MYSQLND_PROTOCOL)</td>
      <td>MINIT ou après</td>
      <td>Oui</td>
      <td>mysqlnd_protocol_get_methods() ou méthode de l&#039;objet de manipulation de la table de fonction</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Il ne faut pas manipuler les tables de fonction après MINIT si
   ce n&#039;est pas autorisé suivant la table ci-dessus.
  </p>
  <p class="simpara">
   Quelques classes contiennent un pointeur vers une méthode de la table
   de fonction. Toutes les instances d&#039;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.
  </p>
  <p class="simpara">
   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&#039;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&#039;un objet au moment du MINIT, et il est également possible d&#039;affiner des
   méthodes d&#039;un objet sans impacter les autres instances de la même
   classe.
  </p>
  <p class="simpara">
   L&#039;avantage de l&#039;approche avec une table de fonction partagée est
   la performance. Il n&#039;est pas nécessaire de copier une table de fonction
   pour chaque objet.
  </p>
  <table id="mysqlnd.plugin.constatus" class="doctable table">
   <caption><strong>Statut du constructeur</strong></caption>
   
    <thead>
     <tr>
      <th>Type</th>
      <th>Allocation, construction, réinitialisation</th>
      <th>Peut-être modifié ?</th>
      <th>Appelant</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Connexion (MYSQLND)</td>
      <td>mysqlnd_init()</td>
      <td>Non</td>
      <td>mysqlnd_connect()</td>
     </tr>

     <tr>
      <td>Jeu de résultats(MYSQLND_RES)</td>
      <td><span class="simpara">
       Allocation :
       </span>
       <ul class="itemizedlist">
        <li class="listitem">
         <span class="simpara">
          Connection::result_init()
         </span>
        </li>
       </ul>
       <span class="simpara">
        Reset et réinitialisation lors de :
       </span>
       <ul class="itemizedlist">
        <li class="listitem">
         <span class="simpara">
          Result::use_result()
         </span>
        </li>
        <li class="listitem">
         <span class="simpara">
          Result::store_result
         </span>
        </li>
       </ul></td>
      <td>Oui, mais appel du parent !</td>
      <td><ul class="itemizedlist">
       <li class="listitem">
        <span class="simpara">
         Connection::list_fields()
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Statement::get_result()
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Statement::prepare() (Méta-données uniquement)
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Statement::resultMetaData()
        </span>
       </li>
       </ul></td>
     </tr>

     <tr>
      <td>Méta du jeu de résultats (MYSQLND_RES_METADATA)</td>
      <td>Connection::result_meta_init()</td>
      <td>Oui, mais appel du parent !</td>
      <td>Result::read_result_metadata()</td>
     </tr>

     <tr>
      <td>Statement (MYSQLND_STMT)</td>
      <td>Connection::stmt_init()</td>
      <td>Oui, mais appel du parent !</td>
      <td>Connection::stmt_init()</td>
     </tr>

     <tr>
      <td>Réseau (MYSQLND_NET)</td>
      <td>mysqlnd_net_init()</td>
      <td>Non</td>
      <td>Connection::init()</td>
     </tr>

     <tr>
      <td>Couche physique (MYSQLND_PROTOCOL)</td>
      <td>mysqlnd_protocol_init()</td>
      <td>Non</td>
      <td>Connection::init()</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   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&#039;API du plugin <code class="literal">mysqlnd</code>
   ainsi que pour la logique de l&#039;objet <code class="literal">mysqlnd</code>. Si
   l&#039;on ne se soucie pas des alertes et que l&#039;on insiste pour
   remplacer les constructeurs, il est recommandé d&#039;au moins appeler
   le constructeur parent avant de faire quoi que ce soit dans le
   constructeur.
  </p>
  <p class="simpara">
   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&#039;objets non partagés, comme les jeux de résultats, le
   réseau ou encore la couche physique.
  </p>
  <table id="mysqlnd.plugin.deststatus" class="doctable table">
   <caption><strong>Statut du destructeur</strong></caption>
   
    <thead>
     <tr>
      <th>Type</th>
      <th>La méthode dérivée doit appeler le parent ?</th>
      <th>Destructeur</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Connexion</td>
      <td>oui, après l&#039;exécution de la méthode</td>
      <td>free_contents(), end_psession()</td>
     </tr>

     <tr>
      <td>Jeu de résultats</td>
      <td>oui, après l&#039;exécution de la méthode</td>
      <td>free_result()</td>
     </tr>

     <tr>
      <td>Méta du jeu de résultats</td>
      <td>oui, après l&#039;exécution de la méthode</td>
      <td>free()</td>
     </tr>

     <tr>
      <td>Requête</td>
      <td>oui, après l&#039;exécution de la méthode</td>
      <td>dtor(), free_stmt_content()</td>
     </tr>

     <tr>
      <td>Réseau</td>
      <td>oui, après l&#039;exécution de la méthode</td>
      <td>free()</td>
     </tr>

     <tr>
      <td>Couche physique</td>
      <td>oui, après l&#039;exécution de la méthode</td>
      <td>free()</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Les destructeurs sont les endroits parfaits pour libérer les propriétés,
   <code class="literal">mysqlnd_plugin_get_plugin_<span class="replaceable">&lt;object&gt;</span>_data()</code>.
  </p>
  <p class="simpara">
   Les destructeurs listés peuvent ne pas être les équivalents aux
   méthodes actuelles <code class="literal">mysqlnd</code> libérant l&#039;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&#039;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 <code class="literal">mysqlnd</code>.
  </p>
  <p class="simpara">
   La méthode recommandée pour les plugins est de modifier simplement les méthodes,
   libérer la mémoire et appeler l&#039;implémentation du parent immédiatement après.
  </p>
 </div><?php manual_footer($setup); ?>