# 18.2 PECL\_Gen
# PECL\_Gen
还有一种更加完善但也更加复杂的代码生成器: PECL\_Gen, 可以在PECL(http:// pecl.php.net)中找到它, 使用pear install PECL\_Gen命令可以安装它.
```
译者注: PECL_Gen已经迁移为CodeGen_PECL(http://pear.php.net/package/ CodeGen_PECL). 本章涉及代码测试使用CodeGen_PECL的版本信息为: "php 1.1.3, Copyright (c) 2003-2006 Hartmut Holzgraefe", 如果您的环境使用有问题, 请参考译序中译者的环境配置.
```
⼀旦安装完成, 它就可以像ext\_skel一样运行, 接受相同的输入参数, 产生大致相同的 输出, 或者如果提供了一个完整的xml定义文件, 则产生一个更加健壮和完整可编译版本的 扩展. PECL\_Gen并不会节省你编写扩展核心功能的时间; 而是提供⼀种可选的方式高效 的生成扩展骨架代码.
#### specfile.xml
下面是最简单的扩展定义文件:
```
<?xml version="1.0" encoding="utf-8" ?> <extension name="sample9"> <functions> <function name="sample9_hello_world" role="public"> <![CDATA[php_printf("Hello World!");]]> </function> </functions></extension>译注: 请注意, 译者使用的原著中第一行少了后面的问号, 导致不能使用, 加上就OK.
```
通过PECL\_Gen命令运行这个文件:
```
jdoe@devbox:/home/jdoe/cvs/php-src/ext/$ pecl-gen specfile.xml
```
则会产生一个名为sample9的扩展, 并暴露一个用户空间函数sample9\_hello\_world().
#### 关于扩展
除了你已经熟悉的功能文件, PECL\_Gen还会产生⼀个package.xml文件 它可以用于 pear安装. 如果你计划发布包到PECL库, 或者哪怕你只是想要使用pear包系统交付内容, 有这个文件都会很有用.
总之, 你可以在PECL\_Gen的specfile.xml中指定多数package.xml文件的元素.
```
<?xml version="1.0" encoding="UTF-8" ?>
<extension name="sample9">
<summary>Extension 9 generated by PECL_Gen</summary>
<description>Another sample of PHP Extension Writing</description>
<maintainers>
<maintainer>
<name>John D. Bookreader</name>
<email>jdb@example.com</email>
<role>lead</role>
</maintainer>
</maintainers>
<release>
<version>0.1</version>
<date>2006-01-01</date>
<state>beta</state>
<notes>Initial Release</notes>
</release>
...
</extension>
```
当PECL\_Gen创建扩展时, 这些信息将被翻译到最终的package.xml文件中. 依赖
#### 依赖
如你在第17章"配置和链接"中所见, 依赖可以扫描出来用于config.m4和config.w32文 件. PECL\_Gen可以使用定义各种类型的依赖完成扫描工作. 默认情况下, 列在 标签下的依赖会同时应用到Unix和win32构建中, 除非显式的是否用platform属性 指定某个目标
```
<?xml version="1.0" encoding="UTF-8" ?>
<extension name="sample9">
...
<deps platform="unix">
<! UNIX specific dependencies >
</deps>
<deps platform="win32">
<! Win32 specific dependencies >
</deps>
<deps platform="all">
<! Dependencies that apply to all platforms >
</deps>
...
</extension>
```
#### with
通常, 扩展在配置时使用--enable-extname样式的配置选项. 通过增加⼀个或多个 标签到块中, 则不仅配置选项被修改为--with-extname, 而且同时需要扫描 头文件:
```
<deps platform="unix">
<with defaults="/usr:/usr/local:/opt"
testfile="include/zlib/zlib.h">zlib headers</with>
</deps>
```
#### 库
必须的库也列在下, 使用标签.
```
<deps platform="all">
<lib name="ssleay" platform="win32"/>
<lib name="crypto" platform="unix"/>
<lib name="z" platform="unix" function="inflate"/>
</deps>
```
在前面两个例子中, 只是检查了库是否存在; 第三个例子中, 库将被真实的加载并扫描 以确认inflate()函数是否定义.
尽管标签实际已经命名了目标平台, 但标签也有⼀个platform属性可以覆盖 标签的platform设置. 当它们混合使用的时候要格外小心.
####
此外, 需要包含的文件也可以通过在块中使用
标签在你的代码中追 加⼀个#include指令列表. 要强制某个头先包含, 可以在标签上增加属性 prepend="yes". 和依赖类似, 也可以严格限制平台:
```
<deps>
<header name="sys/types.h" platform="unix" prepend="yes"/>
<header name="zlib/zlib.h"/>
</deps>
译注: 经测试, 译者的环境<header>标签不支持platform属性.
```
#### 常量
用户空间常量使用块中的一个或多个标签定义. 每个标签需 要一个name和⼀个value属性, 以及⼀个值必须是int, float, string之一的type属性.
```
<constants>
<constant name="SAMPLE9_APINO" type="int" value="20060101"/>
<constant name="SAMPLE9_VERSION" type="float" value="1.0"/>
<constant name="SAMPLE9_AUTHOR" type="string" value="John Doe"/>
</constants>
```
#### 全局变量
线程安全全局变量的定义方式几乎相同. 唯⼀的不同在于type参数需要使用C语言原 型而不是php用户空间描述. ⼀旦定义并构建, 全局变量就可以使用第12章"启动, 终止, 以 及其中的⼀些点"中学习的EXTNAME\_G(global\_name)的宏用法进行访问. 在这里, value属性表示变量在请求启动时的默认值. 要注意在specfile.xml中这个默认值只能指定为简单 的标量数值. 字符串和其他复杂结构应该在RINIT阶段手动设置.
```
<globals>
<global name="greeting" type="char *"/>
<global name="greeting_was_issued" type="zend_bool" value="1"/>
</globals>
```
#### INI选项
要绑定线程安全的全局变量到php.ini设置, 则需要使用标签而不是. 这个标签需要两个额外的参数: onupdate="updatemethod"标识INI的修改应该怎样处理, access="mode"和第13章"INI设置"中介绍的模式含义相同, "mode"值可以是: all, user, perdir, system.
```
<globals>
<phpini name="mysetting" type="int" value="42" onupdate="OnUpdateLong" access="all"/>
</globals>
```
#### 函数
你已经看到了最基本的函数定义; 不过, 标签在PECL\_Gen的specfile中实 际上支持两种不同类型的函数.
两个版本都支持你已经在级别上使用过的
和属 性; 两种类型都必须的元素是`标签, 它包含了将要被放入你的源代码文件中的原文C语言代码.`#### role="public"
如你所想, 所有定义为public角色的函数都将包装恰当的PHP\_FUNCTION()头和花括 号, 对应到扩展的函数表向量中的条目.
除了其他函数支持的标签, public类型还允许指定一个标签. 这个标签的格式 应该匹配php在线手册中的原型展示, 它将被文档生成器解析.
```
<functions>
<function role="public" name="sample9_greet_me">
<summary>Greet a person by name</summary>
<description>Accept a name parameter as a string and say hello to that person.
Returns TRUE.</description>
<proto>bool sample9_greet_me(string name)</proto>
<![CDATA[
char *name;
int name_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
&name, &name_len) == FAILURE) {
return; }
php_printf("Hello ");
PHPWRITE(name, name_len);
php_printf("!\n");
RETURN_TRUE;
]]>
</function>
</functions>
```
#### role="internal"
内部函数涉及5个zend\_module\_entry函数: MINIT, MSHUTDOWN, RINIT, RSHUTDOWN, MINFO. 如果指定的名字不是这5个之一将会产生pecl-gen无法处理的错误.
```
<functions>
<function role="internal" name="MINFO">
<![CDATA[
php_info_print_table_start();
php_info_print_table_header(2, "Column1", "Column2");
php_info_print_table_end();
]]>
</function>
</functions>
```
#### 自定义代码
所有其他需要存在于你的扩展中的代码都可以使用`标签包含. 要放置任意代码 到你的目标文件extname.c中, 使用role="code"; 或者说使用role="header"将代码放到目标 文件php_extname.h中. 默认情况下, 代码将放到代码或头文件的底部, 除非指定了 position="top"属性.`
```
<![CDATA[
typedef struct _php_sample9_data {
long val;
} php_sample9_data;
]]>
<![CDATA[
static php_sample9_data *php_sample9_data_ctor(long value)
{
php_sample9_data *ret;
ret = emalloc(sizeof(php_sample9_data));
ret->val = value;
return ret;
}
]]>
译注: 译者的环境中不支持原著中标签的name属性.
```
## links
- [目录](preface.md)
- 18.1 [ext\_skel](18.1.html)
- 18.3 [小结](18.3.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 小结