Heim > Backend-Entwicklung > PHP-Tutorial > Schreiben der PHP-Erweiterung 1: Einführung in PHP und Zend

Schreiben der PHP-Erweiterung 1: Einführung in PHP und Zend

WBOY
Freigeben: 2016-07-30 13:29:34
Original
1062 Leute haben es durchsucht
Extension Writing Teil I: Einführung in PHP und Zend

Übersetzung: Ich habe es in der Welt verloren

Ursprüngliche Adresse : http://devzone.zend.com/303/extension-writing-part-i-introduction-to-php-and-zend/

Blog-Adresse: http://lg.uuhonghe .com/index/view?id=3

Einführung

Was ist Erweiterung

Lebenszyklus

Hallo Welt

Erstellen Sie Ihre eigene Erweiterung

INI-Konfiguration

Globale Variablen

Ini als globale Variable festlegen

Integritätsprüfung

Dann?

Einführung

Wenn Sie dieses Tutorial lesen, könnten Sie daran interessiert sein, Erweiterungen in der PHP-Sprache zu schreiben. Wenn nicht. . . Vielleicht werden Sie nach der Lektüre feststellen, dass Sie sich für etwas interessieren, das Sie vorher nicht wussten.

In diesem Artikel wird davon ausgegangen, dass der Leser über ein grundlegendes Verständnis der PHP-Sprache und des in C geschriebenen PHP-Interpreters verfügt.

Lassen Sie uns zunächst bestätigen, warum Sie eine PHP-Erweiterung schreiben möchten:

1. Aufgrund der Abstraktion von Der Sprachkern Depth ermöglicht Ihnen einige Bibliotheken und Systemaufrufe, die nicht direkt mit PHP ausgeführt werden können.

2. Sie möchten, dass PHP sein eigenes Verhalten auf ungewöhnliche Weise implementiert.

3. Sie haben eine Menge PHP-Code geschrieben, wissen aber, dass dieser schneller ausgeführt werden kann.

4. Sie haben Code, der eine besonders clevere Idee umsetzt, die Sie verkaufen möchten, aber was noch wichtiger ist, der Code, den Sie verkaufen möchten, muss lauffähig sein, kann es aber nicht sein im Quellcode zu sehen.

Das sind alles sehr legitime Gründe, aber um eine Erweiterung zu erstellen, muss man zunächst verstehen, was eine Erweiterung ist.

Was ist eine Erweiterung?

Wenn Sie jemals PHP geschrieben haben, müssen Sie Erweiterungen verwendet haben. Mit nur wenigen Erweiterungen befindet sich die gesamte User-Space-Funktionalität in PHP in der Funktionsgruppe dieser oder jener Erweiterung. Ein Großteil dieser Funktionen ist Bestandteil der Standarderweiterungen – insgesamt über 400. Im PHP-Quellcode sind 86 Erweiterungen gebündelt, jede mit durchschnittlich etwa 30 Funktionen. Nach Zählung gibt es insgesamt etwa 2500 Funktionen. Wem das noch nicht reicht, dem stehen im PECL-Repository mehr als 100 zusätzliche Erweiterungen zur Verfügung, und weitere können an anderer Stelle online gefunden werden.

„Diese Funktionen sind alle in Erweiterungen, also was sonst?“ Sie fragen sich vielleicht: „Was erweitern sie?“ Was ist der Kern von PHP?

Der Kern von PHP besteht aus zwei Teilen. Unten finden Sie die Zend Engine (kurz ZE). ZE analysiert vom Menschen erkennbare Skripte in maschinenerkannte Symbole und führt diese Symbole im Prozessraum aus. ZE übernimmt gleichzeitig die Speicherverwaltung, Variablenfelder und Funktionsaufrufe. Ein weiterer Teil dieser Unterscheidung ist der PHP-Kern. Der PHP-Kernel kümmert sich um Kommunikation, Verbindungen und die SAPI-Schicht (Server Application Programming Interface, oft auch für Host-Umgebungen wie Apache, IIS, CLI, CGI verwendet). usw.) bietet außerdem eine einheitliche Erkennung von Safe_Mode und Open_basedir in der Kontrollschicht sowie eine Streaming-Schicht im Zusammenhang mit den User-Space-Funktionen fopen(), fread() und fwrite() für Datei- und Netzwerk-E/A.

Lebenszyklus

Wenn eine SAPI startet, zum Beispiel in /usr/local/apache/bin/apachectl start Als Reaktion auf beginnt PHP mit der Initialisierung seines Kernel-Subsystems. Am Ende dieses Bootvorgangs wird der Kernel jeder Erweiterung geladen und ihre Modulinitialisierungsroutine (MINIT) aufgerufen.

