【原创】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开源技术相关信息。欢迎关注微信!
微信二维码扫描高速关注本号码:
【原创】PHP扩展开发入门的更多相关文章
- PHP扩展开发入门
原文:http://www.orlion.ga/1090/ 写一个最简单的将字符串全部变成大写的函数: <?php function my_toupper($str) { return strt ...
- PHP7.1扩展开发入门
第1步: 首先从官网下载了PHP源码http://am1.php.net/distributions/php-7.1.3.tar.bz2 第2步: 解压后可以看到根目录下面的ext文件夹里有ext_s ...
- 【原创】PHP扩展开发进阶
PHP扩展开发进阶 作者:wf (360电商技术) 在第一期PHP扩展开发入门中,简单的介绍了PHP的总体架构和执行机制,并具体说明了怎样开发和编译一个主要的PHP扩展,最后在PHP 5.3的环境下 ...
- UWP开发入门(十)——通过继承来扩展ListView
本篇之所以起这样一个名字,是因为重点并非如何自定义控件,不涉及创建CustomControl和UserControl使用的Template和XAML概念.而是通过继承的方法来扩展一个现有的类,在继承的 ...
- PHP扩展开发(1):入门
有关PHP扩展开发的文章.博客已经很多了,比较经典的有: TIPI项目(http://www.php-internals.com/,强烈推荐) <Extending and Embedding ...
- Android Studio JNI开发入门教程
Android Studio JNI开发入门教程 2016-08-29 14:38 3269人阅读 评论(0) 收藏 举报 分类: JNI(3) 目录(?)[+] 概述 在Andorid ...
- PHP 扩展开发(将自己的一些代码封装成PHP扩展函数)
今天时间不多,先给个地址,能搜到我这篇blog的朋友先看看我最近在看的一些文章.资料吧: 我的环境是 lnmp1.1 的 (LNMP一键安装包),所以要进行PHP扩展开发首先应该对环境配置和shell ...
- JavaWeb学习总结(一)——JavaWeb开发入门
http://www.cnblogs.com/xdp-gacl/p/3729033.html 只为成功找方法,不为失败找借口! JavaWeb学习总结(一)--JavaWeb开发入门 一.基本概念 1 ...
- 基于Nodejs生态圈的TypeScript+React开发入门教程
基于Nodejs生态圈的TypeScript+React开发入门教程 概述 本教程旨在为基于Nodejs npm生态圈的前端程序开发提供入门讲解. Nodejs是什么 Nodejs是一个高性能Ja ...
随机推荐
- Opencascade 选择器算法
算法的阶段 该算法包括预处理和三个主要阶段. 使用深度优先搜索逐层遍历所有对象 . 预处理 计算平截头体及其主要特征的计算. 第一阶段 - 遍历第一级BVH树 在成功构建选择平截头体之后,算法开始遍历 ...
- 字符串KMP || POJ 2185 Milking Grid
求一个最小矩阵,经过复制能够覆盖原矩阵(覆盖,不是填充,复制之后可以有多的) *解法:横着竖着kmp,求最大公倍数的做法是不对的,见http://blog.sina.com.cn/s/blog_69c ...
- 搜索 || DFS || POJ 1321 棋盘问题
棋盘上#可以放,.不可以放,每行每列只能放一个 *解法:类似八皇后问题 dfs+回溯,考虑每一行和每一列 [[[[dfs的样子]]]]最前面写达到目标状态or不能走下去了 然后return #incl ...
- 第1节 flume:7、flume的监控文件夹,实现数据收集到hdfs上
1.2.2 采集案例 1.采集目录到HDFS 需求分析 结构示意图: 采集需求:某服务器的某特定目录下,会不断产生新的文件,每当有新文件出现,就需要把文件采集到HDFS中去 根据需求,首先定义以下3大 ...
- Echarts 异步数据加载遇到的问题
看了Echarts官网异步加载数据的Demo var myChart = echarts.init(document.getElementById('main')); // 显示标题,图例和空的坐标轴 ...
- Fortran学习记录1(Fortran数据类型)
Fortran中的字符 Fortran中的常量 Fortran中的变量 Fortran的I-N规则 Fortran中的有效位数 Fortran中的申明 Fortran中的表达式 Fortran中的语句 ...
- composer 设置代理
在命令行终端中输入以下内容: export https_proxy='192.168.1.133:1080' export http_proxy='192.168.1.133:1080' 此前提是你已 ...
- linux php安装ODBC扩展
进入php源码安装目录的ext/pdo_odbc $ sudo /data/apps/php/bin/phpize $ ./configure --with-php-config=/data/apps ...
- 深入Linux内核架构——简介与概述
一.内核的任务 纯技术层面上,内核是硬件与软件的之间的一个中间层.作用是将应用程序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址. 从应用程序视角上看,内核可以被认为是一台增强 ...
- c++_包子凑数
标题:包子凑数 小明几乎每天早晨都会在一家包子铺吃早餐.他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子.每种蒸笼都有非常多笼,可以认为是无限笼. 每当有顾客想买X个包子,卖包子的大叔就会 ...