Maison > Article > développement back-end > [Français] [Développement d'extensions php et embarquées] Chapitre 18 - Génération automatique d'extensions php
Génération d'extensions
Comme vous l'avez sans doute remarqué, chaque extension php contient des éléments très courants et très structures et fichiers monotones. Lors du démarrage du développement d'une nouvelle extension, il est logique de ne penser à remplir le code fonctionnel que si ces structures communes existent déjà. Pour cela, un script shell simple mais utile.
ext_skel
Basculez vers votre arborescence de code source php ext/ Dans le répertoire, exécutez la commande suivante :
jdoe@devbox:/home/jdoe/cvs/php-src/ext/$ ./ext_skel extname=sample7
Attendez un instant et affichez du texte. Vous verrez le résultat suivant :
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.
Regardez maintenant le répertoire ext/sample7, vous verrez que dans le chapitre 5 « Votre première extension » vous avez écrit une version annotée du code squelette étendu. Vous ne pouvez tout simplement pas encore le compiler, mais vous en avez seulement besoin ; pour apporter quelques modifications à config.m4 pour le faire fonctionner, afin d'éviter la plupart du travail que vous avez effectué dans le chapitre 5.
Générer un prototype de fonction
Si vous souhaitez écrire une extension wrapper pour une bibliothèque tierce, alors vous disposez d'un prototype de fonction et d'une description (fichier d'en-tête) d'une version à l'échelle machine du comportement de base , en passant un argument supplémentaire à ./ext_skel, il analysera automatiquement votre fichier d'en-tête et créera un simple bloc PHP_FUCNTION() correspondant à l'interface. Voici l'utilisation de la directive ./ext_skel pour analyser l'en-tête zlib :
jdoe@devbox:/home/jdoe/cvs/php-src/ext/$ ./ext_skel extname=sample8 \ proto=/usr/local/include/zlib/zlib.h
Maintenant, dans ext/sample8/sample8.c, vous pouvez voir de nombreuses définitions PHP_FUNCTION(), chacune. Il y a une fonction zlib pour chacune. Sachez que le générateur de squelette générera un avertissement. messages pour certains types de ressources inconnus. Vous devez accorder une attention particulière à ces fonctions, et afin d'associer ces structures complexes internes à des variables accessibles dans l'espace utilisateur, vous devrez peut-être utiliser ce que vous avez appris au chapitre 9 « Types de données de ressources ».
PECL_Gen
Il existe également un générateur de code plus complet mais plus complexe : PECL_Gen, que l'on retrouve dans PECL (http://www.php.cn/) et peut être installé à l'aide de la commande pear install PECL_Gen
Note du traducteur : PECL_Gen a été migré vers CodeGen_PECL (http). ://www.php.cn/). Les informations de version de CodeGen_PECL utilisées dans les tests de code dans ce chapitre sont : "php 1.1.3, Copyright (c) 2003-2006 Hartmut Holzgraefe", si vous rencontrez des problèmes lors de l'utilisation de l'environnement, veuillez vous référer à la configuration de l'environnement du traducteur dans la séquence de traduction
Une fois l'installation terminée, il peut fonctionner comme ext_skel, accepte les mêmes paramètres d'entrée, produit à peu près la même sortie. , ou si un fichier de définition XML complet est fourni, produit une version plus robuste et entièrement compilable de l'extension. PECL_Gen ne vous évite pas d'écrire la fonction principale de l'extension mais fournit un moyen facultatif de générer efficacement du code squelette étendu.
specfile.xml
Ce qui suit est le fichier de définition d'extension le plus simple :
<?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)!