Dies gibt jeder Erweiterung die Möglichkeit, interne Variablen zu initialisieren, Ressourcen zuzuweisen, Ressourcenhandles zu registrieren und ihre Funktionen bei ZE zu registrieren, sodass ZE weiß, welche Funktion ausgeführt werden soll, wenn das Skript diese Funktionen aufruft Stück Code.

Als nächstes wartet PHP darauf, dass die SAPI-Schicht eine Seite zur Verarbeitung anfordert. Bei CGI oder CLI SAPI geschieht dies direkt und nur einmal. In Apache, IIS oder einem anderen ausgereiften Webserver-SAPI geschieht dies, wenn der Remote-Benutzer dies anfordert, und kann mehrmals passieren, möglicherweise mit Parallelität. Unabhängig davon, wie die Anfrage eintrifft, weist PHP zunächst ZE an, eine Umgebung für die Ausführung des Skripts einzurichten, und ruft dann die Anfrageinitialisierungsfunktion (RINI) jeder Erweiterung auf. RINI gibt Erweiterungen die Möglichkeit, ihre eigenen spezifischen Umgebungsvariablen einzurichten, Anforderungen für bestimmte Ressourcen zuzuweisen oder andere Aufgaben wie Audits auszuführen. RINIEin Paradebeispiel für Funktionsverhalten sind Sitzungen Wenn in der Erweiterung das Element session.auto_start aktiviert ist, löst RINI automatisch die User-Space-Funktion session_start() und die Voreinstellung $_SESSIONVariablen.

Sobald die Anfrage initialisiert ist, übernimmt ZE die Übersetzung des PHP-Skripts in Token und schließlich in Opcodes, die in einem Schritt debuggt und ausgeführt werden können.

Wenn eine in einem der Opcodes enthaltene Erweiterungsmethode aufgerufen wird, bündelt ZE die Parameter der Methode und übergibt vorübergehend die Kontrolle an die direkte Methodenvervollständigung.

Nachdem das Skript abgeschlossen ist, ruft PHP die Funktion zum Herunterfahren jeder erweiterten Anforderung (RSHUTDOWN) auf, um die letzten Aufräumarbeiten durchzuführen (z. B. das Sicherstellen von Sitzungsvariablen). Scheibe). Als nächstes führt ZE einen Bereinigungsprozess (Garbage Collection genannt) aus, der die Operation unset() effektiv für jede Variable ausführt, die im vorherigen Teil der Anfrage verwendet wurde.


Nachdem der Vorgang abgeschlossen ist, wartet PHP darauf, dass SAPI ein weiteres Dokument anfordert oder das Signal geschlossen wird. Bei CGI- und CLI-SAPI gibt es keine „nächste Anfrage“, daher startet die SAPI den Shutdown-Prozess direkt. Während des Herunterfahrvorgangs durchläuft PHP jede Erweiterung erneut, ruft die Funktion zum Herunterfahren des Moduls (MSHUTDOWN) auf und fährt schließlich sein eigenes Kernel-Subsystem herunter.

Das Gesagte mag einschüchternd klingen, aber wenn Sie beginnen, eine funktionierende Erweiterung zu entwickeln, wird Ihnen einiges davon nach und nach klar.

Speicherverwaltung

Um den Verlust von schlecht geschriebenem Erweiterungsspeicher zu verhindern, ZE führt seinen internen Speichermanager mit einem zusätzlichen Flag aus, das die Persistenz anzeigt. Die kontinuierliche Zuweisung ist wichtig, um sicherzustellen, dass die Speicherzuweisung länger dauert als eine Seitenanforderung. Im Gegensatz dazu wird eine nicht persistente Zuweisung am Ende der Anforderung, für die sie zugewiesen wurde, freigegeben, unabhängig davon, ob die Freigabefunktion aufgerufen wird. Benutzerbereichsvariablen werden beispielsweise nicht dauerhaft zugewiesen, wenn sie nach Ende der Anforderung nicht mehr verwendet werden.

Möglicherweise würde eine Erweiterung jedoch theoretisch darauf basieren, dass ZE am Ende der Seitenanforderung automatisch nicht persistenten Speicher freigibt, was nicht empfohlen wird. Die Speicherzuweisung führt zu einem längeren Zeitraum des Nichtrecyclings, es ist unwahrscheinlich, dass speicherbezogene Ressourcen zum richtigen Zeitpunkt geschlossen werden, und wenn keine Aufräumarbeiten durchgeführt werden, wird diese Arbeit zu einem Chaos. Wie Sie später sehen werden, ist es einfach, sicherzustellen, dass die zugewiesenen Daten rechtzeitig bereinigt werden. Vergleichen wir kurz die traditionelle Speicherzuweisung (muss bei Verwendung externer Bibliotheken verwendet werden) und die Persistenz in PHP/ZE.

Traditional Non-Persistent Persistent
malloc(count)
calloc(count, num)
emalloc(count)
ecalloc(count, num)
pemalloc(count, 1)*
pecalloc(count, num, 1)
strdup(str)
strndup(str, len)
estrdup(str)
estrndup(str, len)
pestrdup(str, 1)
pemalloc() & memcpy()
free(ptr) efree(ptr) pefree(ptr, 1)
realloc(ptr, newsize) erealloc(ptr, newsize) perealloc(ptr, newsize, 1)
malloc(count * num + extr)** safe_emalloc(count, num, extr) safe_pemalloc(count, num, extr)
 

* pemalloc()pemalloc() Familie enthält a 'persistent'-Flag, damit sie ihren nicht persistenten Teilen entsprechen. Zum Beispiel:

                                                                      safe_emalloc(1234) und safe_pemalloc(1234, 0)safe_pemalloc(1234, 0) Das Gleiche.

** safe_emalloc()safe_emalloc() und safe_pemalloc()safe_pemalloc()

( existieren PHP5) fügt eine Prüfung hinzu, um einen Ganzzahlüberlauf zu vermeiden.

Aufbau einer Entwicklungsumgebung


Nachdem wir nun etwas über die Theorie hinter der Funktionsweise von PHP und der Zend Engine gelernt haben, schätze ich Sie möchte mehr lernen. Seine Fähigkeiten haben begonnen, sich zu entwickeln. Bevor Sie beginnen, müssen Sie einige notwendige Arbeiten zusammentragen, um eine Entwicklungsumgebung einzurichten, die Ihren Anforderungen entspricht.

Zunächst benötigen Sie PHP selbst, da diese Reihe von Entwicklungstools untrennbar mit PHP verbunden ist. Wenn Sie mit der Verwendung von Quellcode zum Erstellen von PHP nicht vertraut sind, empfehle ich Ihnen, zuerst diesen Artikel zu lesen: http://www.php.net/install.unix. (Der Artikel über die Entwicklung von PHP-Erweiterungen unter Windows folgt später) . Obwohl es aus Sicherheitsgründen sehr verlockend ist, die in Ihrer Linux-Distribution enthaltenen Binärpakete zu verwenden, fehlen ihnen zwei ./configure-Optionen, die während der Entwicklung sehr praktisch sind. Der erste ist --enable-debug --enable-experimental-zts. --enable-maintainer-zts Diese Option bewirkt, dass PHP beim Kompilieren ausführbare Symbolinformationen hinzufügt, sodass Sie bei Auftreten eines Segfault diese aus dem Kernel-Speicher abrufen und mit gdb nachverfolgen können, wo und warum der Segfault aufgetreten ist. Eine weitere Option hängt von der PHP-Version ab, mit der Sie entwickeln. Diese Option heißt in PHP 4.3 und in PHP 5 oder höher. Diese Priorität ermöglicht es PHP, über seine Vorgänge in einer Multithread-Umgebung nachzudenken, sodass Sie einige häufige Ausnahmen abfangen können, die in einer Umgebung ohne Threads harmlos sind und dazu führen, dass Ihre Erweiterung in einer Umgebung mit Multithreads unbrauchbar wird. Solange Sie PHP mit diesen zusätzlichen Optionen kompiliert haben, die auf Ihrem Entwicklungsserver (oder Ihrer Workstation) installiert sind, können Sie mit dem Schreiben Ihrer ersten Erweiterung beginnen.


                                                                                                             Abgeschlossen. Daher werden wir im Folgenden nur eine Erweiterung einer Funktion vornehmen, die die Zeichenfolge „Hello World“ zurückgibt. Im PHP-Code können Sie so schreiben:


Jetzt verwenden wir die PHP-Erweiterung, um diesen Code zu implementieren. Zuerst erstellen wir ein Verzeichnis
im Verzeichnis ext/ PHP-Quellcode Hallo

