Heim  >  Artikel  >  Backend-Entwicklung  >  [Übersetzung] [PHP-Erweiterungsentwicklung und eingebettet] Kapitel 19 – Einrichten der Hosting-Umgebung

[Übersetzung] [PHP-Erweiterungsentwicklung und eingebettet] Kapitel 19 – Einrichten der Hosting-Umgebung

黄舟
黄舟Original
2017-02-10 10:41:501402Durchsuche


Hosting-Umgebung einrichten

Jetzt kennen Sie die Welt von PHPAPI und können zval as nutzen Nun, der interne Erweiterungsmechanismus der Sprache leistet viel Arbeit, und es ist an der Zeit, den Gang zu wechseln und ihn für das zu verwenden, was er am besten kann: Skriptcode interpretieren. PHP hat einen erstellt Hierarchisches System. Die oberste Ebene bietet alle Erweiterungen von User-Space-Funktionen und Klassenbibliotheken. Gleichzeitig befindet sich darunter die Service-API-Ebene (SAPI), die die Rolle der Schnittstelle des Webservers spielt (z. B. Apache, IIS und Befehl).

Unter diesen vielen Sapi-Implementierungen gibt es eine spezielle Sapi-Implementierung. Wenn diese Sapi-Implementierung erstellt wird, wird ein Bibliotheksobjekt erstellt, das alle enthält PHP- und Zend-API-Funktionen und -Variablen, die Sie kennen, enthalten auch einige zusätzliche Hilfsfunktionen und Makros, um den Aufruf externer Programme zu vereinfachen Aktionen wie das Kompilieren anderer SAPIS. Übergeben Sie einfach --enable-embed an den Befehl ./configure. Wie zuvor ist die Verwendung von --enable-debug für die Fehlerberichterstattung und -verfolgung hilfreich.

Sie Möglicherweise müssen Sie auch --enable-maintainer-zts aktivieren. Aus Gründen, mit denen Sie bereits vertraut sind, können Sie den Fehler im Code erkennen Sie haben mehrere Anwendungen, die die eingebettete PHP-Bibliothek zum Ausführen von Skriptaufgaben verwenden. Eine der Anwendungen ist ein einfacher Kurzlebenszyklus und verwendet keine Threads. Aus Effizienzgründen sollten Sie daher ZTS deaktivieren.

Angenommen, die zweite Anwendung verwendet Threads, z. B. einen Webserver, muss jeder Thread seinen eigenen Anforderungskontext verfolgen. Wenn ZTS deaktiviert ist, können jedoch nur die ersten beiden Anwendungen diese Bibliothek verwenden aktiviert ist, können beide Anwendungen dasselbe gemeinsame Objekt in ihrem eigenen Prozessraum verwenden.

Natürlich können Sie auch beide Versionen gleichzeitig erstellen und ihnen unterschiedliche Namen geben, aber das ist ein größeres Problem als der kleine Effizienzverlust durch die Einbindung von ZTS, wenn es nicht benötigt wird.

Standardmäßig wird die eingebettete Bibliothek als gemeinsam genutztes libphp5.so-Objekt erstellt Als dynamische Linkbibliothek unter Windows kann sie jedoch auch als statische Bibliothek mit dem optionalen Schlüsselwort static (--enable-embed=static) erstellt werden.

Erstellung als statische Bibliothek vermeidet das ZTS/Nicht-ZTS-Problem und möglicherweise mehrere PHP-Versionen auf einem System. Das Risiko besteht darin, dass Ihre Anwendungsbinärdatei erheblich größer wird und daher das gesamte ZendEngine- und PHP-Framework hostet Bei der Auswahl müssen Sie sorgfältig abwägen, ob Sie eine relativ kleinere Bibliothek benötigen.

Egal welche Build-Methode Sie wählen, sobald Sie make install ausführen, wird libphp5 in die lib kopiert / Verzeichnis unter dem von Ihrem ./configure angegebenen PREFIX-Verzeichnis. Darüber hinaus befindet es sich auch in PREFIX/include/php/sapi/embed. Legen Sie die Header-Datei mit dem Namen php_embed.h sowie mehrere andere wichtige Header-Dateien im Verzeichnis ab Sie benötigen, wenn Sie die eingebettete PHP-Bibliothek zum Kompilieren des Programms verwenden

Erstellen und kompilieren Sie eine Hostanwendung

At Ihr Herzstück, eine Bibliothek, ist nur eine Sammlung von Code ohne Zweck. Damit sie funktioniert, benötigen Sie eine Anwendung, die PHP einbettet. Lassen Sie uns zunächst eine sehr einfache Anwendung kapseln, die die Zend-Engine startet und PHP zur Verarbeitung von a initialisiert Anfrage und geht dann zurück, um Ressourcen zu bereinigen

#include <sapi/embed/php_embed.h>

int main(int argc, char *argv[])
{
    PHP_EMBED_START_BLOCK(argc,argv)
    PHP_EMBED_END_BLOCK()

    return 0;
}


Da es sich dabei um so viele Header-Dateien handelt, dauert der Build tatsächlich länger als bei einem so kleinen Code Wenn Sie ein anderes PREFIX als den Standardpfad (/usr/local) verwenden, stellen Sie bitte sicher, dass Sie den Pfad wie folgt angeben:

gcc -I /usr/local/php-dev/include/php/ \
	-I /usr/local/php-dev/include/php/main/ \
	-I /usr/local/php-dev/include/php/Zend/ \
	-I /usr/local/php-dev/include/php/TSRM/ \
	-lphp5 \
	-o embed1 
	embed1.c

due zu diesem Befehl Die Eingabe ist jedes Mal mühsam. Möglicherweise bevorzugen Sie stattdessen die Verwendung eines einfachen Makefiles:

CC = gcc 
CFLAGS = -c \
    -I /usr/local/php-dev/include/php/ \
    -I /usr/local/php-dev/include/php/main/ \
    -I /usr/local/php-dev/include/php/Zend/ \
    -I /usr/local/php-dev/include/php/TSRM/ \
    -Wall -g
LDFLAGS = -lphp5

all: embed1.c
    $(CC) -o embed1.o embed1.c $(CFLAGS)
    $(CC) -o embed1 embed1.o $(LDFLAGS)

Dieses Makefile weist einige wichtige Unterschiede zu den zuvor bereitgestellten Befehlen auf. Erstens verwendet es den Schalter -Wall, um Warnungen zur Kompilierungszeit zu aktivieren, und verwendet -g, um Debugging-Informationen zu aktivieren Die Kompilierungs- und Verknüpfungsphasen sind in zwei Phasen unterteilt: Für zwei unabhängige Phasen ist es relativ einfach, dieses Makefile später selbst neu zu organisieren, aber die Ausrichtung ist hier Tabulator (horizontale Tabulatortaste) anstelle von Leerzeichen 🎜>

Nachdem Sie die Quelldatei „embed1.c“ geändert haben, müssen Sie nur noch einen Make-Befehl ausführen, um ein neues ausführbares Programm „embed1“ zu erstellen.

Erstellen Sie die CLI neu, indem Sie den Wrapper einbetten.

Da PHP jetzt in Ihrer App verfügbar ist, ist es an der Zeit, dafür zu sorgen, dass es einige Dinge erledigt. Der Rest dieses Kapitels dreht sich darum rund um die Neuerstellung des CLI-Sapi in diesem Testanwendungs-Framework.

Sehr einfach, die grundlegendste Funktion der CLI-Binärdatei besteht darin, einen Skriptnamen auf der Befehlszeile anzugeben, der interpretiert und ausgeführt wird durch PHP. Ersetzen Sie den Inhalt Ihrer Datei „embed1.c“ durch den folgenden Code, um cli in Ihrer Anwendung zu implementieren 🎜>

