# 12.4 PHP扩展中的全局变量
# 12.4 PHP扩展中的全局变量
这一章,我们将学会如何在PHP扩展中使用全局变量。
在扩展中定义全局变量
首先,我们需要在扩展的头文件中(默认是php\_\*.h)中定义所有的全局变量。举个例子,比如我们要定义一个无符号的long类型的全局变量,我们可以这样定义:
```
ZEND_BEGIN_MODULE_GLOBALS(sample4)
unsigned long counter;
ZEND_END_MODULE_GLOBALS(sample4)
```
用ZEND\_BEGIN\_MODULE\_GLOBALS和ZEND\_END\_MODULE\_GLOBALS宏将定义的全局变量包起来。将上例中的宏展开后,是下面这个样子:
```
typedef struct _zend_sample4_globals {
unsigned long counter;
} zend_sample4_globals;
```
如果你还有其他的全局变量需要定义,只需加在两个宏之间就可以了。接下来我们该在simple4.c中声明我们在头文件中定义的这些全局变量了:
```
ZEND_DECLARE_MODULE_GLOBALS(sample4);
```
这个宏的内部实现取决于是否启用了线程安全,在非线程安全的环境下,如:Apache1,Apache2-prefork, CGI,CLI...会使用zend\_sample4\_globals结构来定义 全局变量:
```
zend_sample4_globals sample4_globals;
```
我们可以直接通过sample4\_globals.counter来获取计数器的值。在线程安全的版本中,另一种方法是声明一个整数:
```
int sample4_globals_id;
```
填充这个ID就等于定义了扩展中的全局变量。根据其定义的信息,将为每个新线程的独立存储空间分配内存块。我们可以在MINIT中这样定义:
```
#ifdef ZTS
ts_allocate_id(
&sample4_globals_id,
sizeof(zend_sample4_globals),
NULL, NULL);
#endif
```
有一点需要注意这种方法需要包裹在#ifdef中,以防止它在没有启动Zend Thread Safety(ZTS)时执行。因为sample4\_globals\_id只能在多线程环境中使用。非线程 的版本用我们在前面提到的sample4\_globals来声明全局变量。
线程中的初始化和关闭
在非线程的环境中,会将一个zend\_sample4\_globals结构的副本保存在指定进程中。你可以指定他的默认值,或者在MINIT或者RINIT中分配资源来初始化它。要记得 在对应的MSHUTDOWN或者RSHUTDOWN中及时释放这些资源。
然而在线程版本中,一个新的结构会在一个新线程spun的时候被分配。为了知道怎样初始化和关闭扩展中的全局变量,需要向ZE引擎提供回调函数。在前面我们在调用 ts\_allocate\_id()的时候是是以NULL来作为这个值的,接下来我们添加2个一会需要在MINIT调用的方法:
```
static void php_sample4_globals_ctor(zend_sample4_globals *sample4_globals TSRMLS_DC)
{
/* Initialize a new zend_sample4_globals struct
* During thread spin-up */
sample4_globals->counter = 0;
}
static void php_sample4_globals_dtor(zend_sample4_globals *sample4_globals TSRMLS_DC)
{
/* Any resources allocated during initialization
* May be freed here */
}
```
我们在启用和关闭的时候调用它们:
```
PHP_MINIT_FUNCTION(sample4) {
REGISTER_STRING_CONSTANT("SAMPLE4_VERSION", PHP_SAMPLE4_EXTVER, CONST_CS | CONST_PERSISTENT);
#ifdef ZTS
ts_allocate_id(&sample4_globals_id,
sizeof(zend_sample4_globals),
(ts_allocate_ctor)php_sample4_globals_ctor,
(ts_allocate_dtor)php_sample4_globals_dtor);
#else
php_sample4_globals_ctor(&sample4_globals TSRMLS_CC);
#endif
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(sample4) {
#ifndef ZTS
php_sample4_globals_dtor(&sample4_globals TSRMLS_CC);
#endif
return SUCCESS;
}
```
现在我们已经知道如何在扩展中创建全局变量了,在不是ZTS的环境中,使用它们很简单,我们还来看前面定义的那个计数器的递增功能如何实现:
```
PHP_FUNCTION(sample4_counter) {
RETURN_LONG(++sample4_globals.counter);
}
```
是不是看起来很简单,但是,在线程版本中将无法正常工作。那么我们来看看怎么在线程环境中完成这个功能吧:
```
PHP_FUNCTION(sample4_counter)
{
#ifdef ZTS
RETURN_LONG(++TSRMG(sample4_globals_id, \
zend_sample4_globals*, counter));
#else
/* non-ZTS */
RETURN_LONG(++sample4_globals.counter);
#endif
}
```
看起来很丑对吗?想象一下,在你的整个代码库中,这些IFDEF指令在每一个线程安全的全局访问时穿插。它会看起来比Perl更糟糕!这就是为什么所有 的核心扩展,都有使用一个额外的宏观层抽象这种情况。我们可以在php\_sample4.h中找到下面这段代码:
```
#ifdef ZTS
#include "TSRM.h"
#define SAMPLE4_G(v) TSRMG(sample4_globals_id, zend_sample4_globals*, v)
#else
#define SAMPLE4_G(v) (sample4_globals.v)
#endif
```
使用它们会让你的方法看起来更简洁:
```
PHP_FUNCTION(sample4_counter) {
RETURN_LONG(++SAMPLE4_G(counter));
}
```
看到\*G()这样的宏是不是有种似曾相识的感觉?也许以前你看到过EG()、CG()等宏,了解他们会让你对PHP的了解更深一步:
Accessor MacroAssociated DataEG()这个宏可以用来访问符号表,函数,资源信息和常量。CG()用来访问核心全局变量。PG()PHP全局变量。我们知道php.ini会映射一个或者多个PHP全局结构。举几个使用这个宏的例子:PG(register\_globals), PG(safe\_mode), PG(memory\_limit)FG()文件全局变量。大多数文件I/O或相关的全局变量的数据流都塞进标准扩展出口结构。## links
- [目录](preface.md)
- 12.3 [常量](12.3.html)
- 12.5 [PHP语言中的超级全局变量](12.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 小结