<?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 => 'de',
  ),
  'this' => 
  array (
    0 => 'mysqlnd.plugin.architecture.php',
    1 => 'Plugin-Architektur des MySQL Native Drivers',
    2 => 'Plugin-Architektur des MySQL Native Drivers',
  ),
  'up' => 
  array (
    0 => 'mysqlnd.plugin.php',
    1 => 'Plugin-API des MySQL Native Drivers',
  ),
  'prev' => 
  array (
    0 => 'mysqlnd.plugin.obtaining.php',
    1 => 'Bezugsquellen f&uuml;r die mysqlnd-Plugin-API',
  ),
  'next' => 
  array (
    0 => 'mysqlnd.plugin.api.php',
    1 => 'Die Plugin-API von mysqlnd',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'de',
    '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">Plugin-Architektur des MySQL Native Drivers</h2>
  <p class="simpara">
   Dieser Abschnitt bietet einen Überblick über die Plugin-Architektur von
   <code class="literal">mysqlnd</code>.
  </p>
  <p class="simpara">
   <strong>Überblick über den MySQL Native Driver</strong>
  </p>
  <p class="simpara">
   Vor der Entwicklung eines <code class="literal">mysqlnd</code>-Plugins ist es
   nützlich, ein wenig über den Aufbau von <code class="literal">mysqlnd</code> selbst
   zu wissen. <code class="literal">Mysqlnd</code> besteht aus den folgenden Modulen:
  </p>
  <table id="mysqlnd.plugin.orgchart" class="doctable table">
   <caption><strong>Das Organisationsdiagramm von mysqlnd, pro Modul</strong></caption>
   
    <thead>
     <tr>
      <th>Modul-Statistiken</th>
      <th>mysqlnd_statistics.c</th>
     </tr>

    </thead>

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

     <tr>
      <td>Ergebnismenge</td>
      <td>mysqlnd_result.c</td>
     </tr>

     <tr>
      <td>Metadaten der Ergebnismenge</td>
      <td>mysqlnd_result_meta.c</td>
     </tr>

     <tr>
      <td>Anweisung</td>
      <td>mysqlnd_ps.c</td>
     </tr>

     <tr>
      <td>Netzwerk</td>
      <td>mysqlnd_net.c</td>
     </tr>

     <tr>
      <td>Wire-Protokoll</td>
      <td>mysqlnd_wireprotocol.c</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   <strong>Objektorientiertes Paradigma in C</strong>
  </p>
  <p class="simpara">
   Auf der Code-Ebene verwendet <code class="literal">mysqlnd</code> ein C-Pattern, um
   die Objektorientierung zu implementieren.
  </p>
  <p class="simpara">
   In C wird ein Objekt mittels <code class="literal">struct</code> beschrieben. Die
   Mitglieder dieser Struktur sind die Eigenschaften des Objekts. Die
   Strukturmitglieder, die auf Funktionen verweisen, stellen die Methoden dar.
  </p>
  <p class="simpara">
   Im Gegensatz zu anderen Sprachen wie C++ oder Java gibt es im
   objektorientierten Paradigma von C keine festen Regeln für die Vererbung.
   Es gibt jedoch einige Konventionen, die befolgt werden müssen und auf die
   später eingegangen wird.
  </p>
  <p class="simpara">
   <strong>Der PHP-Lebenszyklus</strong>
  </p>
  <p class="simpara">
   Der Lebenszyklus von PHP besteht aus 2 grundlegenden Zyklen:
  </p>
  <ul class="itemizedlist">
   <li class="listitem">
    <span class="simpara">
     Der Start- und Shutdown-Zyklus der PHP-Engine
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     Der Zyklus einer Anfrage
    </span>
   </li>
  </ul>
  <p class="simpara">
   Wenn die PHP-Engine startet, ruft sie für jede registrierte Erweiterung die
   Funktion für die Modulinitialisierung (MINIT) auf. Dies ermöglicht es jedem
   Modul, Variablen zu definieren und Ressourcen zuzuweisen, die während der
   gesamten Lebensdauer des Prozesses der PHP-Engine vorhanden sind. Wenn die
   PHP-Engine herunterfährt, ruft sie für jede Erweiterung die Funktion für
   das Herunterfahren des Moduls (MSHUTDOWN) auf.
  </p>
  <p class="simpara">
   Während der Lebensdauer der PHP-Engine erhält sie eine Reihe von Anfragen.
   Jede Anfrage löst einen neuen Lebenszyklus aus. Bei jeder Anfrage ruft die
   PHP-Engine die Anfrage-Initialisierungsfunktion der jeweiligen Erweiterung
   auf. Die Erweiterung kann alle für die Bearbeitung der Anfrage
   erforderlichen Variablen definieren und Ressourcen zuweisen. Am Ende des
   Anfragezyklus ruft die Engine für jede Erweiterung die Funktion zum
   Herunterfahren der Anfrage (RSHUTDOWN) auf, damit die Erweiterung alle
   erforderlichen Aufräumarbeiten durchführen kann.
  </p>
  <p class="simpara">
   <strong>Wie ein Plugin funktioniert</strong>
  </p>
  <p class="simpara">
   Ein <code class="literal">mysqlnd</code>-Plugin funktioniert, indem es die Aufrufe an
   <code class="literal">mysqlnd</code> abfängt, die von Erweiterungen stammen, die
   <code class="literal">mysqlnd</code> verwenden. Dies wird dadurch erreicht, dass die
   Funktionstabelle von <code class="literal">mysqlnd</code> abgerufen, gesichert und
   durch eine eigene Funktionstabelle ersetzt wird, die die Funktionen des
   Plugins nach Bedarf aufruft.
  </p>
  <p class="simpara">
   Der folgende Code zeigt, wie die Funktionstabelle von
   <code class="literal">mysqlnd</code> ersetzt wird:
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* ein Ort zum Speichern der ursprünglichen Funktionstabelle */
struct st_mysqlnd_conn_methods org_methods;

void minit_register_hooks(TSRMLS_D) {
  /* die aktive Funktionstabelle */
  struct st_mysqlnd_conn_methods * current_methods
    = mysqlnd_conn_get_methods();

  /* Sicherung der ursprünglichen Funktionstabelle */
  memcpy(&amp;org_methods, current_methods,
    sizeof(struct st_mysqlnd_conn_methods);

  /* Installation der neuen Methoden */
  current_methods-&gt;query = MYSQLND_METHOD(my_conn_class, query);
}
</pre></div>
  </div>

  <p class="simpara">
   Die Bearbeitung der Tabelle der Verbindungsfunktionen muss während der
   Modulinitialisierung (MINIT) erfolgen. Die Funktionstabelle ist eine
   gemeinsam genutzte globale Ressource. In einer Multi-Thread-Umgebung mit
   einem TSRM-Build führt die Bearbeitung einer gemeinsam genutzten globalen
   Ressource während der Verarbeitung einer Anfrage mit ziemlicher Sicherheit
   zu Konflikten.
  </p>
  <blockquote class="note"><p><strong class="note">Hinweis</strong>: 
   <span class="simpara">
    Bei der Bearbeitung der Funktionstabelle von <code class="literal">mysqlnd</code>
    sollte keine Logik mit fester Größe verwendet werden: Neue Methoden
    könnten am Ende der Funktionstabelle hinzugefügt werden. Die
    Funktionstabelle kann sich in der Zukunft jederzeit ändern.
   </span>
  </p></blockquote>
  <p class="simpara">
   <strong>Aufrufen von Elternmethoden</strong>
  </p>
  <p class="simpara">
   Wenn die ursprüngliche Funktionstabelle gesichert wird, können die
   ursprünglichen Einträge - die Elternmethoden - weiterhin aufgerufen werden.
  </p>
  <p class="simpara">
   In einigen Fällen, z. B. bei <code class="literal">Connection::stmt_init()</code>, ist
   es unerlässlich, die Elternmethode aufzurufen, bevor weitere Aktionen in
   der abgeleiteten Methode erfolgen.
  </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); /* return with call to parent */
}
</pre></div>
  </div>

  <p class="simpara">
   <strong>Erweitern von Eigenschaften</strong>
  </p>
  <p class="simpara">
   Ein <code class="literal">mysqlnd</code>-Objekt wird durch eine C-Struktur (struct)
   dargestellt. Es ist nicht möglich, einer C-Struktur zur Laufzeit ein
   Mitglied hinzuzufügen. Benutzer von <code class="literal">mysqlnd</code>-Objekten
   können nicht einfach Eigenschaften zu den Objekten hinzufügen.
  </p>
  <p class="simpara">
   Beliebige Daten (Eigenschaften) können zu einem
   <code class="literal">mysqlnd</code>-Objekt hinzugefügt werden, indem eine geeignete
   Funktion der
   <code class="literal">mysqlnd_plugin_get_plugin_&lt;object&gt;_data()</code>-Familie
   verwendet wird. Wenn ein Objekt zugewiesen wird, reserviert
   <code class="literal">mysqlnd</code> am Ende des Objekts Speicherplatz für einen
   <code class="literal">void *</code>-Zeiger auf beliebige Daten.
   <code class="literal">mysqlnd</code> reserviert den Platz für einen
   <code class="literal">void *</code>-Zeiger pro Plugin.
  </p>
  <p class="simpara">
   Die folgende Tabelle zeigt, wie die Position des Zeigers für ein bestimmtes
   Plugin berechnet werden kann:
  </p>
  <table id="mysqlnd.plugin.pointercalc" class="doctable table">
   <caption><strong>Berechnen eines Zeigers für mysqlnd</strong></caption>
   
    <thead>
     <tr>
      <th>Speicheradresse</th>
      <th>Inhalt</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>0</td>
      <td>Beginn der C-Struktur des mysqlnd-Objekts</td>
     </tr>

     <tr>
      <td>n</td>
      <td>Ende der C-Struktur des mysqlnd-Objekts</td>
     </tr>

     <tr>
      <td>n + (m x sizeof(void*))</td>
      <td>void* zu den Objektdaten des m-ten Plugins</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Wenn geplant ist, Unterklassen für einen der Konstruktoren des
   <code class="literal">mysqlnd</code>-Objekts zu erstellen, was erlaubt ist, muss dies
   unbedingt bedacht werden!
  </p>
  <p class="simpara">
   Der folgende Code zeigt, wie Eigenschaften erweitert werden:
  </p>
  <div class="example-contents">