Übersetzungshinweis: Der Code im Originalwerk kann nicht direkt in der Umgebung des Übersetzers ausgeführt werden, der obige Code wurde geändert.

Natürlich , Sie benötigen eine Datei zum Testen, erstellen ein kleines PHP-Skript mit dem Namen test.php und führen es mit Ihrem Einbettungsprogramm in der Befehlszeile aus:

#include <stdio.h>
#include <sapi/embed/php_embed.h>

int main(int argc, char *argv[]) {
    zend_file_handle    script;

    /* 基本的参数检查 */
    if ( argc <= 1 ) {
        fprintf(stderr, "Usage: %s <filename.php> <arguments>\n", argv[0]);
        return -1;
    }
    
    /* 设置一个文件处理结构 */
    script.type             = ZEND_HANDLE_FP;
    script.filename         = argv[1];
    script.opened_path      = NULL;
    script.free_filename    = 0;
    if ( !(script.handle.fp = fopen(script.filename, "rb")) ) {
        fprintf(stderr, "Unable to open: %s\n", argv[1]);
        return -1;
    }
    
    /* 在将命令行参数注册给php时(php中的$argv/$argc), 忽略第一个命令行参数, 因为它对php脚本无意义 */
    argc --;
    argv ++;
    
    PHP_EMBED_START_BLOCK(argc, argv)
        php_execute_script(&script TSRMLS_CC);
    PHP_EMBED_END_BLOCK()
    
    return 0;
}

Wenn Sie andere Parameter an die Befehlszeile übergeben, können Sie $_SERVER['argc']/$ in Ihrem PHP-Skript _SERVER['argv'] verwenden, um diese anzuzeigen .

Sie haben möglicherweise bemerkt, dass der Code zwischen PHP_EMBED_START_BLOCK() und PHP_EMBED_END_BLOCK() eingerückt ist. Dieses Detail ist darauf zurückzuführen, dass diese beiden Makros tatsächlich einen Codeblockbereich in der C-Sprache bilden. Das heißt, PHP_EMBED_START_BLOCK() enthält eine öffnende geschweifte Klammer „{“ und es gibt eine entsprechende schließende geschweifte Klammer „} in PHP_EMBED_END_BLOCK()“. Ein sehr wichtiges Problem dabei ist, dass sie nicht in separate Start-/Stopp-Klammern gesetzt werden können Die Lösung für dieses Problem finden Sie im nächsten Kapitel.

$ ./embed1 test.php

Neue Nutzung alter Technologie

Nachdem PHP_EMBED_START_BLOCK() aufgerufen wurde, befindet sich Ihre Anwendung am Anfang eines PHP-Anforderungszyklus, was dem Abschluss der RINIT-Rückruffunktion entspricht. Später können Sie den Befehl php_execute_script() wie zuvor ausführen, oder Jeder andere legale PHP/Zend-API-Befehl, der im PHP_FUNCTION()- oder RINIT()-Block erscheinen kann.

Anfangsvariablen festlegen

第2章"变量的里里外外"中介绍了操纵符号表的概念, 第5至18章则介绍了怎样通过用户空间脚本调用内部函数使用这些技术. 到这里这些处理也并没有发生变化, 虽然这里并没有激活的用户空间脚本, 但是你的包装应用仍然可以操纵符号表. 将你的PHP_EMBED_START_BLOCK()/PHP_EMBED_END_BLOCK()代码块替换为下面的代码:

    PHP_EMBED_START_BLOCK(argc, argv)
        zval    *type;

        ALLOC_INIT_ZVAL(type);
        ZVAL_STRING(type, "Embedded", 1);
        ZEND_SET_SYMBOL(&EG(symbol_table), "type", type);
        
        php_execute_script(&script TSRMLS_CC);
    PHP_EMBED_END_BLOCK()

现在使用make重新构建embed1, 并用下面的测试脚本进行测试:

<?php
	var_dump($type);
?>

