【原创】PHP扩展开发入门




PHP扩展开发入门php

做者:wf (360电商技术组)web

 

 

    在咱们编写本身的第一个php扩展以前,先了解一下php的整体架构和执行机制。apache


    php的架构如图1所看到的。编程

当中一个重要的就是SAPI(server端应用编程端口),它使得PHP可以和其它应用进行数据交互,把外部错综复杂的外部环境进行抽象化,为内部的php提供一套固定和统一的接口。使得php自身不受外部影响,保持必定的独立性。常见的SAPI有CGI。FastCGI。Shell的CLI,apache的mod_php5,IIS的ISAPI。微信

    另一个很是重要就是ZendEngine。Zend Engine是官方提供的PHP实现的核心,提供了语言实现上的基础设施,其它比較知名的还有facebook的hiphop实现。架构

好比PHP的语法实现。脚本的编译执行环境。扩展机制以及内存管理等。框架

咱们在后面编写php扩展时,也将基于Zend Engine。函数

    PHP3时代仍是採用边解释边执行的执行方式,这种方式执行效率很是受影响,其次代码整体耦合度比較高。可扩展性也不够好。所以随着php在web应用开发中的普及,因而ZeevSuraski和Andi Gutmans决定重写代码以解决这两个问题。终于他们俩把该项技术的核心引擎命名为Zend Engine 。post

    Zend Engine最基本的特性就是把PHP的边解释边执行的执行方式改成先预编译(Compile),再执行(Execute)。学习

这二者的分开给 PHP 带来了革命性的变化:执行效率大幅提升。由于实行了功能分离。减小了模块间耦合度,可扩展性也大大加强。

    眼下PHP的实现和Zend Engine之间的关系很是紧密。好比很是多PHP扩展都是使用的Zend API,而Zend正是PHP语言自己的实现,PHP仅仅是使用Zend这个内核来构建PHP语言的,而PHP扩展大都使用Zend API,这就致使PHP的很是多扩展和Zend引擎耦合在一块儿了,后来才有PHP核心开发人员就提出将这种耦合解开的建议。只是如下咱们还如下在Zend Engine的基础上開始编写咱们第一个简单的php扩展。

 

1.配置文件

 

    每一个PHP扩展都至少需要一个配置文件和一个源文件。配置文件用来告诉编译器应该编译哪几个文件。以及编译本扩展是否需要的其它库文件。

    在php源代码文件夹的ext文件夹下建立一个新的文件,扩展的名字取做myfirst。而后在这个文件夹下建立一个config.m4文件,并输入如下内容:

 

PHP_ARG_ENABLE(

   myfirst,

   [Whether to enable the "myfirst" extension],

   [enable-myfirst    Enable"myfirst" extension support])

if test $PHP_Myfirst !="no"; then

   PHP_SUBST(Myfirst_SHARED_LIBADD)

   PHP_NEW_EXTENSION(myfirst, myfirst.c, $ext_shared)

fi

   

上面PHP_ARG_ENABLE函数有三个參数,第一个參数是咱们的扩展名(注意不用加引號),第二个參数是当咱们执行./configure脚本时显示的内容。最后一个參数则是咱们在调用./configure--help时显示的帮助信息。PHP_SUBST函数仅仅是php官方对autoconf中AC_SUBST函数的一层封装。

PHP_NEW_EXTENSION函数声明了这个扩展的名称、需要的源文件名称、扩展的编译形式。假设扩展使用了多个文件。可以将文件名称罗列在函数的參数里,如:PHP_NEW_EXTENSION(sample, sample.c sample2.c sample3.c, $ext_shared)最后的$ext_shared參数用来声明这个扩展为动态库。在php执行时动态载入的。

 

2.源文件

    在完毕了配置文件后。如下的就是完毕扩展主逻辑的头文件和C文件。

    头文件

 

//php_myfirst.h

#ifndef Myfirst_H

#define Myfirst_H

//载入config.h。假设配置了的话

#ifdef HAVE_CONFIG_H

#include "config.h"

#endif

//载入php头文件

#include "php.h"

#define phpext_myfirst_ptr &myfirst_module_entry

extern zend_module_entrymyfirst_module_entry;

#endif

 

    C文件

//myfirst.c

#include "php_myfirst.h"

//module entry

zend_module_entrymyfirst_module_entry = {

#if ZEND_MODULE_API_NO >= 20010901

     STANDARD_MODULE_HEADER,

#endif

    "myfirst",//扩展名称

    NULL, /*Functions */

    NULL, /*MINIT */

    NULL, /*MSHUTDOWN */

    NULL, /*RINIT */

    NULL, /*RSHUTDOWN */

    NULL, /*MINFO */

#if ZEND_MODULE_API_NO >= 20010901

    "2.1",//扩展的版本号

#endif

    STANDARD_MODULE_PROPERTIES

};

 

#ifdef COMPILE_DL_Myfirst

ZEND_GET_MODULE(myfirst)

#endif

 

3.扩展编译

    准备好了扩展需要编译的源文件。接下来需要的即是把它们编译成目标文件了。

    第一步:依据config.m4文件使用phpize生成一个configure脚本、Makefile等文件:

 

$ phpize

PHP Api Version: 20041225

Zend Module Api No: 20050617

Zend Extension Api No: 220050617

   

现在查看扩展所在的文件夹,会发现phpize程序依据config.m4里的信息生成了不少编译php扩展必须的文件,比方makefiles等。

    第二部:执行./configure脚本。而后执行make; make test就能够。

假设没有错误。那么在module文件夹如下便会生成扩展的目标文件 myfirst.so,这里由于以前咱们在配置文件中写申明的是动态扩展,因此会被编译成动态库。

    现在,先让咱们执行一下PHP源代码根文件夹下的./buildconf —force,再执行./configure --help命令。会发现myfirst扩展的信息已经出现了。

    为了使PHP可以找到需要的扩展文件,咱们需要把编译好的so文件拷贝到PHP的扩展文件夹下,并在php.ini中配置:

extension_dir=/usr/local/lib/php/modules/

extension=myfirst.so

 

这样php就会在每次启动的时候本身主动载入咱们的扩展了。

 

4.扩展功能函数编写

    前面咱们已经生成好了一份扩展框架,但它是没有什么实际做用的,咱们还需要编写详细的功能函数。

#definePHP_FUNCTION         ZEND_FUNCTION

#defineZEND_FUNCTION(name)    ZEND_NAMED_FUNCTION(ZEND_FN(name))

#defineZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)

#define ZEND_FN(name)                zif_##name

    当中zif是zend internal function的意思,zif前缀是可供PHP语言调用的函数在C语言中的函数名称前缀。

 

ZEND_FUNCTION(myfirst_hello)

{

    php_printf("HelloWorld!\n");

}

   

上面的函数在C语言中宏展开后是这种:

 

voidzif_myfirst_hello(INTERNAL_FUNCTION_PARAMETERS)

{

    php_printf("HelloWorld!\n");

}

   

函数的功能已经实现了,但是还不能在程序中调用。由于这个函数尚未在扩展模块中注冊。

现在看下扩展中zend_module_entry

myfirst_module_entry(它是联系C扩展与PHP语言的重要纽带)中/*Functions*/的值为NULL。这是以前尚未编写函数。

现在咱们可以将编写的函数赋值给它了。这个值需要是zend_function_entry[]类型:

 

static zend_function_entrymyfirst_functions[] = {

     ZEND_FE(myfirst_hello, NULL)

    { NULL, NULL,NULL }

};

   

当中最后的{NULL,NULL,NULL}是固定不变的。ZEND_FE()宏函数是对myfirst_hello函数的一个声明,假设有多个函数,可以直接以类似的形式加入到{NULL,NULL,NULL}以前,注意每一个之间不需要加逗号。确保一切无误后,咱们替换掉zend_module_entry里的原有成员,现在应该是这种:

 

ZEND_FUNCTION(myfirst_hello)

{

    php_printf("HelloWorld!\n");

}

 

static zend_function_entrymyfirst_functions[] = {

    ZEND_FE(myfirst_hello,       NULL)

    { NULL, NULL,NULL }

};

 

zend_module_entrymyfirst_module_entry = {

#if ZEND_MODULE_API_NO >= 20010901

     STANDARD_MODULE_HEADER,

#endif

    "myfirst",//扩展名称。

    myfirst_functions,/* Functions */

    NULL, /*MINIT */

    NULL, /*MSHUTDOWN */

    NULL, /*RINIT */

    NULL, /*RSHUTDOWN */

    NULL, /*MINFO */

#if ZEND_MODULE_API_NO >= 20010901

    "2.1",//这个地方是咱们扩展的版本号

#endif

    STANDARD_MODULE_PROPERTIES

};

   

这样咱们就完毕扩展的一个简单功能,而后再又一次configure、make、make test。并复制.so文件到extension dir文件夹。

最后写一个脚本在命令行測试,应该可以输出helloworld了。

 

<?php

myfirst_hello();

?>

 

 


-------------------------------------------------------------------------------------

黑夜路人,一个关注开源技术、乐于学习、喜欢分享的程序猿


博客:http://blog.csdn.net/heiyeshuwu

微博:http://weibo.com/heiyeluren

微信:heiyeluren2012  

想获取不少其它IT开源技术相关信息。欢迎关注微信!

微信二维码扫描高速关注本号码:


相关文章
相关标签/搜索