🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 老技术新用 在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()代码块替换为下面的代码: ````c 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, 并用下面的测试脚本进行测试: ````c <?php var_dump($type); ?> ```` 当然, 这个简单的概念可以很容易的扩展为填充这个类型信息到$_SERVER超级全局变量数组中. ````c 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()命令打开或关闭: ````c 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. ````c 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 超级全局变量并给它设置一个初始值来进行测试: ````c 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.md>) * 19.5 [小结](<19.5.md>)