und geben Sie das Verzeichnis ein. Tatsächlich kann sich dieses Verzeichnis innerhalb oder außerhalb des PHP-Verzeichnisbaums befinden, aber ich lasse Sie es hier platzieren, damit ich später ein unabhängiges Konzept demonstrieren kann. In diesem Verzeichnis müssen drei Dateien erstellt werden: eine Quelldatei, die die Methode
<?php
function hello_word(){
    return 'Hello World';
}
?>
Nach dem Login kopieren

enthält, eine Header-Datei mit Referenzen für PHP zum Laden Ihrer Erweiterung und eine Header-Datei für phpize Konfigurationsdatei zur Vorbereitung von Erweiterungen für die Kompilierung. hello_worldconfig.m4


php_hello.h

PHP_ARG_ENABLE(hello, whether to enable Hello World support, [--enable-hello Enable Hello World support])

if test "$PHP_HELLO" = "yes"; then
    AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
    PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi
Nach dem Login kopieren

hello.c

#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1

#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_FUNCTION(hello_world);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif
Nach dem Login kopieren

Es ist ersichtlich, dass der größte Teil des Codes in der obigen Beispielerweiterung eine Kleberprotokollsprache ist, die verwendet wird, um die Erweiterung in PHP einzuführen und einen Dialog herzustellen Lass sie kommunizieren. Nur die letzten 4 Codezeilen können als „echter Code“ bezeichnet werden und werden zur Ausführung von Aufgaben verwendet, mit denen Skripte der Benutzerraumschicht interagieren können. Tatsächlich sieht der Code auf dieser Ebene dem zuvor betrachteten PHP-Code sehr ähnlich und ist leicht zu verstehen:

        1、声明一个方法  hello_world

        2、让这个方法返回一个字符串:"Hello World"

        3、。。呃。。。1?这个1几个意思?

    回忆一下,ZE包含了一套复杂的内存管理层,能确保分配的资源在脚本退出时被释放。然而在内存管理的掌控下,释放同样的块两次是非常大的禁忌。这种行为通常称为"double freeing",是一个常见的段错误的原因,涉及到一个正在调用的程序访问一个不再属于它的内存块。同样的,你不希望允许ZE去释放一个存活于程序空间并且其数据块被其他进程占用的静态字符串buffer(例如我们示例中的"Hello World")。  RETURN_STRING()能够假设任何一个传递给它的字符串都被复制过以便可以安全的释放;但是因为在内核函数里分配内存给一个字符串不太常见,所以动态的填充它,然后返回,  RETURN_STRING()允许用户指定是不是必须复制字符串。为了进一步说明这个概念,下面这个代码片段作用和上面相应部分一样:

PHP_FUNCTION(hello_world)
{
    char *str;
    str = estrup("hello World");
    RETURN_STRING(str, 0);
}
Nach dem Login kopieren

    在此版本中,我们手动地分配内存给"Hello World"这个串,并且最终传回调用脚本,然后把内存传给 RETURN_STRING()第二个参数值为0表示不需要复制一份,可以直接使用传递过来的。

    编译你的扩展

    本练习的最后一步就是将你的扩展编译为一个动态可加载模块。如果你把上面的示例原封不动的抄下来,那么只需要在ext/hello/目录下运行下面三步命令就行:

phpize
./configure --enable-hello

(译者注:如果编译PHP的时候使用了 --prefix 参数,此处要加上 --with-php-config 选项,
如笔者编译PHP时使用的是 ./configure --prefix=/use/local/phpdev/ 此处命令应使用
./configure --enable-hello --with-php-c/local/phpdev/bin/php-config)

