Maison > développement back-end > tutoriel php > [Français] [développement d'extensions php et embarquées] Chapitre 19 - Mise en place de l'environnement d'hébergement

[Français] [développement d'extensions php et embarquées] Chapitre 19 - Mise en place de l'environnement d'hébergement

黄舟
Libérer: 2023-03-05 16:42:01
original
1461 Les gens l'ont consulté


Configurer l'environnement d'hébergement

Vous connaissez maintenant le monde de PHPAPI et pouvez utiliser zval comme eh bien Le mécanisme d'extension interne du langage effectue beaucoup de travail, et il est temps de changer de vitesse et de l'utiliser pour faire ce qu'il fait le mieux : interpréter le code de script >

En revoyant l'introduction, PHP a construit un. système hiérarchique. La couche supérieure fournit toutes les extensions des fonctions de l'espace utilisateur et des bibliothèques de classes. En même temps, en dessous se trouve la couche API de service (SAPI), qui joue le rôle d'interface du serveur Web (comme Apache, iis et command). line interface cli). Parmi ces nombreuses implémentations sapi, il existe une sapi spéciale qui est la sapi intégrée. Lorsque cette implémentation sapi est construite, elle créera un objet bibliothèque qui contient tous les éléments. Fonctions et variables des API PHP et Zend que vous connaissez. Cet objet de bibliothèque contient également des fonctions d'assistance et des macros supplémentaires pour simplifier l'appel de programmes externes.

La génération de bibliothèques et de fichiers d'en-tête pour les API intégrées effectue la même chose. actions comme la compilation d'autres sapis. Passez simplement --enable-embed à la commande ./configure Comme auparavant, utilisez --enable- debug est utile pour le rapport et le suivi des erreurs.

Vous. Vous devrez peut-être également activer --enable-maintainer-zts, bien sûr, pour des raisons que vous connaissez déjà, cela vous aidera à remarquer l'erreur de code, cependant, il y a d'autres raisons ici Supposons qu'à un certain moment, vous. avoir plusieurs applications utilisant la bibliothèque PHP intégrée pour effectuer des tâches de script ; l'une des applications est un simple cycle de vie courte et n'utilise pas de threads, donc pour plus d'efficacité, vous souhaiterez peut-être désactiver ZTS.

En supposant maintenant que la deuxième application utilise des threads, comme un serveur Web, chaque thread doit suivre son propre contexte de requête. Si ZTS est désactivé, seules les deux premières applications peuvent utiliser cette bibliothèque ; est activé, les deux applications peuvent utiliser le même objet partagé dans leur propre espace de processus.

Bien sûr, vous pouvez également créer les deux versions en même temps et leur donner des noms différents, mais c'est plus un problème que le petit gain d'efficacité lié à l'inclusion de ZTS lorsqu'il n'est pas nécessaire

Par défaut, la bibliothèque intégrée sera construite comme un objet partagé libphp5.so, ou comme. une bibliothèque de liens dynamiques sous Windows, cependant, elle peut également être construite en tant que bibliothèque statique à l'aide du mot-clé statique facultatif (--enable-embed=static).

Construire en tant que bibliothèque statique évite le problème ZTS/non-ZTS, et potentiellement avoir plusieurs versions de PHP sur un système. Le risque est que cela signifie que votre binaire d'application deviendra considérablement plus grand et qu'il hébergera donc l'intégralité du framework ZendEngine et PHP. , lors du choix, vous devez soigneusement déterminer si vous avez besoin d'une bibliothèque relativement plus petite.

Peu importe la méthode de construction que vous choisissez, une fois que vous avez exécuté make install, libphp5 sera copié dans la bibliothèque / sous le répertoire PREFIX spécifié par votre ./configure. De plus, il sera également copié dans PREFIX/include/php/sapi/embed. Mettez le fichier d'en-tête nommé php_embed.h dans le répertoire, ainsi que plusieurs autres en-têtes importants. fichiers dont vous avez besoin lorsque vous utilisez la bibliothèque php intégrée pour compiler le programme

Construire et compiler une application hôte

. En son cœur, une bibliothèque n'est qu'une collection de code sans but. Pour qu'elle fonctionne, vous avez besoin d'une application qui intègre PHP. Tout d'abord, encapsulons une application très simple, qui démarre le moteur Zend et initialise PHP pour le traiter. une demande, puis retourne nettoyer les ressources .

#include <sapi/embed/php_embed.h>

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

    return 0;
}
Copier après la connexion


Comme cela implique tellement de fichiers d'en-tête, la construction prendra en fait plus de temps que pour un si petit code La fragmentation prend généralement du temps. Si vous utilisez un PREFIX différent du chemin par défaut (/usr/local), assurez-vous de spécifier le chemin de la manière suivante :

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
Copier après la connexion

dû. à cette commande Il est difficile de saisir à chaque fois. Vous préférerez peut-être utiliser un simple Makefile à la place :

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)
Copier après la connexion

<🎜. >

Ce Makefile présente quelques différences importantes par rapport aux commandes fournies précédemment. Premièrement, il utilise le commutateur -Wall pour activer les avertissements au moment de la compilation et utilise -g pour activer les informations de débogage. les phases de compilation et de liaison en deux phases : Afin d'avoir deux étapes indépendantes, il sera relativement facile d'ajouter plus de fichiers sources ultérieurement. Veuillez réorganiser vous-même ce Makefile, mais l'alignement ici est une tabulation (tabulation horizontale) au lieu d'espaces. 🎜>

Maintenant, après avoir modifié le fichier source embed1.c, il vous suffit d'exécuter une commande make pour créer un nouveau programme exécutable embed1.

Recréez le cli en intégrant le wrapper

Maintenant que php est accessible dans votre application, il est temps de lui faire faire le reste. ce chapitre tourne autour de la recréation du cli sapi dans ce framework d'application de test.

Très simple, la fonction la plus basique du binaire cli est de spécifier un script sur le nom de la ligne de commande, qui est interprété et exécuté par PHP. Remplacez le contenu de votre embed1.c par le code suivant pour implémenter cli dans votre application

<. 🎜>

Remarque sur la traduction : le code de l'œuvre originale ne peut pas être exécuté directement dans l'environnement du traducteur, le code ci-dessus a été modifié.

#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;
}
Copier après la connexion

Bien sûr, vous avez besoin d'un fichier pour le tester, créez un petit script php, nommé test.php, et exécutez-le à l'aide de votre programme d'intégration sur la ligne de commande :

Si vous transmettez d'autres paramètres à la ligne de commande, vous pouvez utiliser $_SERVER['argc']/$ dans votre script php _SERVER['argv'] pour les voir.

$ ./embed1 test.php
Copier après la connexion
Vous avez peut-être remarqué que le code entre PHP_EMBED_START_BLOCK() et PHP_EMBED_END_BLOCK() est en retrait. Ce détail est dû au fait que ces deux macros forment en réalité un code en langage C. portée du bloc. C'est-à-dire que PHP_EMBED_START_BLOCK() contient une accolade ouvrante "{", et il y a une accolade fermante correspondante "} dans PHP_EMBED_END_BLOCK() ". Un problème très important avec ceci est qu'ils ne peuvent pas être mis séparément. fonctions start/stop. Vous verrez la solution à ce problème dans le chapitre suivant.

Nouvelle utilisation de l'ancienne technologie

Après l'appel de PHP_EMBED_START_BLOCK(), votre application est au début d'un cycle de requête PHP, ce qui équivaut à l'achèvement de la fonction de rappel RINIT. Plus tard, vous pouvez exécuter la commande php_execute_script() comme. avant, ou toute autre commande légale de l'API php/Zend pouvant apparaître dans le bloc PHP_FUNCTION() ou RINIT().

Définir les variables initiales

第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()
Copier après la connexion

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

<?php
	var_dump($type);
?>
Copier après la connexion

当然, 这个简单的概念可以很容易的扩展为填充这个类型信息到$_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()
Copier après la connexion

译注: 译者的环境中代码运行到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);
Copier après la connexion

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()
Copier après la connexion

定义附加的超级全局变量

在第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()
Copier après la connexion

要记住, 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)!


Étiquettes associées:
php
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal