【原创】PHP扩展开发入门

PHP扩展开发入门

作者:wf (360电商技术组)

在我们编写自己的第一个php扩展之前,先了解一下php的总体架构和执行机制。

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 。

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开源技术相关信息。欢迎关注微信!

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

?

时间: 2024-10-11 04:17:26

【原创】PHP扩展开发入门的相关文章

PHP7.1扩展开发入门

第1步: 首先从官网下载了PHP源码http://am1.php.net/distributions/php-7.1.3.tar.bz2 第2步: 解压后可以看到根目录下面的ext文件夹里有ext_skel文件, 这个ext_skel是shell文件,是可以运行的. cd到ext文件夹后,执行./ext_skel --extname=hello 就可以生成hello文件夹了 第3步: cd到hello文件夹里可以看到config.m4文件,打开找到这样的代码 dnl PHP_ARG_WITH(h

【原创】PHP扩展开发进阶

PHP扩展开发进阶 ?作者:wf (360电商技术) 在第一期PHP扩展开发入门中,简单的介绍了PHP的总体架构和执行机制,并具体说明了怎样开发和编译一个主要的PHP扩展,最后在PHP 5.3的环境下结合zend api高速编写了一个静态的PHP扩展. 然而仅仅编译一个PHP扩展是没有实际用途的,它仅仅是一个华丽的外壳,为了使扩展实现更强大的功能,须要在扩展中开发一些有用的功能函数.在这一章中,将会着重介绍PHP内核中变量的实现.在此基础上,才干将须要的功能,使用zend api在PHP扩展中实

AppleWatch开发入门六——Glance(预览)扩展的应用

AppleWatch开发入门六--Glance(预览)扩展的应用 一.简介 Glance是watchOS中类似iOS的today插件一样的预览扩展.提供了Glance功能的WatchApp可以在手表主页上唤起Glance,展示app相关信息,然而这个扩展只能作为展示作用,并不能进行太多的交互,界面的布局也有很大的限制,因此,Glance的应用主要在于展示备忘信息等.特点如下: 1.扩展的样式布局我们并不能完全个性化,只能通过系统模板来布局. 2.扩展中不能添加交互功能,只能展示信息,点击界面间唤

PHP扩展开发(1):入门

有关PHP扩展开发的文章.博客已经很多了,比较经典的有: TIPI项目(http://www.php-internals.com/,强烈推荐) <Extending and Embedding PHP>(中文翻译见http://www.walu.cc/phpbook/,强烈推荐) <PHP核心技术与最佳实践>一书有一章专门讲PHP扩展开发的,不过版本较老,可供参考. <PHP5权威指南>一书中也有一章专门讲到了PHP的扩展开发. 我准备在此系列博文中总结我有关PHP扩展

Node.js开发入门—Stream用法详解

Stream是Node.js中非常重要的一个模块,应用广泛.一个流是一个具备了可读.可写或既可读又可写能力的接口,通过这些接口,我们可以和磁盘文件.套接字.HTTP请求来交互,实现数据从一个地方流动到另一个地方的功能. 所有的流都实现了EventEmitter的接口,具备事件能力,通过发射事件来反馈流的状态.比如有错误发生时会发射"error"事件,有数据可被读取时发射"data"事件.这样我们就可以注册监听器来处理某个事件,达到我们的目的. Node.js定义了R

Node.js开发入门—使用AngularJS

做一个Web应用,一般都有前台和后台,Node.js可以实现后台,利用jade模板引擎也可以生成一些简单的前台页面,但要想开发出具有实际意义的现代Web应用,还得搭配一个Web前端框架. AngularJS是一个JavaScript前端框架,对于Node.js来说是一个完美的客户端库.AngularJS强制使用MVC(模型-视图-控制器,Model-View-Controller)框架,而它又使用JavaScript对象作为它的模型,和Node.js特别般配,用AngularJS的某些服务(比如

Node.js开发入门—使用jade模板引擎

在"Node.js开发入门--Express安装与使用"里,我们曾经使用express generator创建了一个HelloExpress网站,express工具为我们生成了基本的目录结构.模板.stylesheet.routers等.虽然那只是一个简单的HelloWorld类的小东西,可里面包含的内容还是有些多了,为了更好的理解Express所支持的jade模板引擎的用法,我们这次提供一个手动创建的小网站,可以显示来访者的IP,并对访问进行计数. 安装jade npm instal

Unity3D技术之本地客户端开发入门

欢迎来到unity学习.unity培训.unity企业培训教育专区,这里有很多U3D资源.U3D培训视频.U3D教程.U3D常见问题.U3D项目源码,我们致力于打造业内unity3d培训.学习第一品牌. 本地客户端开发入门 本地客户端 (NaCl) 是 Google 提供的新技术,其允许您在 Web 页面嵌入本地可执行代码,以便您在无需安装插件的情况下部署性能非常强的 web 应用程序.目前,NaCl 仅支持在 Windows.Mac OS X 和 Linux(含可用的 Chrome 操作系统支

JSP自定义标签开发入门《转》

JSP自定义标签开发入门 一般情况下开发jsp自定义标签需要引用以下两个包 import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; 首先我们需要大致了解开发自定义标签所涉及到的接口与类的层次结构(其中SimpleTag接口与SimpleTagSupport类是JSP2.0中新引入的). 目标1:自定义一个用表格显示用户信息的简单标签 效果图: 在jsp页面使用此自定义标签: 假设我们有一个UserInfo的javabean,那