make
Nach dem Login kopieren

    运行完上述三个命令之后,你应该在ext/hello/modules/下找到一个 hello.so 文件。(译者注:如果在make时报错: error: unknown type name 'function_entry' ,可以把 'function_entry' 改为 'zend_function_entry',参见:https://bugs.php.net/bug.php?id=61479  )。现在,就像其他PHP扩展一样,你可以把你的扩展拷贝到扩展目录(默认为,/usr/local/lib/php/extensions/,可以通过php.ini确认)然后在php.ini里加上extension=hello.so一行可以在以触发它在程序启动时被加载到了。对于CGI/CLI SAPIs 来说,启动就指下一次运行PHP;而对我web server SAPIs如Apache来说,启动指下次web server重启。让我们试下运行下面命令:

$ php -r 'echo hello_world();'
Nach dem Login kopieren

    如果一切顺利,你现在应该能看到这段代码输出"Hello World"了,因为你的扩展里的"hello_world()"返回了一个字符串"Hello World",而echo命令会原封不动地显示传递给他的参数(此处即为该函数的返回值)。

    其他标量也可用类似的方式返回,使用RETURN_LONG() 返回整型, RETURN_DOUBLE() 返回浮点数, RETURN_BOOL() 返回true/false值,RETURN_NULL() 你猜到了,返回 NULL 值。让我们来逐行分析一下在hello.cfunction_entry 结构体下通过 PHP_FE() 添加的行和文件末尾的那些PHP_FUNCTION()都做了些什么。

static function_entry hello_functions[] =
{
    PHP_FE(hello_world, NULL)
    PHP_FE(hello_long, NULL)
    PHP_FE(hello_double, NULL)
    PHP_FE(hello_bool, NULL)
    PHP_FE(hello_null, NULL)
    {NULL, NULL, NULL}
};

PHP_FUNCTION(hello_long)
{
    RETURN_LONG(42);
}

PHP_FUNCTION(hello_double)
{
    RETURN_DOUBLE(3.1415926535);
}

PHP_FUNCTION(hello_bool)
{
    RETURN_BOOL(1);
}

PHP_FUNCTION(hello_null)
{
    RETURN_NULL();
}
Nach dem Login kopieren

    你同样需要在头文件php_hello.h hello_world() 的旁边添加这些方法的原型,拿得创建过程能正确执行:

PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);
Nach dem Login kopieren

    因为你没有修改config.m4文件,所以技术上这次跳过phpize和./configure这两个步骤直接make是安全的。然后,在本游戏的这个阶段,我还是要求你从头把三个步骤都执行一遍以确认活干得漂亮。另外,最后一步的时候,你应该执行make clean all而不是简单的执行make,来确保所有源文件重建。再次声明,现在的改动上述这些步骤是非必须的,但是会更安全更清晰。模块建好后,再拷贝到你的扩展目录下,替换旧版本。

    这个时候你可以再次调用你的PHP解析器,运行一段简单的脚本来试试你刚刚添加的方法。事实上,为何不现在不试呢?我等着呢。。。

    试完了?很好。如果你使用 var_dump() 而不是 echo 来查看每个函数的返回值你也许会发现"hello_bool()"返回true。这就是值1在"RETURN_BOOL()"所表示的。就像在PHP脚本里,整型值0 等于 FALSE, 但是其他所有的整形值都等于 TRUE扩展作者经常使用"1"作这一系列的约定,我们希望你也这样,但是也不必拘泥于此。为了更具可读性, RETURN_TRUE 和  RETURN_FALSE 两个宏也可使用;现在再次修改"hello_bool()",这次使用"RETURN_TRUE":

PHP_FUNCTION(hello_bool){
    RETURN_TRUE;
}
Nach dem Login kopieren

    注意这里没有使用括号哦。 RETURN_TRUERETURN_FALSE 与其他宏RETURN_*() 格式的变体,所以这里注意别被捕获。

    你也许注意到上面这些代码样品我们都没有传0和1什来表示这些值是否需要被拷贝。这是因为没有额外的内存(变量容器之外——我们将在第2部分深入)需要被分配或释放,因为这些标量都很简单很小。

    还有另外三种返回类型: RESOURCE (例如"mysql_connect()","fsockopen()"和"ftp_connect()"的返回值), ARRAY (也称为HASH),还有 OBJECT (通过关键词new返回)。我们将会在第二章更深入变量学习的时候了解这一系列。

INI Settings

    Zend 引擎提供了两个方式处理 INI 变量

    我们先看一下较简单的一种,而更全面、更复杂的方式,等以后你有机会接触全局变量再说。

    现在我们想在php.ini中定义一个变量,"hello.greeting", 用来处理你在"hello_function()"函数里用来打招呼的变量。你需要在hello.cphp_hello.h中添另一些东西,并且hello_module_entry 结构也得有所变化。以加入下面这些原型到php_hello.h的用户空间方法原型旁边:

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello); 

PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);
Nach dem Login kopieren

    现在到hello.c中用下面这串代码覆盖当前版本的hello_module_entry:

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    PHP_MINIT(hello),
    PHP_MSHUTDOWN(hello),
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

PHP_INI_BEGIN()
PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL)
PHP_INI_END()