当然, 这个简单的概念可以很容易的扩展为填充这个类型信息到$_SERVER超级全局变量数组中.

    PHP_EMBED_START_BLOCK(argc, argv)
        zval    **SERVER_PP, *type;

        /* 注册$_SERVER超级全局变量 */
        zend_is_auto_global_quick("_SERVER", sizeof("_SERVER") - 1, 0 TSRMLS_CC);
        /* 查找$_SERVER超级全局变量 */
        zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&SERVER_PP) ;

        /* $_SERVER[&#39;SAPI_TYPE&#39;] = "Embedded"; */
        ALLOC_INIT_ZVAL(type);
        ZVAL_STRING(type, "Embedded", 1);
        ZEND_SET_SYMBOL(Z_ARRVAL_PP(SERVER_PP), "SAPI_TYPE", type);

        php_execute_script(&script TSRMLS_CC);
    PHP_EMBED_END_BLOCK()

译注: 译者的环境中代码运行到zend_hash_find()处$_SERVER尚未注册, 经过跟踪, 发现它是直到编译用户空间代码的时候, 发现用户空间使用了$_SERVER变量才进行的注册. 因此, 上面的代码中增加了zend_is_auto_global_quick()的调用, 通过这个调用将完成对$_SERVER的注册.

覆写INI选项

在第13章"INI设置"中, 有一部分是讲INI修改处理器的, 在那里看到的是INI阶段的处理. PHP_EMBED_START_BLOCK()宏则将这些代码放到了运行时阶段. 也就是说这个时候修改某些设置(比如register_globals/magic_quotes_gpc)已经有点迟了.

不过在内部访问也没有什么不好. 所谓的"管理设置"比如safe_mode在这个略迟的阶段可以使用下面的zend_alter_ini_entry()命令打开或关闭:

int zend_alter_ini_entry(char *name, uint name_length,
                         char *new_value, uint new_value_length,
                         int modify_type, int stage);

name, new_value以及它们对应的长度参数的含义正如你所预期的: 修改名为name的INI设置的值为new_value. 要注意name_length包含了末尾的NULL字节, 然而new_value_length则不包含; 然而, 无论如何, 两个字符串都必须是NULL终止的.

modify_type则提供简化的访问控制检查. 回顾每个INI设置都有一个modifiable属性, 它是PHP_INI_SYSTEM, PHP_INI_PERDIR, PHP_INI_USER等常量的组合值. 当使用zend_alter_ini_entry()修改INI设置时, modify_type参数必须包含至少一个INI设置的modifiable属性值.

用户空间的ini_set()函数通过传递PHP_INI_USER利用了这个特性, 也就是说只有modifiable属性包含PHP_INI_USER标记的INI设置才能使用这个函数修改. 当在你的嵌入式应用中使用这个API调用时, 你可以通过传递PHP_INI_ALL标记短路这个访问控制系统, 它将包含所有的INI访问级别.

stage必须对应于Zend Engine的当前状态; 对于这些简单的嵌入式示例, 总是PHP_INI_STAGE_RUNTIME. 如果这是一个扩展或更高端的嵌入式应用, 你可能就需要将这个值设置为PHP_INI_STAGE_STARTUP或PHP_INI_STAGE_ACTIVE.

下面是扩展embed1.c源文件, 让它在执行脚本文件之前强制开启safe_mode.

    PHP_EMBED_START_BLOCK(argc, argv)
        zval    **SERVER_PP, *type;

        /* 不论php.ini中如何设置都强制开启safe_mode */
        zend_alter_ini_entry("safe_mode", sizeof("safe_mode"), "1", sizeof("1") - 1, PHP_INI_ALL, PHP_INI_STAGE_RUNTIME);
        
        /* 注册$_SERVER超级全局变量 */
        zend_is_auto_global_quick("_SERVER", sizeof("_SERVER") - 1, 0 TSRMLS_CC);
        /* 查找$_SERVER超级全局变量 */
        zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&SERVER_PP) ;
        
        /* $_SERVER[&#39;SAPI_TYPE&#39;] = "Embedded"; */
        ALLOC_INIT_ZVAL(type);
        ZVAL_STRING(type, "Embedded", 1);
        ZEND_SET_SYMBOL(Z_ARRVAL_PP(SERVER_PP), "SAPI_TYPE", type);
        
        php_execute_script(&script TSRMLS_CC);
    PHP_EMBED_END_BLOCK()

定义附加的超级全局变量

在第12章"启动, 终止, 以及其中的一些点"中, 你知道了用户空间全局变量以及超级全局变量可以在启动(MINIT)阶段定义. 同样, 本章介绍的嵌入式直接跳过了启动阶段, 处于运行时状态. 和覆写INI一样, 这并不会显得太迟.

超级全局变量的定义实际上只需要在脚本编译之前定义即可, 并且在php的进程生命周期中它只应该出现一次. 在扩展中的正常情况下, MINIT是唯一可以保证这些条件的地方.

由于你的包装应用现在是在控制中的, 因此可以保证定义用户空间自动全局变量的这些点位于真正编译脚本源文件的php_execute_script()命令之前. 我们定义一个$_EMBED超级全局变量并给它设置一个初始值来进行测试:

    PHP_EMBED_START_BLOCK(argc, argv)
        zval    **SERVER_PP, *type, *EMBED, *foo;

        /* 在全局作用域创建$_EMBED数组 */
        ALLOC_INIT_ZVAL(EMBED);
        array_init(EMBED);
        ZEND_SET_SYMBOL(&EG(symbol_table), "_EMBED", EMBED);

        /* $_EMBED[&#39;foo&#39;] = &#39;Bar&#39;; */
        ALLOC_INIT_ZVAL(foo);
        ZVAL_STRING(foo, "Bar", 1); 
        add_assoc_zval_ex(EMBED, "foo", sizeof("foo"), foo);

        /* 注册超级全局变量$_EMBED */
        zend_register_auto_global("_EMBED", sizeof("_EMBED")
#ifdef ZEND_ENGINE_2
            , 1, NULL TSRMLS_CC);
#else
            , 1 TSRMLS_CC);
#endif

        /* 不论php.ini中如何设置都强制开启safe_mode */
        zend_alter_ini_entry("safe_mode", sizeof("safe_mode"), "1", sizeof("1") - 1, PHP_INI_ALL, PHP_INI_STAGE_RUNTIME);

        /* 注册$_SERVER超级全局变量 */
        zend_is_auto_global_quick("_SERVER", sizeof("_SERVER") - 1, 0 TSRMLS_CC);
        /* 查找$_SERVER超级全局变量 */
        zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&SERVER_PP) ;

        /* $_SERVER[&#39;SAPI_TYPE&#39;] = "Embedded"; */
        ALLOC_INIT_ZVAL(type);
        ZVAL_STRING(type, "Embedded", 1); 
        ZEND_SET_SYMBOL(Z_ARRVAL_PP(SERVER_PP), "SAPI_TYPE", type);

        php_execute_script(&script TSRMLS_CC);
    PHP_EMBED_END_BLOCK()

要记住, Zend Engine 2(php 5.0或更高)使用了不同的zend_register_auto_global()元婴, 因此你需要用前面讲php 4兼容时候讲过的#ifdef. 如果你不关心旧版本php的兼容性, 则可以丢弃这些指令让代码变得更加整洁.

小结

如你所见, 将完整的Zend Engine和PHP语言嵌入到你的应用中相比如扩展新功能来说工作量要少. 由于它们共享相同的基础API, 我们可以学习尝试让其他实例可访问.

通过本章的学习, 你了解了最简单的嵌入式脚本代码格式, 同时还有all-in-one的宏PHP_EBED_START_BLOCK()和PHP_EMBED_END_BLOCK(). 下一章你将回到这些宏的层的使用, 利用它们将php和你的宿主系统结合起来.

以上就是[翻译][php扩展开发和嵌入式]第19章-设置宿主环境的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Stellungnahme:
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