# 老技术新用
在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>)
- about
- 开始阅读
- 目录
- 1 PHP的生命周期
- 1.让我们从SAPI开始
- 2.PHP的启动与终止
- 3.PHP的生命周期
- 4.线程安全
- 5.小结
- 2 PHP变量在内核中的实现
- 1. 变量的类型
- 2. 变量的值
- 3. 创建PHP变量
- 4. 变量的存储方式
- 5. 变量的检索
- 6. 类型转换
- 7. 小结
- 3 内存管理
- 1. 内存管理
- 2. 引用计数
- 3. 总结
- 4 动手编译PHP
- 1. 编译前的准备
- 2. PHP编译前的config配置
- 3. Unix/Linux平台下的编译
- 4. 在Win32平台上编译PHP
- 5. 小结
- 5 Your First Extension
- 1. 一个扩展的基本结构
- 2. 编译我们的扩展
- 3. 静态编译
- 4. 编写函数
- 5. 小结
- 6 函数返回值
- 1. 一个特殊的参数:return_value
- 2. 引用与函数的执行结果
- 3. 小结
- 7 函数的参数
- 1. zend_parse_parameters
- 2. Arg Info 与类型绑定
- 3. 小结
- 8 使用HashTable与{数组}
- 1. 数组(C中的)与链表
- 2. 操作HashTable的API
- 3. 在内核中操作PHP语言中数组
- 4. 小结
- 9 PHP中的资源类型
- 1. 复合类型的数据——{资源}
- 2. Persistent Resources
- 3. {资源}自有的引用计数
- 4. 小结
- 10 PHP中的面向对象(一)
- 1. zend_class_entry
- 2. 定义一个类
- 3. 定义一个接口
- 4. 类的继承与接口的实现
- 5. 小结
- 11 PHP中的面向对象(二)
- 1. 生成对象的实例与调用方法
- 2. 读写对象的属性
- 3. 小结
- 12 启动与终止的那点事
- 2. 小结
- 1. 关于生命周期
- 2. MINFO与phpinfo
- 3. 常量
- 4. PHP扩展中的全局变量
- 5. PHP语言中的超级全局变量
- 6. 小结
- 13 INI设置
- 1. 声明和访问ini设置
- 2. 小结
- 2. 小结
- 14 流式访问
- 1. 概览
- 2. 打开流
- 3. 访问流
- 4. 静态资源操作
- 5. 小结
- 15 流的实现
- 1. php流的表象之下
- 2. 包装器操作
- 3. 实现一个包装器
- 4. 操纵
- 5. 检查
- 6. 小结
- 16 有趣的流
- 1. 上下文
- 2. 过滤器
- 3. 小结
- 17 配置和链接
- 1. autoconf
- 2. 库的查找
- 3. 强制模块依赖
- 4. Windows方言
- 5. 小结
- 18 扩展生成
- 1. ext_skel
- 2. PECL_Gen
- 3. 小结
- 19 设置宿主环境
- 1. 嵌入式SAPI
- 2. 构建并编译一个宿主应用
- 3. 通过嵌入包装重新创建cli
- 4. 老技术新用
- 5. 小结
- 20 高级嵌入式
- 1. 回调到php中
- 2. 错误处理
- 3. 初始化php
- 4. 覆写INI_SYSTEM和INI_PERDIR选项
- 5. 捕获输出
- 6. 同时扩展和嵌入
- 7. 小结
- 约定