PHP_MINIT_FUNCTION(hello)
{
    REGISTER_INI_ENTRIES();
    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(hello)
{
    UNREGISTER_INI_ENTRIES();
    return SUCCESS;
}
Nach dem Login kopieren

    现在,你只需要在hello.c顶部剩下的 #include 上加一个 #include 来包含支持INI 的正确的头:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h
"#include "php_hello.h"
Nach dem Login kopieren

    最后,我们修改 hello_world 方法来使用INI 值:

PHP_FUNCTION(hello_world)
{
    RETURN_STRING(INI_STR("hello.greeting"), 1);
}
Nach dem Login kopieren

    注意,你复制了来自 INI_STR()的返回值。因为这涉及到PHP变量栈都关心的问题,这是一个静态变量。实际上,如果你试图去修改这个方法返回的值,PHP运行环境会变得不稳定甚至可能崩溃。

    首次修改的部分包含了两个你需要熟悉的方法:MINIT MSHUTDOWN。正如上面提到的,这两个方法分别是在SAPI层初始启动和最后关闭时调用的。他们不在请求之间也不在请求之中调用。在这个示例中,你用他们注册在你扩展中定义的php.ini选项。在本文的后面,你将学习如何使用"MINIT"和"MSHUTDOWN"方法来注册资源、对象和流句柄。

    在你的方法hellow_world()中使用"INI_STR()"来取回当前"hello.greeting"项的值作为一个字符串。那些其他的已有方法取值作为长整型,浮点型和布尔型,如下表所示,"ORIG"补充了其他方法,能提供从 INI设置中引用的值(在被.htaccess或者 ini_set() 修改之前的值)。

    

Current Value Original Value Type
INI_STR(name) INI_ORIG_STR(name) char * (NULL terminated)
INI_INT(name) INI_ORIG_INT(name) signed long
INI_FLT(name) INI_ORIG_FLT(name) signed double
INI_BOOL(name) INI_ORIG_BOOL(name) zend_bool

Übergeben Sie PHP_INI_ENTRY() Der erste Parameter von ist ein Zeichen, das den Namen der Option der Zeichenfolge php.ini enthält . Um Namensraumkollisionen zu vermeiden, müssen Sie die gleiche Konvention wie Ihre Methode verwenden, d. h. allen Werten den Namen Ihrer Erweiterung voranstellen, z. B. „hello.greeting“. Die Erweiterung bestand aus Namen und INI-Einstellungen für den aussagekräftigeren Teil des Namens und wurde getrennt.

Der zweite Parameter ist der Anfangswert und wird normalerweise als char*-Zeichenfolge verwendet, unabhängig davon, ob es sich um eine Zahl handelt oder nicht. Dies liegt hauptsächlich daran, dass die Werte in der Datei .ini tatsächlich standardmäßig Text sind – es handelt sich um eine Textdatei. Sie können INI_INT(), INI_FLT(), oder INI_BOOL() in Ihrem Skript zur Ausführung verwenden Typkonvertierung.

Der dritte übergebene Parameter ist ein Zugriffsmodusmodifikator. Dies ist eine Bitmaske, die verwendet wird, um zu bestimmen, wann und wo INI geändert werden kann. Für einige, wie zum Beispiel register_globals, darf einfach nicht mit ini_set() im Skript geändert werden, da diese Konfiguration nur in verwendet werden kann Skripte: Gibt der Anforderung die Möglichkeit, vor dem Starten des Skripts ausgeführt zu werden. Darüber hinaus allow_url_fopen gibt es einige Verwaltungselemente, die Benutzer in einer Shared-Hosting-Umgebung nicht ändern dürfen, unabhängig davon, ob es sich um die Direktive ini_set() oder die .htaccess handelt . Ein weiterer typischer Wert für diesen Parameter sollte PHP_INI_ALL sein, was darauf hinweist, dass dieser Wert überall geändert werden kann. Es gibt auch PHP_INI_SYSTEM|PHP_INI_PERDIR, , was bedeutet, dass diese Konfiguration in der Datei php.ini festgelegt und auch in der .htaccess-Datei über Apache-Anweisungen geändert werden kann, aber es darf nicht passieren ini_set()Ändern. Darüber hinaus gibt es PHP_INI_SYSTEM, was bedeutet, dass der Wert nur in der Datei php.ini geändert werden kann, nicht anderswo.

Wir überspringen den vierten Parameter und klicken einfach auf den Wert, um eine Rückrufmethode zu übergeben, sodass diese immer dann aufgerufen wird, wenn die INI-Konfiguration geändert wird, beispielsweise wenn sie mit geändert wird ini_set(). Dadurch kann die Erweiterung geänderte Einstellungen präziser steuern oder durch Änderung einer neuen Konfiguration entsprechendes Verhalten auslösen.

Globale Variablen

    通常,扩展需要在特定的请求里跟踪变量的值,使之独立于并发请求。在无线程的SAPI中这可能比较简单:只需要在源文件中声明一个全局变量,在需要时调用。然而麻烦在于PHP被设计运行于线程级的web服务器(如Apache 2 和 IIS),这就需要保证一个线程中的全局变量与其他线程中的分离。PHP通过使用TSRM(Thread Safe Resource Management)抽象层,大大地简化了这一操作,有时被称为ZTS(Zend Thread Safety)。实际 上,到现在你已经使用了一部分TSRM了,虽然你对其不甚了解。(不要急着去搜索,通过这一系列的进展,你会发现它无处不在。)

    创建线程安全全局变量的第一步,跟创建其他全局变量一样,声明它。为了实现这个例子,你将声明一个以 long 型开始,值为 0 的全局变量。每次hello_long() 方法被调用时,我们增值该变量变返回。下面这段php_hello.h中的代码放在#define PHP_HELLO_H 模块后面:

#ifdef ZTS
#include "TSRM.h"
#endif

ZEND_BEGIN_MODULE_GLOBALS(hello)
    login counter;
ZEND_END_MODULE_GLOBALS(hello)

#ifdef ZTS
#define HELLO_G(v) TSRM(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif
Nach dem Login kopieren

    这次你还将用到 RINIT 方法,因此得在头文件中声明它的原型:

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);
Nach dem Login kopieren

    现在 到hello.c中添加下面这段代码到include 模块后:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_hello.h"

