首頁  >  文章  >  後端開發  >  [翻譯][php擴充開發與嵌入式]第18章-php的擴充自動生成

[翻譯][php擴充開發與嵌入式]第18章-php的擴充自動生成

黄舟
黄舟原創
2017-02-10 10:36:591536瀏覽


擴展生成

毫無疑問你已經註意到, 每個php擴展都包含一些非常公共的並且非常單調的結構和文件. 當開始一個新擴展開發的時候, 如果這些擴展公共的結構已經存在, 我們只用考慮填充功能代碼是很有意義的. 為此, 在php中包含了一個簡單但是很有用的shell腳本.

ext_skel

切換到你的php原始碼樹下ext/目錄中, 執行下面的命令:

jdoe@devbox:/home/jdoe/cvs/php-src/ext/$ ./ext_skel extname=sample7

稍等便可, 輸出一些文字, 你將看到下面的這些輸出:

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.

reee此刻觀察ext/sample7目錄, 你將看到在第5章"你的第一個擴展"中你編寫的擴展骨架代碼的註釋版本. 只是現在你還不能編譯它; 不過只需要對config.m4做少許修改就可以讓它工作了, 這樣你就可以避免第5章中你所做的大部分工作.

生成函數原型

如果你要編寫一個對第三方庫的包裝擴展, 那麼你就已經有了一個函數原型及基本行為的機器刻度版本的描述(頭文件), 通過傳遞一個額外的參數給./ext_skel, 它將自動的掃描你的頭文件並創建對應於接口的簡單PHP_FUCNTION()區塊. 以下是使用./ext_skel指令解析zlib頭:

jdoe@devbox:/home/jdoe/cvs/php-src/ext/$ ./ext_skel extname=sample8 \
proto=/usr/local/include/zlib/zlib.h

現在在ext/sample8/sample8.c中, 你就可以看到許多PHP_FUNCTION()定義, 每個zlib函數對應一個. 要注意, 骨架生成程序會對某些未知資源類型產生警告消息. 你需要對這些函數特別注意, 並且為了將這些內部的複雜結構體和用戶空間可訪問的變量關聯起來, 可能會需要使用你在第9章"資源資料型別"中學到的知識.

PECL_Gen

還有一個更完善但也更複雜的程式碼產生器: PECL_Gen, 可以在PECL(http:// www.php.cn/)中找到它, 使用pear install PECL_Gen命令可以安裝它.

譯者註: PECL_Gen已經遷移為CodeGen_PECL(http://www.php.cn/). 本章涉及代碼測試使用CodeGen_PECL的版本資訊為: "php 1.1.3, Copyright (c) 2003-2006 Hartmut Holzgraefe", 如果您的環境使用有問題, 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">
   <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)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn