💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# PECL_Gen 还有一种更加完善但也更加复杂的代码生成器: PECL_Gen, 可以在PECL(http:// pecl.php.net)中找到它, 使用pear install PECL_Gen命令可以安装它. ````c 译者注: 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 下面是最简单的扩展定义文件: ````c <?xml version="1.0" encoding="utf-8" ?> <extension name="sample9"> <functions> <function name="sample9_hello_world" role="public"> <code><![CDATA[php_printf("Hello World!");]]> </code> </function> </functions></extension>译注: 请注意, 译者使用的原著中第一行少了后面的问号, 导致不能使用, 加上就OK. ```` 通过PECL_Gen命令运行这个文件: ````c 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文件的元素. ````c <?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可以使用<deps>定义各种类型的依赖完成扫描工作. 默认情况下, 列在 <deps>标签下的依赖会同时应用到Unix和win32构建中, 除非显式的是否用platform属性 指定某个目标 ````c <?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>标签到<deps>块中, 则不仅配置选项被修改为--with-extname, 而且同时需要扫描 头文件: ````c <deps platform="unix"> <with defaults="/usr:/usr/local:/opt" testfile="include/zlib/zlib.h">zlib headers</with> </deps> ```` #### 库 必须的库也列在<deps>下, 使用<lib>标签. ````c <deps platform="all"> <lib name="ssleay" platform="win32"/> <lib name="crypto" platform="unix"/> <lib name="z" platform="unix" function="inflate"/> </deps> ```` 在前面两个例子中, 只是检查了库是否存在; 第三个例子中, 库将被真实的加载并扫描 以确认inflate()函数是否定义. 尽管<deps>标签实际已经命名了目标平台, 但<lib>标签也有⼀个platform属性可以覆盖 <deps>标签的platform设置. 当它们混合使用的时候要格外小心. #### <header> 此外, 需要包含的文件也可以通过在<deps>块中使用<header>标签在你的代码中追 加⼀个#include指令列表. 要强制某个头先包含, 可以在<header>标签上增加属性 prepend="yes". 和<lib>依赖类似, <header>也可以严格限制平台: ````c <deps> <header name="sys/types.h" platform="unix" prepend="yes"/> <header name="zlib/zlib.h"/> </deps> 译注: 经测试, 译者的环境<header>标签不支持platform属性. ```` #### 常量 用户空间常量使用<constants>块中的一个或多个<constant>标签定义. 每个标签需 要一个name和⼀个value属性, 以及⼀个值必须是int, float, string之一的type属性. ````c <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阶段手动设置. ````c <globals> <global name="greeting" type="char *"/> <global name="greeting_was_issued" type="zend_bool" value="1"/> </globals> ```` #### INI选项 要绑定线程安全的全局变量到php.ini设置, 则需要使用<phpini>标签而不是<globa>. 这个标签需要两个额外的参数: onupdate="updatemethod"标识INI的修改应该怎样处理, access="mode"和第13章"INI设置"中介绍的模式含义相同, "mode"值可以是: all, user, perdir, system. ````c <globals> <phpini name="mysetting" type="int" value="42" onupdate="OnUpdateLong" access="all"/> </globals> ```` #### 函数 你已经看到了最基本的函数定义; 不过, <function>标签在PECL_Gen的specfile中实 际上支持两种不同类型的函数. 两个版本都支持你已经在<extension>级别上使用过的<summary>和<description>属 性; 两种类型都必须的元素是<code>标签, 它包含了将要被放入你的源代码文件中的原文C语言代码. #### role="public" 如你所想, 所有定义为public角色的函数都将包装恰当的PHP_FUNCTION()头和花括 号, 对应到扩展的函数表向量中的条目. 除了其他函数支持的标签, public类型还允许指定一个<proto>标签. 这个标签的格式 应该匹配php在线手册中的原型展示, 它将被文档生成器解析. ````c <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> <code> <![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; ]]> </code> </function> </functions> ```` #### role="internal" 内部函数涉及5个zend_module_entry函数: MINIT, MSHUTDOWN, RINIT, RSHUTDOWN, MINFO. 如果指定的名字不是这5个之一将会产生pecl-gen无法处理的错误. ````c <functions> <function role="internal" name="MINFO"> <code> <![CDATA[ php_info_print_table_start(); php_info_print_table_header(2, "Column1", "Column2"); php_info_print_table_end(); ]]> </code> </function> </functions> ```` #### 自定义代码 所有其他需要存在于你的扩展中的代码都可以使用<code>标签包含. 要放置任意代码 到你的目标文件extname.c中, 使用role="code"; 或者说使用role="header"将代码放到目标 文件php_extname.h中. 默认情况下, 代码将放到代码或头文件的底部, 除非指定了 position="top"属性. ````c <code role="header" position="bottom"> <![CDATA[ typedef struct _php_sample9_data { long val; } php_sample9_data; ]]> </code> <code role="code" position="top"> <![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; } ]]> </code> 译注: 译者的环境中不支持原著中<code>标签的name属性. ```` ## links * [目录](<preface.md>) * 18.1 [ext_skel](<18.1.md>) * 18.3 [小结](<18.3.md>)