ZEND_DECLARE_MODULE_GLOBALS(hello)
Nach dem Login kopieren

    修改 hello_module_entry 添加PHP_RINIT(hello):

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    PHP_MINIT(hello),
    PHP_MSHUTDOWN(hello),
    PHP_RINIT(hello),
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};
Nach dem Login kopieren

    修改你的 MINIT 方法,添加另一对方法,来处理初始化启动请求:

static void
php_hello_init_globals(zend_hello_globals *hello_globals)
{
}

PHP_RINIT_FUNCTION(hello)
{
    HELLO_G(counter) = 0;
    return SUCCESS;
}

PHP_MINIT_FUNCTION(hello)
{
    ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);
    REGISTER_INI_ENTRIES();
    
    return SUCCESS;
}
Nach dem Login kopieren

    最后,修改你的 hello_long() 方法来使用这个值:

PHP_FUNCTION(hello_long)
{
    HELLO_G(counter)++;
    
    RETURN_LONG(HELLO_G(counter));
}
Nach dem Login kopieren

    在添加到php_hello.h的代码里,使用了一对宏ZEND_BEGIN_MODULE_GLOBALS() 和 ZEND_END_MODULE_GLOBALS() 创建了一个包含一个 long变量的结构体 zend_hello_globals 。然后根据条件是否处于一个无线程环境定义了是从线程池取值还是仅从全局范围取值的宏 HELLO_G() 。在hello.c中,我们使用 ZEND_DECLARE_MODULE_GLOBALS() 来创建了一个zend_hello_globals结构的实例,作为一个全局变量(如果在非线程安全中创建)或者一个线程资源池的一员。作为扩展作者,这个区别我们是不用但心的,Zend 引擎会帮我们处理好的。最后,在 MINI ,使用 ZEND_INIT_MODULE_GLOBALS() 分配了一个线程安全资源id——现在还不用担心这个是啥。你也许注意到 php_hello_init_globals()并没有作什么,但是我们声明 RINIT 时初始化counter为 0,为何?关键在于这两个函数何时被调用。php_hello_init_globals() 仅在一个新进程或者线程启动时调用;然而一个进程可以处理多个请求,所以使用这个方法初始化我们的counter为 0 只会在第一个页面请求时调用。接下来的请求到同一个进程的页面会一直使用这个已存储的值,因为不会从 0 开始计数。为了在每个页面请求中初始化counter为 0 ,我们实现了 RINIT 方法,如前所述的在页面请求之前实现。我们在这里包含php_hello_init_globals() 方法是因为一会就要使用到它,传一个 NULL 给ZEND_INIT_MODULE_GLOBALS() 来初始化函数会在非线程平台导致一个段错误 。

    INI设置 vs 全局变量

    如果你回观前文,一个在 PHP_INI_ENTRY() 里声明的php.ini是作为字符串转换成其他需要的类型,通过 INI_INT(), INI_FLT()和 INI_BOOL()。对某些配置,这代表在脚本执行的过程中会做大量重复的工作一遍又一遍的读这个值。幸运的是,可以指定ZE使用特殊的数据类型存储 INI 值,只在其值改变时做类型转换。让我们通过声明另一个 INI 值来试一下这个功能,这次使用布尔型标明计数器应该增还是减。首先修改php_hello.h下的MODULE_GLOBALS 模块如下:

