# 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)
- 介绍
- 1 PHP的生命周期
- 1.1 让我们从SAPI开始
- 1.2 PHP的启动与终止
- 1.3 PHP的生命周期
- 1.4 线程安全
- 1.5 PHP的生命周期
- 2 PHP变量在内核中的实现
- 2.1 变量的类型
- 2.2 变量的值
- 2.3 创建PHP变量
- 2.4 变量的存储方式
- 2.5 变量的检索
- 2.6 类型转换
- 2.7 小结
- 3 内存管理
- 3.1 内存管理
- 3.2 引用计数
- 3.3 内存管理
- 4 动手编译PHP
- 4.1 动手编译PHP
- 4.2 动手编译PHP
- 4.3 Unix/Linux平台下的编译
- 4.4 在Win32平台上编译PHP
- 4.5 动手编译PHP
- 5 Your First Extension
- 5.1 Your First Extension
- 5.2 编译我们的扩展
- 5.3 静态编译
- 5.4 编写函数
- 5.5 Your First Extension
- 6 函数返回值
- 6.1 函数返回值
- 6.2 引用与函数的执行结果
- 6.3 函数返回值
- 7 函数的参数
- 7.1 函数的参数
- 7.2 函数的参数
- 7.3 函数的参数
- 8 使用HashTable与{数组}
- 8.1 使用HashTable与{数组}
- 8.2 使用HashTable与{数组}
- 8.3 使用HashTable与{数组}
- 8.4 使用HashTable与{数组}
- 9 PHP中的资源类型
- 9.1 PHP中的资源类型
- 9.2 PHP中的资源类型
- 9.3 PHP中的资源类型
- 9.4 PHP中的资源类型
- 10 PHP中的面向对象(一)
- 10.1 PHP中的面向对象(一)
- 10.2 PHP中的面向对象(一)
- 10.3 PHP中的面向对象(一)
- 10.4 PHP中的面向对象(一)
- 10.5 PHP中的面向对象(一)
- 11 PHP中的面向对象(二)
- 11.1 PHP中的面向对象(二)
- 11.2 PHP中的面向对象(二)
- 11.3 PHP中的面向对象(二)
- 12 启动与终止的那点事
- 12.1 关于生命周期
- 12.2 MINFO与phpinfo
- 12.3 常量
- 12.4 PHP扩展中的全局变量
- 12.5 PHP语言中的超级全局变量(Superglobals)
- 12.6 小结
- 13 INI设置
- 13.1 声明和访问INI设置
- 13.2 小结
- 14 流式访问
- 14.1 流的概览
- 14.2 访问流
- 14.3 静态资源操作
- 14.4 links
- 15 流的实现
- 15.1 php流的表象之下
- 15.2 包装器操作
- 15.3 实现一个包装器
- 15.4 操纵
- 15.5 检查
- 15.6 小结
- 16 有趣的流
- 16.1 上下文
- 16.2 过滤器
- 16.3 小结
- 17 配置和链接
- 17.1 autoconf
- 17.2 库的查找
- 17.3 强制模块依赖
- 17.4 Windows方言
- 17.5 小结
- 18 扩展生成
- 18.1 ext_skel
- 18.2 PECL_Gen
- 18.3 小结
- 19 设置宿主环境
- 19.1 嵌入式SAPI
- 19.2 构建并编译一个宿主应用
- 19.3 通过嵌入包装重新创建cli
- 19.4 老技术新用
- 19.5 小结
- 20 高级嵌入式
- 20.1 回调到php中
- 20.2 错误处理
- 20.3 初始化php
- 20.4 覆写INI_SYSTEM和INI_PERDIR选项
- 20.5 捕获输出
- 20.6 同时扩展和嵌入
- 20.7 小结