Heim > Artikel > Backend-Entwicklung > Nutzung von PHP-Erweiterungsressourcen
Beschreiben Sie zunächst die Struktur des Typs {resource} im Kernel:
//Jede Ressource wird dadurch implementiert.
typedef struct _zend_rsrc_list_entry
{
void *ptr;
int refcount;
In der realen Welt müssen wir oft etwas Schlechtes tun Operationen Daten, die durch einen Skalarwert dargestellt werden, z. B. ein Handle auf eine Datei, sind lediglich ein Zeiger auf C.
#include
int main(void)
FILE *fd;
fd = fopen("/home/jdoe/.plan", "r"
fclose(fd);
return 0;
}
Der Dateideskriptor von stdio in der C-Sprache (Dateideskriptor) ist eine Variable, die jeder geöffneten Datei entspricht. Es handelt sich tatsächlich um einen Zeiger vom Typ FILE , die verwendet wird, wenn das Programm mit der Hardware interagiert. Wir können die Funktion fopen verwenden, um eine Datei zu öffnen und ein Handle zu erhalten. Dann müssen wir das Handle nur an Funktionen wie feof(), fread(), fwrite(), fclose() übergeben und können dann nachfolgende Operationen ausführen auf der Datei. Da diese Daten in der C-Sprache nicht direkt durch Skalardaten dargestellt werden können, wie können wir sie kapseln, um sicherzustellen, dass Benutzer sie auch in der PHP-Sprache verwenden können? Dies ist die Rolle von Ressourcentypvariablen in PHP! Daher ist es auch durch eine Zval-Struktur gekapselt. Die Implementierung des Ressourcentyps ist nicht kompliziert. Sein Wert ist eigentlich nur eine Ganzzahl. Basierend auf diesem Ganzzahlwert sucht der Kernel an einem Ort, der einem Ressourcenpool ähnelt, um die endgültigen erforderlichen Daten zu finden.
Verwendung von Ressourcentypvariablen
Ressourcentypvariablen werden auch in der Implementierung typdifferenziert! Um verschiedene Arten von Ressourcen zu unterscheiden, z. B. ein Dateihandle und einen MySQL-Link, müssen wir ihnen unterschiedliche Klassifizierungsnamen geben. Zuerst müssen wir diese Kategorie zum Programm hinzufügen. Dieser Schritt kann in MINIT durchgeführt werden:
#define PHP_SAMPLE_DESCRIPTOR_RES_NAME "Shanzhai-Dateideskriptor"
static int le_sample_descriptor;
ZEND_MINIT_FUNCTION(sample)
{
le_sample_descriptor = zend_register_list_destruct ors_ex(NULL, NULL, PHP_ SAMPLE_DESCRIPTOR_RES_NAME ,module_number);
return SUCCESS; void (*)(void *))pld, module_number);
ZEND_API int zend_register_list_destructors(void (*ld)(void *), void (*pld)(void *), int module_number);
ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number);
Als nächstes fügen wir die definierte MINIT-Stufenfunktion zum erweiterten module_entry hinzu Ersetzen Sie eine Zeile:
ZEND_MINIT(sample), /* MINIT */
Das Makro ZEND_MINIT_FUNCTION() wird verwendet, um uns bei der Definition der Funktion der MINIT-Stufe zu helfen, was wir bereits in Kapitel 1 beschrieben haben, aber wird in den Kapiteln 12 und 3 ausführlicher beschrieben. An dieser Stelle ist es wichtig zu wissen, dass die MINIT-Methode einmal ausgeführt wird, wenn Ihre Erweiterung zum ersten Mal geladen wird und bevor Anfragen eingegangen sind. Hier haben Sie diese Gelegenheit genutzt, um die NULL-Werte der Destruktorfunktionen zu registrieren, die Sie bald ändern werden ein Ressourcentyp, der künftig durch eine eindeutige Ganzzahl-ID bekannt ist. Wenn Sie die Funktion zend_register_list_destructors_ex() sehen, werden Sie sich bestimmt fragen, ob es auch eine Funktion zend_register_list_destructors() gibt? Ja, es gibt tatsächlich eine solche Funktion, deren Parameter weniger Ressourcenkategorienamen enthalten als die erstere. Was ist also der Unterschied zwischen den beiden?
eaco $re_1;
//resource(4) vom Typ (Nachahmer-Dateihandle)
echo $re_2;
//resource(4) vom Typ (Unbekannt)
Ressourcen erstellen
Wir haben oben einen neuen Ressourcentyp im Kernel registriert. Der nächste Schritt besteht darin, Ressourcenvariablen dieses Typs zu erstellen. Als nächstes implementieren wir einfach eine fopen-Funktion, die jetzt sample_open heißt:
PHP_FUNCTION(sample_fopen)
{
FILE *fp;
int filename_len, mode_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",&filename, &filename_len,&mode, &mode_len) == FAILURE)
{
RETURN_NULL(); }
if ( !filename_len ||. !mode_len)
{ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Ungültiger Dateiname oder Moduslänge");fp = fopen(filename, mode);
if (!fp)
{
php_error_docref(NULL TSRMLS_CC, E_WARNING,"Unable to open %s using mode %s",filename, mode); > RETURN_FALSE;
}
//Füge fp zum Ressourcenpool hinzu und markiere es als le_sample_descriptor-Typ.
ZEND_REGISTER_RESOURCE(return_value,fp,le_sample_descriptor);
}
Wenn Sie das Wissen in den vorherigen Kapiteln gelesen haben, sollten Sie erraten können, was die letzte Codezeile bewirkt. Es erstellt eine neue Ressource vom Typ le_sample_descriptor. Der Wert dieser Ressource ist fp. Außerdem wird diese Ressource zu einer HashTable hinzugefügt, in der Ressourcen gespeichert werden, und der entsprechende digitale Schlüssel dieser Ressource wird return_value zugewiesen.
Ressourcen sind nicht auf Dateihandles beschränkt. Wir können ein Stück Speicher beantragen und einen Zeiger darauf als Ressource verwenden. Ressourcen können also jeder Art von Daten entsprechen.
Ressourcen zerstören
Alles auf der Welt hat seine Höhen und Tiefen, Leben und Tod. Es ist Zeit für uns, darüber zu diskutieren, wie wir Ressourcen zerstören können. Die einfachste Möglichkeit besteht darin, fclose zu imitieren und eine Funktion „sample_close()“ zu schreiben, in der die Freigabe einer bestimmten {Ressource: sich speziell auf den Wert bezieht, der durch die Ressourcentypvariable von PHP dargestellt wird}.
Aber was passiert, wenn das benutzerseitige Skript eine Variable eines bestimmten Ressourcentyps über die Funktion unset() freigibt? Sie wissen nicht, dass sein Wert letztendlich einem FILE-Zeiger entspricht, daher können sie ihn nicht mit der Funktion fclose() freigeben. Dieses FILE-Handle bleibt wahrscheinlich im Speicher, bis das PHP-Programm aufhängt und vom Betriebssystem recycelt wird . Aber in einer normalen Webumgebung laufen unsere Server lange. Gibt es keine Lösung? Natürlich nicht, die Antwort liegt im NULL-Parameter, dem ersten und zweiten Parameter der Funktion zend_register_list_destructors_ex(), die wir oben aufgerufen haben, um einen neuen Ressourcentyp zu generieren. Beide Parameter stellen jeweils einen Callback-Parameter dar. Die erste Rückruffunktion wird ausgelöst, wenn die Ressourcenvariable des entsprechenden Typs im Skript freigegeben wird, z. B. wenn der Bereich endet oder unset() ist.
Die zweite Rückruffunktion wird für eine Ressource verwendet, die einem langen Linktyp ähnelt. Das heißt, diese Ressource bleibt nach ihrer Erstellung immer im Speicher vorhanden und wird nach Ende der Anforderung nicht freigegeben. Es wird aufgerufen, wenn der Webserverprozess beendet wird, was einem Aufruf durch den Kernel während der MSHUTDOWN-Phase entspricht. Bezüglich persistenter Ressourcen werden wir diese im nächsten Abschnitt ausführlich besprechen.
Lassen Sie uns zunächst die erste Callback-Funktion definieren.
static void php_sample_descriptor_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
FILE *fp = (FILE*)rsrc->ptr;
fclose(fp);
Dann verwenden Sie es Ersetzen Sie den ersten Parameter der Funktion zend_register_list_destructors_ex () mit Null:
le_Sampel_Descriptor = Zend_register_List_Destructors_ex (
php_sample_descriptor_dtor,
null,
php_scress_scriptor_res_res_name; gets Eine Ressourcenvariable des oben genannten Typs wird abgerufen, wenn sie nicht gesetzt ist oder wenn der Bereich vom Kernel freigegeben wird, wird der zugrunde liegende php_sample_descriptor_dtor vom Kernel aufgerufen, um ihn vorzuverarbeiten. Auf diese Weise scheinen wir die Funktion „sample_close()“ überhaupt nicht zu benötigen!
$fp = sample_fopen("/home/jdoe/notes.txt", "r");
unset($fp);
unset($ Nachdem fp) ausgeführt wurde, ruft der Kernel automatisch die Funktion php_sample_descriptor_dtor auf, um einige dieser Variablen entsprechende Daten zu bereinigen. Natürlich ist die Sache definitiv nicht so einfach. Behalten wir diese Frage im Hinterkopf und lesen Sie weiter.
Ressourcen dekodieren
Wir vergleichen Ressourcenvariablen mit Lesezeichen, aber wenn es nur Lesezeichen gibt, hat das absolut keine Auswirkung! Wir müssen die entsprechende Seite über Lesezeichen finden. Für Ressourcenvariablen müssen wir in der Lage sein, die entsprechenden endgültigen Daten darüber zu finden!
ZEND_FUNCTION(sample_fwrite)
{ FILE *fp;
char *data;
int data_len;
if (zend_parse_parameters(ZEND_NUM_AR GS() TSRMLS_CC, "rs ",&file_resource, &data, &data_len) == FAILURE )
{
RETURN_NULL();
}
/* Verwenden Sie zval*, um den Ressourcentyp zu überprüfen und
* seinen Zeiger abzurufen die Nachschlagetabelle */
ZEND_FETCH_RESOURCE(fp,FILE*,&file_resource,-1,PHP_SAMPLE_DESCRIPTOR_RES_NAME,le_sample_descriptor); */
RETURN_LONG(fwrite(data, 1, data_len, fp));
Der r-Platzhalter in den zend_parse_parameters( ) stellt die Variable dar, die den Ressourcentyp empfängt, und ihr Träger ist ein zval *. Dann werfen wir einen Blick auf die Makrofunktion ZEND_FETCH_RESOURCE().
#define ZEND_FETCH_RESOURCE(rsrc, rsrc_type, Passed_id,default_id, resources_type_name, resources_type)
rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC,default_id, resources_type_name, NULL,1, resources_type);
ZEND_VER IFY_RESOURCE( rsrc ) ;
//In unserem Beispiel sieht es so aus:
fp = (FILE*) zend_fetch_resource(&file_descriptor TSRMLS_CC, -1,PHP_SAMPLE_DESCRIPTOR_RES_NAME, NULL,1, le_sample_descriptor);
if ( !fp)
{
RETURN_FALSE;
}
zend_fetch_resource() ist eine Kapselungsebene von zend_hash_find(), die nach dem Finden nach verschiedenen {Ressourcen} sucht Um die endgültigen erforderlichen Daten zu erhalten, verwenden wir die Makrofunktion ZEND_VERIFY_RESOURCE(), um die Daten zu überprüfen. Aus dem obigen Code können wir ersehen, dass NULL und 0 nicht als Ressource verwendet werden können. Im obigen Beispiel ruft die Funktion zend_fetch_resource() zunächst den durch le_sample_descriptor dargestellten Ressourcentyp ab. Wenn die Ressource nicht existiert oder der empfangene zval keine Ressourcentypvariable ist, gibt sie NULL zurück und löst die entsprechende Fehlermeldung aus. Die letzte Makrofunktion ZEND_VERIFY_RESOURCE() kehrt automatisch zurück, wenn ein Fehler erkannt wird, sodass wir uns von der Fehlererkennung lösen und uns mehr auf die Hauptlogik des Programms konzentrieren können. Nachdem wir nun die entsprechende DATEI* erhalten haben, schreiben wir mit fwrite() einige Daten hinein! .
Um zu vermeiden, dass zend_fetch_resource() bei einem Fehler einen Fehler generiert, übergeben Sie einfach NULL für den Parameter „resource_type_name“. Ohne eine aussagekräftige Fehlermeldung schlägt zend_fetch_resource() stillschweigend fehl.
Wir können stattdessen auch eine andere Methode verwenden die Daten, die wir letztendlich wollen.
ZEND_FUNCTION(sample_fwrite)
{
FILE *fp;
char *data;
if (zend_parse _parameters ( ZEND_NUM_ARGS () TSRMLS_CC, "RS", & File_Resource, & Data, & Data_Len) == Fehler) {
Return_null ();
}
fp = (file*) Zend_list_find ( Z_resval_p (file_resource) , & rsrc_type);
if (!fp || rsrc_type != le_sample_descriptor) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,"Ungültige Ressource bereitgestellt");
RETURN_LONG (fwrite(data, 1, data_len, fp));
}
Sie können nach Ihren eigenen Gewohnheiten auswählen, welches Formular Sie verwenden möchten, es wird jedoch empfohlen, die Makrofunktion ZEND_FETCH_RESOURCE() zu verwenden.
Zerstörung erzwingen
Wir haben oben immer noch eine ungelöste Frage, das heißt, ist unset($fp) ähnlich dem, was wir oben implementiert haben, wirklich allmächtig? Natürlich nicht, schauen Sie sich den folgenden Code an:
$fp = sample_fopen("/home/jdoe/world_domination.log", "a"); 🎜 > unset($fp);
?>
Dieses Mal teilen sich $fp und $evil_log ein zval, aber sein zval wird nicht freigegeben, da $evil_log noch verwendet wird. Mit anderen Worten, das durch $evil_log dargestellte Dateihandle ist weiterhin beschreibbar! Um diese Art von Fehler zu vermeiden, müssen wir ihn also unbedingt manuell schließen! Die Funktion sample_close() muss vorhanden sein!
PHP_FUNCTION(sample_fclose)
{ FILE *fp;
zval *file_resource; 🎜 > RETURN_NULL();
/* Es ist zwar nicht notwendig, die
* DATEI abzurufen * Ressource, das Durchführen des Abrufs bietet
* eine Möglichkeit, zu überprüfen, ob wir * den richtigen Ressourcentyp schließenZEND_FETCH_RESOURCE(fp, FILE*, &file_resource, -1,PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor);
RETURN_TRUE;
}
Dieser Löschvorgang zeigt erneut, dass die Ressourcendaten in der HashTable gespeichert sind . Obwohl wir diese HashTable, die Ressourcen speichert, über Funktionen wie zend_hash_index_find() oder zend_hash_next_index_insert() betreiben können, ist dies niemals eine gute Idee, da PHP in nachfolgenden Versionen möglicherweise die Implementierung dieses Teils ändert. Die obige Methode funktioniert daher nicht Für eine bessere Kompatibilität verwenden Sie bitte Standardmakrofunktionen oder API-Funktionen. Wenn wir Daten in der HashTable von EG (regular_list) löschen, wird eine Dtor-Funktion zurückgerufen, die die entsprechende Dtor-Funktionsimplementierung entsprechend der Kategorie der Ressourcenvariablen aufruft, die der erste Parameter ist, wenn wir die Funktion zend_register_list_destructors_ex() aufrufen.
An vielen Stellen werden wir eine Makrofunktion zend_list_delete() sehen, die speziell zum Löschen verwendet wird, da sie den Referenzzähler der Ressourcendaten selbst berücksichtigt.