Heim > Artikel > Backend-Entwicklung > [Übersetzung] [PHP-Erweiterungsentwicklung und eingebettet] Kapitel 18 – Automatische Generierung von PHP-Erweiterungen
Erweiterungsgenerierung
Wie Sie zweifellos bemerkt haben, enthält jede PHP-Erweiterung einige sehr häufige und sehr häufige Wenn Sie mit der Entwicklung einer neuen Erweiterung beginnen, ist es sinnvoll, nur dann über das Ausfüllen des Funktionscodes nachzudenken, wenn diese gemeinsamen Strukturen bereits vorhanden sind.
ext_skel
Wechseln Sie zu Ihrem PHP-Quellcodebaum ext/ Führen Sie im Verzeichnis den folgenden Befehl aus:
jdoe@devbox:/home/jdoe/cvs/php-src/ext/$ ./ext_skel extname=sample7
Warten Sie einen Moment und geben Sie einen Text aus. Sie sehen die folgende Ausgabe:
To use your new extension, you will have to execute the following steps: 1. $ cd .. 2. $ vi ext/sample7/config.m4 3. $ ./buildconf 4. $ ./configure [with|enable]-sample7 5. $ make 6. $ ./php -f ext/sample7/sample7.php 7. $ vi ext/sample7/sample7.c 8. $ make Repeat steps 3-6 until you are satisfied with ext/sample7/config.m4 and step 6 confirms that your module is compiled into PHP. Then, start writing code and repeat the last two steps as often as necessary.
Schauen Sie sich nun das Verzeichnis ext/sample7 an. Sie werden sehen, dass Sie in Kapitel 5 „Ihre erste Erweiterung“ eine kommentierte Version des erweiterten Grundcodes geschrieben haben. Sie können ihn nur noch nicht kompilieren um ein paar Änderungen an config.m4 vorzunehmen, damit es funktioniert, damit Sie den Großteil der Arbeit, die Sie in Kapitel 5 erledigt haben, vermeiden können.
Funktionsprototyp generieren
Wenn Sie eine Wrapper-Erweiterung für eine Bibliothek eines Drittanbieters schreiben möchten, benötigen Sie einen Funktionsprototyp und eine Beschreibung (Header-Datei) einer Maschinenversion des Grundverhaltens Durch die Übergabe eines zusätzlichen Arguments an ./ext_skel wird Ihre Header-Datei automatisch gescannt und ein einfacher PHP_FUCNTION()-Block erstellt, der der Schnittstelle entspricht. Im Folgenden wird die ./ext_skel-Direktive zum Parsen des zlib-Headers verwendet:
jdoe@devbox:/home/jdoe/cvs/php-src/ext/$ ./ext_skel extname=sample8 \ proto=/usr/local/include/zlib/zlib.h
Jetzt können Sie in ext/sample8/sample8.c viele PHP_FUNCTION()-Definitionen sehen, für jede gibt es eine zlib-Funktion. Beachten Sie, dass der Skelettgenerator eine Warnung generiert Sie müssen diesen Funktionen besondere Aufmerksamkeit schenken und um diese internen komplexen Strukturen mit vom Benutzer zugänglichen Variablen zu verknüpfen, müssen Sie möglicherweise das verwenden, was Sie in Kapitel 9 „Ressourcendatentypen“ gelernt haben.
PECL_Gen
Es gibt auch einen vollständigeren, aber komplexeren Codegenerator: PECL_Gen, der in zu finden ist PECL (http://www.php.cn/) und kann mit dem Befehl „pear install PECL_Gen“ installiert werden
Anmerkung des Übersetzers: PECL_Gen wurde auf CodeGen_PECL (http ://www.php.cn/). Die Versionsinformationen von CodeGen_PECL, die in diesem Kapitel zum Testen des Codes verwendet werden, lauten: „php 1.1.3, Copyright (c) 2003-2006 Hartmut Holzgraefe“, wenn Sie Probleme bei der Verwendung der Umgebung haben, Bitte beachten Sie die Umgebungskonfiguration des Übersetzers in der Übersetzungssequenz.
Sobald die Installation abgeschlossen ist, kann sie wie ext_skel ausgeführt werden, akzeptiert dieselben Eingabeparameter und erzeugt ungefähr die gleiche Ausgabe , oder wenn eine vollständige XML-Definitionsdatei bereitgestellt wird, wird eine robustere und vollständig kompilierbare Version der Erweiterung erstellt. PECL_Gen erspart Ihnen nicht das Schreiben der Erweiterungskernfunktion, sondern bietet eine optionale Möglichkeit, erweiterten Skelettcode effizient zu generieren.
specfile.xml
Das Folgende ist die einfachste Erweiterungsdefinitionsdatei:
<?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命令运行这个文件:
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可以使用13935679349a1ec3c6b24bf69d8dc011定义各种类型的依赖完成扫描工作. 默认情况下, 列在13935679349a1ec3c6b24bf69d8dc011标签下的依赖会同时应用到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样式的配置选项. 通过增加一个或多个3f7bdb502a84bf1a14abe8c8945ca31d标签到13935679349a1ec3c6b24bf69d8dc011块中, 则不仅配置选项被修改为--with-extname, 而且同时需要扫描头文件:
deps platform="unix"> <with defaults="/usr:/usr/local:/opt" testfile="include/zlib/zlib.h">zlib headers</with> </deps>
库
必须的库也列在13935679349a1ec3c6b24bf69d8dc011下, 使用00074440385736cfd09597d9818e6839标签.
<deps platform="all"> <lib name="ssleay" platform="win32"/> <lib name="crypto" platform="unix"/> <lib name="z" platform="unix" function="inflate"/> </deps>
在前面两个例子中, 只是检查了库是否存在; 第三个例子中, 库将被真实的加载并扫描以确认inflate()函数是否定义.
尽管13935679349a1ec3c6b24bf69d8dc011标签实际已经命名了目标平台, 但00074440385736cfd09597d9818e6839标签也有一个platform属性可以覆盖13935679349a1ec3c6b24bf69d8dc011标签的platform设置. 当它们混合使用的时候要格外小心.
1aa9e5d373740b65a0cc8f0a02150c53
此外, 需要包含的文件也可以通过在13935679349a1ec3c6b24bf69d8dc011块中使用1aa9e5d373740b65a0cc8f0a02150c53标签在你的代码中追加一个#include指令列表. 要强制某个头先包含, 可以在1aa9e5d373740b65a0cc8f0a02150c53标签上增加属性prepend="yes". 和00074440385736cfd09597d9818e6839依赖类似, 1aa9e5d373740b65a0cc8f0a02150c53也可以严格限制平台:
<deps> <header name="sys/types.h" platform="unix" prepend="yes"/> <header name="zlib/zlib.h"/> </deps>
译注: 经测试, 译者的环境1aa9e5d373740b65a0cc8f0a02150c53标签不支持platform属性.
常量
用户空间常量使用7e75141f12bcfb6308da8eb0ac04b6c2块中的一个或多个826c8bb1a1396ec98291fb764e7d0752标签定义. 每个标签需要一个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设置, 则需要使用7ff739baee71802d5a32f10ed81e75df标签而不是0605e9470624fea4d1cc217279bc6161. 这个标签需要两个额外的参数: 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>
函数
你已经看到了最基本的函数定义; 不过, 8fd0b0dd077e9e3b89425c7034f15437标签在PECL_Gen的specfile中实际上支持两种不同类型的函数.
两个版本都支持你已经在d48e197d6ef23c023982d3f523fa26d2级别上使用过的631fb227578dfffda61e1fa4d04b7d25和8b55addfb40ddf4a384b1010d729e503属性; 两种类型都必须的元素是ffbe95d20f3893062224282accb13e8f标签, 它包含了将要被放入你的源代码文件中的原文C语言代码.
role="public"
如你所想, 所有定义为public角色的函数都将包装恰当的PHP_FUNCTION()头和花括号, 对应到扩展的函数表向量中的条目.
除了其他函数支持的标签, public类型还允许指定一个b59f493af26f496bf5bf5aa84cbcd7de标签. 这个标签的格式应该匹配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> <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无法处理的错误.
<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>
自定义代码
所有其他需要存在于你的扩展中的代码都可以使用ffbe95d20f3893062224282accb13e8f标签包含. 要放置任意代码到你的目标文件extname.c中, 使用role="code"; 或者说使用role="header"将代码放到目标文件php_extname.h中. 默认情况下, 代码将放到代码或头文件的底部, 除非指定了position="top"属性.
<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>
译注: 译者的环境中不支持原著中ffbe95d20f3893062224282accb13e8f标签的name属性.
小结
使用本章讨论的工具, 你就可以快速的开发php扩展, 并且让你的代码相比手写更加不容易产生bug. 现在是时候转向将php嵌入到其他项目了. 剩下的章节中, 你将利用php环境和强大的php引擎为你的已有项目增加脚本能力, 使它可以为你的客户提供更多更有用的功能.
以上就是的内容,更多相关内容请关注PHP中文网(www.php.cn)!