Dieser Abschnitt bietet einen Überblick über die Plugin-Architektur von
   mysqlnd.
  
Überblick über den MySQL Native Driver
   Vor der Entwicklung eines mysqlnd-Plugins ist es
   nützlich, ein wenig über den Aufbau von mysqlnd selbst
   zu wissen. Mysqlnd besteht aus den folgenden Modulen:
  
| Modul-Statistiken | mysqlnd_statistics.c | 
|---|---|
| Datenbankverbindung | mysqlnd.c | 
| Ergebnismenge | mysqlnd_result.c | 
| Metadaten der Ergebnismenge | mysqlnd_result_meta.c | 
| Anweisung | mysqlnd_ps.c | 
| Netzwerk | mysqlnd_net.c | 
| Wire-Protokoll | mysqlnd_wireprotocol.c | 
Objektorientiertes Paradigma in C
   Auf der Code-Ebene verwendet mysqlnd ein C-Pattern, um
   die Objektorientierung zu implementieren.
  
   In C wird ein Objekt mittels struct beschrieben. Die
   Mitglieder dieser Struktur sind die Eigenschaften des Objekts. Die
   Strukturmitglieder, die auf Funktionen verweisen, stellen die Methoden dar.
  
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.
Der PHP-Lebenszyklus
Der Lebenszyklus von PHP besteht aus 2 grundlegenden Zyklen:
Der Start- und Shutdown-Zyklus der PHP-Engine
Der Zyklus einer Anfrage
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.
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.
Wie ein Plugin funktioniert
   Ein mysqlnd-Plugin funktioniert, indem es die Aufrufe an
   mysqlnd abfängt, die von Erweiterungen stammen, die
   mysqlnd verwenden. Dies wird dadurch erreicht, dass die
   Funktionstabelle von mysqlnd abgerufen, gesichert und
   durch eine eigene Funktionstabelle ersetzt wird, die die Funktionen des
   Plugins nach Bedarf aufruft.
  
   Der folgende Code zeigt, wie die Funktionstabelle von
   mysqlnd ersetzt wird:
  
/* 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(&org_methods, current_methods,
    sizeof(struct st_mysqlnd_conn_methods);
  /* Installation der neuen Methoden */
  current_methods->query = MYSQLND_METHOD(my_conn_class, query);
}
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.
Hinweis:
Bei der Bearbeitung der Funktionstabelle von
mysqlndsollte 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.
Aufrufen von Elternmethoden
Wenn die ursprüngliche Funktionstabelle gesichert wird, können die ursprünglichen Einträge - die Elternmethoden - weiterhin aufgerufen werden.
   In einigen Fällen, z. B. bei Connection::stmt_init(), ist
   es unerlässlich, die Elternmethode aufzurufen, bevor weitere Aktionen in
   der abgeleiteten Methode erfolgen.
  
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); /* return with call to parent */
}
Erweitern von Eigenschaften
   Ein mysqlnd-Objekt wird durch eine C-Struktur (struct)
   dargestellt. Es ist nicht möglich, einer C-Struktur zur Laufzeit ein
   Mitglied hinzuzufügen. Benutzer von mysqlnd-Objekten
   können nicht einfach Eigenschaften zu den Objekten hinzufügen.
  
   Beliebige Daten (Eigenschaften) können zu einem
   mysqlnd-Objekt hinzugefügt werden, indem eine geeignete
   Funktion der
   mysqlnd_plugin_get_plugin_<object>_data()-Familie
   verwendet wird. Wenn ein Objekt zugewiesen wird, reserviert
   mysqlnd am Ende des Objekts Speicherplatz für einen
   void *-Zeiger auf beliebige Daten.
   mysqlnd reserviert den Platz für einen
   void *-Zeiger pro Plugin.
  
Die folgende Tabelle zeigt, wie die Position des Zeigers für ein bestimmtes Plugin berechnet werden kann:
| Speicheradresse | Inhalt | 
|---|---|
| 0 | Beginn der C-Struktur des mysqlnd-Objekts | 
| n | Ende der C-Struktur des mysqlnd-Objekts | 
| n + (m x sizeof(void*)) | void* zu den Objektdaten des m-ten Plugins | 
   Wenn geplant ist, Unterklassen für einen der Konstruktoren des
   mysqlnd-Objekts zu erstellen, was erlaubt ist, muss dies
   unbedingt bedacht werden!
  
Der folgende Code zeigt, wie Eigenschaften erweitert werden:
/* 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->persistent);
    (*props)->query_counter = 0;
  }
  return props;
}
Der Plugin-Entwickler ist für die Verwaltung des Speichers verantwortlich, der für die Plugin-Daten verwendet wird.
   Es wird empfohlen, für die Daten von Plugins den Speicherallokator von
   mysqlnd zu verwenden. Diese Funktionen werden nach dem
   Schema mnd_*loc() benannt. Der
   mysqlnd-Allokator hat einige nützliche Eigenschaften,
   zum Beispiel die Möglichkeit, einen Debug-Allokator in einem
   Nicht-Debug-Build zu benutzen.
  
| Wann soll die Unterklasse erstellt werden? | Hat jede Instanz ihre eine eigene Funktionstabelle? | Wie wird die Unterklasse erstellt? | |
|---|---|---|---|
| Verbindung (MYSQLND) | MINIT | Nein | mysqlnd_conn_get_methods() | 
| Ergebnismenge (MYSQLND_RES) | MINIT oder später | Ja | mysqlnd_result_get_methods() oder Manipulation der Funktionstabelle der Objektmethoden | 
| Metadaten der Ergebnismenge (MYSQLND_RES_METADATA) | MINIT | Nein | mysqlnd_result_metadata_get_methods() | 
| Anweisung (MYSQLND_STMT) | MINIT | Nein | mysqlnd_stmt_get_methods() | 
| Netzwerk (MYSQLND_NET) | MINIT oder später | Ja | mysqlnd_net_get_methods() oder Manipulation der Funktionstabelle der Objektmethoden | 
| Wire-Protokoll (MYSQLND_PROTOCOL) | MINIT oder später | Ja | mysqlnd_protocol_get_methods() oder Manipulation der Funktionstabelle der Objektmethoden | 
Nach MINIT darf die Funktionstabelle nicht mehr manipuliert werden, wenn es nach der obigen Tabelle nicht erlaubt ist.
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.
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.
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.
| Typ | Zuweisung, Konstruktion, Zurücksetzen | Kann geändert werden? | Aufgerufen von | 
|---|---|---|---|
| Verbindung (MYSQLND) | mysqlnd_init() | Nein | mysqlnd_connect() | 
| Ergebnismenge (MYSQLND_RES) | Zuweisung: 
 Zurücksetzen und Neuinitialisierung während: 
 | Ja, aber die Elternmethode aufrufen! | 
 | 
| Metadaten der Ergebnismenge (MYSQLND_RES_METADATA) | Connection::result_meta_init() | Ja, aber die Elternmethode aufrufen! | Result::read_result_metadata() | 
| Anweisung (MYSQLND_STMT) | Connection::stmt_init() | Ja, aber die Elternmethode aufrufen! | Connection::stmt_init() | 
| Netzwerk (MYSQLND_NET) | mysqlnd_net_init() | Nein | Connection::init() | 
| Wire-Protokoll (MYSQLND_PROTOCOL) | mysqlnd_protocol_init() | Nein | Connection::init() | 
   Es wird dringend empfohlen, einen Konstruktor nicht vollständig zu
   ersetzen. Die Konstruktoren führen Speicherzuweisungen durch, die für die
   API des mysqlnd-Plugins und die Objektlogik von
   mysqlnd 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.
  
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.
| Typ | Muss die abgeleitete Methode die Elternmethode aufrufen? | Destruktor | 
|---|---|---|
| Verbindung | ja, nachdem die Methode ausgeführt wurde | free_contents(), end_psession() | 
| Ergebnismenge | ja, nachdem die Methode ausgeführt wurde | free_result() | 
| Metadaten der Ergebnismenge | ja, nachdem die Methode ausgeführt wurde | free() | 
| Anweisung | ja, nachdem die Methode ausgeführt wurde | dtor(), free_stmt_content() | 
| Netzwerk | ja, nachdem die Methode ausgeführt wurde | free() | 
| Wire-Protokoll | ja, nachdem die Methode ausgeführt wurde | free() | 
   Der Destruktor ist der geeignete Ort, um Ressourcen freizugeben, die von
   mysqlnd_plugin_get_plugin_<object>_data()-Eigenschaften
   belegt sind.
  
   Die aufgeführten Destruktoren entsprechen nicht unbedingt der eigentlichen
   mysqlnd-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
   mysqlnd zuerst aufgerufen wird.
  
Die empfohlene Methode für Plugins ist, einfach die Methoden einzuhängen, den Speicher freizugeben und unmittelbar danach die übergeordnete Implementierung aufzurufen.