<div class="cdata"><pre>
/* alle Daten, die zugeordnet werden sollen */
typedef struct my_conn_properties {
  unsigned long query_counter;
} MY_CONN_PROPERTIES;

/* die Plugin-ID */
unsigned int my_plugin_id;

void minit_register_hooks(TSRMLS_D) {
  /* erhalten einer eindeutigen Plugin-ID */
  my_plugin_id = mysqlnd_plugin_register();
  /* snip - siehe Extending Connection: methods */
}

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">
   Der Plugin-Entwickler ist für die Verwaltung des Speichers verantwortlich,
   der für die Plugin-Daten verwendet wird.
  </p>
  <p class="simpara">
   Es wird empfohlen, für die Daten von Plugins den Speicherallokator von
   <code class="literal">mysqlnd</code> zu verwenden. Diese Funktionen werden nach dem
   Schema <code class="literal">mnd_*loc()</code> benannt. Der
   <code class="literal">mysqlnd</code>-Allokator hat einige nützliche Eigenschaften,
   zum Beispiel die Möglichkeit, einen Debug-Allokator in einem
   Nicht-Debug-Build zu benutzen.
  </p>
  <table id="mysqlnd.plugin.subclass" class="doctable table">
   <caption><strong>Wann und wie eine Unterklasse erstellt wird</strong></caption>
   
    <thead>
     <tr>
      <th class="empty">&nbsp;</th>
      <th>Wann soll die Unterklasse erstellt werden?</th>
      <th>Hat jede Instanz ihre eine eigene Funktionstabelle?</th>
      <th>Wie wird die Unterklasse erstellt?</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Verbindung (MYSQLND)</td>
      <td>MINIT</td>
      <td>Nein</td>
      <td>mysqlnd_conn_get_methods()</td>
     </tr>

     <tr>
      <td>Ergebnismenge (MYSQLND_RES)</td>
      <td>MINIT oder später</td>
      <td>Ja</td>
      <td>
       mysqlnd_result_get_methods() oder Manipulation der Funktionstabelle der
       Objektmethoden
      </td>
     </tr>

     <tr>
      <td>Metadaten der Ergebnismenge (MYSQLND_RES_METADATA)</td>
      <td>MINIT</td>
      <td>Nein</td>
      <td>mysqlnd_result_metadata_get_methods()</td>
     </tr>

     <tr>
      <td>Anweisung (MYSQLND_STMT)</td>
      <td>MINIT</td>
      <td>Nein</td>
      <td>mysqlnd_stmt_get_methods()</td>
     </tr>

     <tr>
      <td>Netzwerk (MYSQLND_NET)</td>
      <td>MINIT oder später</td>
      <td>Ja</td>
      <td>
       mysqlnd_net_get_methods() oder Manipulation der Funktionstabelle der
       Objektmethoden
      </td>
     </tr>

     <tr>
      <td>Wire-Protokoll (MYSQLND_PROTOCOL)</td>
      <td>MINIT oder später</td>
      <td>Ja</td>
      <td>
       mysqlnd_protocol_get_methods() oder Manipulation der Funktionstabelle
       der Objektmethoden
      </td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Nach MINIT darf die Funktionstabelle nicht mehr manipuliert werden, wenn es
   nach der obigen Tabelle nicht erlaubt ist.
  </p>
  <p class="simpara">
   Einige Klassen enthalten einen Zeiger auf die Funktionstabelle der
   Methoden. Alle Instanzen einer solchen Klasse teilen sich dieselbe
   Funktionstabelle. Um Chaos zu vermeiden, insbesondere in Umgebungen mit
   Threads, dürfen solche Funktionstabellen nur während MINIT manipuliert
   werden.
  </p>
  <p class="simpara">
   Andere Klassen verwenden Kopien einer gemeinsam genutzten globalen
   Funktionstabelle. Die Kopie der Funktionstabelle der Klasse wird zusammen
   mit dem Objekt erstellt. Jedes Objekt verwendet seine eigene
   Funktionstabelle. Dadurch ergeben sich zwei Möglichkeiten: Zum einen kann
   die Standard-Funktionstabelle eines Objekts während MINIT manipuliert
   werden, zum anderen können die Methoden eines Objekts zusätzlich angepasst
   werden, ohne dass sich dies auf andere Instanzen derselben Klasse auswirkt.
  </p>
  <p class="simpara">
   Der Vorteil der gemeinsam genutzten Funktionstabellen ist die verbesserte
   Leistung. Das liegt daran, dass es nicht nötig ist, eine Funktionstabelle
   für jedes einzelne Objekt zu kopieren.
  </p>
  <table id="mysqlnd.plugin.constatus" class="doctable table">
   <caption><strong>Status des Konstruktors</strong></caption>
   
    <thead>
     <tr>
      <th>Typ</th>
      <th>Zuweisung, Konstruktion, Zurücksetzen</th>
      <th>Kann geändert werden?</th>
      <th>Aufgerufen von</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Verbindung (MYSQLND)</td>
      <td>mysqlnd_init()</td>
      <td>Nein</td>
      <td>mysqlnd_connect()</td>
     </tr>

     <tr>
      <td>Ergebnismenge (MYSQLND_RES)</td>
      <td>
       <span class="simpara">
        Zuweisung:
       </span>
       <ul class="itemizedlist">
        <li class="listitem">
         <span class="simpara">
          Connection::result_init()
         </span>
        </li>
       </ul>
       <span class="simpara">
        Zurücksetzen und Neuinitialisierung während:
       </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>Ja, aber die Elternmethode aufrufen!</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() (nur Metadaten)
         </span>
        </li>
        <li class="listitem">
         <span class="simpara">
          Statement::resultMetaData()
         </span>
        </li>
       </ul>
      </td>
     </tr>

     <tr>
      <td>Metadaten der Ergebnismenge (MYSQLND_RES_METADATA)</td>
      <td>Connection::result_meta_init()</td>
      <td>Ja, aber die Elternmethode aufrufen!</td>
      <td>Result::read_result_metadata()</td>
     </tr>

     <tr>
      <td>Anweisung (MYSQLND_STMT)</td>
      <td>Connection::stmt_init()</td>
      <td>Ja, aber die Elternmethode aufrufen!</td>
      <td>Connection::stmt_init()</td>
     </tr>

     <tr>
      <td>Netzwerk (MYSQLND_NET)</td>
      <td>mysqlnd_net_init()</td>
      <td>Nein</td>
      <td>Connection::init()</td>
     </tr>

     <tr>
      <td>Wire-Protokoll (MYSQLND_PROTOCOL)</td>
      <td>mysqlnd_protocol_init()</td>
      <td>Nein</td>
      <td>Connection::init()</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Es wird dringend empfohlen, einen Konstruktor nicht vollständig zu
   ersetzen. Die Konstruktoren führen Speicherzuweisungen durch, die für die
   API des <code class="literal">mysqlnd</code>-Plugins und die Objektlogik von
   <code class="literal">mysqlnd</code> unerlässlich sind. Wenn ein Entwickler die
   Warnungen ignoriert und darauf besteht, die Konstruktoren einzuhängen,
   sollte er zumindest den übergeordneten Konstruktor aufrufen, bevor er etwas
   mit dem Konstruktor tut.
  </p>
  <p class="simpara">
   Ungeachtet aller Warnungen kann es nützlich sein, Konstruktoren zu
   vererben. Konstruktoren sind der perfekte Ort, um die Funktionstabellen von
   Objekten zu ändern, die nicht gemeinsam genutzte Objekttabellen (z. B.
   Ergebnismenge, Netzwerk, Wire-Protokoll) haben.
  </p>
  <table id="mysqlnd.plugin.deststatus" class="doctable table">
   <caption><strong>Status des Destruktors</strong></caption>
   
    <thead>
     <tr>
      <th>Typ</th>
      <th>Muss die abgeleitete Methode die Elternmethode aufrufen?</th>
      <th>Destruktor</th>
     </tr>

    </thead>

    <tbody class="tbody">
     <tr>
      <td>Verbindung</td>
      <td>ja, nachdem die Methode ausgeführt wurde</td>
      <td>free_contents(), end_psession()</td>
     </tr>

     <tr>
      <td>Ergebnismenge</td>
      <td>ja, nachdem die Methode ausgeführt wurde</td>
      <td>free_result()</td>
     </tr>

     <tr>
      <td>Metadaten der Ergebnismenge</td>
      <td>ja, nachdem die Methode ausgeführt wurde</td>
      <td>free()</td>
     </tr>

     <tr>
      <td>Anweisung</td>
      <td>ja, nachdem die Methode ausgeführt wurde</td>
      <td>dtor(), free_stmt_content()</td>
     </tr>

     <tr>
      <td>Netzwerk</td>
      <td>ja, nachdem die Methode ausgeführt wurde</td>
      <td>free()</td>
     </tr>

     <tr>
      <td>Wire-Protokoll</td>
      <td>ja, nachdem die Methode ausgeführt wurde</td>
      <td>free()</td>
     </tr>

    </tbody>
   
  </table>

  <p class="simpara">
   Der Destruktor ist der geeignete Ort, um Ressourcen freizugeben, die von
   <code class="literal">mysqlnd_plugin_get_plugin_<span class="replaceable">&lt;object&gt;</span>_data()</code>-Eigenschaften
   belegt sind.
  </p>
  <p class="simpara">
   Die aufgeführten Destruktoren entsprechen nicht unbedingt der eigentlichen
   <code class="literal">mysqlnd</code>-Methode, die das Objekt selbst freigibt. Dennoch
   sind sie der bestmögliche Ort, um die Plugin-Daten einzuhängen und
   freizugeben. Wie bei den Konstruktoren können die Methoden vollständig
   ersetzt werden, was jedoch nicht empfohlen wird. Wenn in der obigen Tabelle
   mehrere Methoden aufgeführt sind, müssen alle aufgeführten Methoden
   eingehängt und die Plugin-Daten in der Methode freigegeben werden, die von
   <code class="literal">mysqlnd</code> zuerst aufgerufen wird.
  </p>
  <p class="simpara">
   Die empfohlene Methode für Plugins ist, einfach die Methoden einzuhängen,
   den Speicher freizugeben und unmittelbar danach die übergeordnete
   Implementierung aufzurufen.
  </p>
 </div><?php manual_footer($setup); ?>