ZEND_BEGIN_MODULE_GLOBAL(hello)
    login counter;
    zend_bool direction;
ZEND_ENG_MODULE_GLOBALS(hello)
Nach dem Login kopieren

    然后,通过修改PHP_INI_BEGIN() 模块来声明 INI 自己的值:

PHP_INI_BEGIN()

    PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL)
    STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)
    
PHP_INI_END()
Nach dem Login kopieren

    现在在 init_globals 方法里初始化配置:

static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
    hello_globals->direction = 1;
}
Nach dem Login kopieren

    最后,在hello_long()中使用这个配置的值来决定是自增还是自减:

PHP_FUNCTION(hello_long)
{
    if (HELLO_G(direction)) {
        HELLO_G(counter)++;
    } else {
        HELLO_G(counter)--;
    }    
    
    RETURN_LONG(HELLO_G(counter));
}
Nach dem Login kopieren

    就是这样,我们在INI_ENTRY 里指定的方法 OnUpdateBool 方法会自动转换php.ini, .htaccess或者脚本中通过 ini_set() 指定的值到相关的 TRUE/FALSE 值当你直接通过脚本访问的时候。STD_PHP_INI_ENTRY 的最后三个参数告诉PHP去修改哪个全局变量,我们扩展的全局变量的结构体是什么样,以及他们包含的全局范围的名称。

    完整性检查

    到现在,我们的三个文件里的内容应该如下所列的(为了可阅读性,一些条目被移到了一起)。

    config.m4

PHP_ARG_ENABLE(hello, whether to enable Hello World support, [ --enable-hello   Enable Hello World support])

if test "$PHP_HELLO" = "yes"; then
  AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
  PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi
Nach dem Login kopieren

    php_hello.h

#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1
#ifdef ZTS
#include "TSRM.h"
#endif
ZEND_BEGIN_MODULE_GLOBALS(hello)
    long counter;
    zend_bool direction;
ZEND_END_MODULE_GLOBALS(hello)
#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif
#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"
PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);
PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);
extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry
#endif
Nach dem Login kopieren

    hello.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "php_hello.h"
ZEND_DECLARE_MODULE_GLOBALS(hello)
static function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    PHP_FE(hello_long, NULL)
    PHP_FE(hello_double, NULL)
    PHP_FE(hello_bool, NULL)
    PHP_FE(hello_null, NULL)
    {NULL, NULL, NULL}
};
zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    PHP_MINIT(hello),
    PHP_MSHUTDOWN(hello),
    PHP_RINIT(hello),
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif
PHP_INI_BEGIN()
    PHP_INI_ENTRY("hello.greeting", "Hello World",
PHP_INI_ALL, NULL)
    STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL,
OnUpdateBool, direction, zend_hello_globals, hello_globals)
PHP_INI_END()
static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
    hello_globals->direction = 1;
}
PHP_RINIT_FUNCTION(hello)
{
    HELLO_G(counter) = 0;
    return SUCCESS;
}
PHP_MINIT_FUNCTION(hello)
{
    ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals,
NULL);
    REGISTER_INI_ENTRIES();
    return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(hello)
{
    UNREGISTER_INI_ENTRIES();
    return SUCCESS;
}
PHP_FUNCTION(hello_world)
{
    RETURN_STRING("Hello World", 1);
}
PHP_FUNCTION(hello_long)
{
    if (HELLO_G(direction)) {
        HELLO_G(counter)++;
    } else {
        HELLO_G(counter)--;
    }
    RETURN_LONG(HELLO_G(counter));
}
PHP_FUNCTION(hello_double)
{
    RETURN_DOUBLE(3.1415926535);
}
PHP_FUNCTION(hello_bool)
{
    RETURN_BOOL(1);
}
PHP_FUNCTION(hello_null)
{
    RETURN_NULL();
}
Nach dem Login kopieren

    接下来做什么?

    在这教程中我们开发了一个简单的PHP扩展,导出方法,返回值,声明了 INI 配置并且在一个请求中跟踪他的内核状态。

    下一章我们研究PHP变量的内核结构,以及变量如何存储、跟踪和在脚本环境中修改。当一个函数被调用时我们使用zend_parse_parameters 从程序获取参数,接着学习返回更复杂的结果的方法,包括本章提到的 arrayobject 

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage