企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 19.4 老技术新用 # 老技术新用 在PHP\_EMBED\_START\_BLOCK()被调用后, 你的应用处于⼀个php请求周期的开始 位置, 相当于RINIT回调函数完成以后. 此刻你就可以和前面一样执行 php\_execute\_script()命令, 或者其他任意合法的, 可以在PHP\_FUNCTION()或RINIT()块中出现的php/Zend API指令. #### 设置初始变量 第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['SAPI_TYPE'] = "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['SAPI_TYPE'] = "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 超级全局变量并给它设置一个初始值来进行测试: ``` HP_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['foo'] = 'Bar'; */ 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 #else #endif , 1, NULL TSRMLS_CC); , 1 TSRMLS_CC); /* 不论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['SAPI_TYPE'] = "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的兼容性, 则可以丢弃这些指令让代码变得更加整洁. ## links - [目录](preface.md) - 19.3 [通过嵌入包装重新创建cli](19.3.html) - 19.5 [小结](19.5.html)