一步步入门编写PHP扩展

news2024/11/23 19:01:37

1、写在最前

      随着互联网飞速发展,lamp架构的流行,php支持的扩展也越来越多,这样直接促进了php的发展。

      但是php也有脚本语言不可避免的问题,性能比例如C等编译型语言相差甚多,所以在考虑性能问题的时候最好还是通过php扩展来解决。

      那么,怎么去做一个php扩展呢。下面从一个例子开始(本文章需要C基础)。

2、解决一个问题

      在一个系统中,如果经常要求一个数组的平方和,我们可以这么写。

1

2

3

4

5

6

7

8

<?php

    function array_square_sum($data){

        $sum = 0;

        foreach($data as $value){

            $sum += $value $value;

        }

        return $sum;

    }

      实际执行的时候,php zend引擎会把这段话翻译成C语言,每次都需要进行内存分配。所以性能比较差。进而,基于性能上的考虑,我们可以编写一个扩展来做这个事情。

3、编写扩展

      构建一个扩展,至少需要2个文件。一个是Configulator文件,它会告诉编译器编译这个扩展至少需要哪些依赖库;第二个是实际执行的文件。

3.1 生成框架

      听起来很复杂,实际上有一个工具可以帮我们搞定一个扩展的框架。在php源代码里面有个工具ext_skel,他可以帮我们生成扩展框架。

liujun@ubuntu:~/test/php-5.5.8/ext$ ls ext_skel
ext_skel

      现在我们利用它生成扩展 array_square_sum。($表示提示符命令)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

$ ./ext_skel --extname=array_square_sum

Creating directory array_square_sum

Creating basic files: config.m4 config.w32 .svnignore array_square_sum.c php_array_square_sum.h CREDITS EXPERIMENTAL tests/001.phpt array_square_sum.php [done].

To use your new extension, you will have to execute the following steps:

1.  $ cd ..

2.  $ vi ext/array_square_sum/config.m4

3.  $ ./buildconf

4.  $ ./configure --[with|enable]-array_square_sum

5.  $ make

6.  $ ./php -f ext/array_square_sum/array_square_sum.php

7.  $ vi ext/array_square_sum/array_square_sum.c

8.  $ make

Repeat steps 3-6 until you are satisfied with ext/array_square_sum/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.

      执行命令之后,终端告诉我们怎么去生产新的扩展。查看一下文件内容,会发现多了几个比较重要的文件config.m4, php_array_square_sum.h,array_square_sum.c,下面会一一叙述。

liujun@ubuntu:~/test/php-5.5.8/ext$ ll array_square_sum/
total 44
drwxr-xr-x  3 liujun liujun 4096 Jan 29 15:40 ./
drwxr-xr-x 80 liujun liujun 4096 Jan 29 15:40 ../
-rw-r--r--  1 liujun liujun 5548 Jan 29 15:40 array_square_sum.c
-rw-r--r--  1 liujun liujun  532 Jan 29 15:40 array_square_sum.php
-rw-r--r--  1 liujun liujun 2354 Jan 29 15:40 config.m4
-rw-r--r--  1 liujun liujun  366 Jan 29 15:40 config.w32
-rw-r--r--  1 liujun liujun   16 Jan 29 15:40 CREDITS
-rw-r--r--  1 liujun liujun    0 Jan 29 15:40 EXPERIMENTAL
-rw-r--r--  1 liujun liujun 3112 Jan 29 15:40 php_array_square_sum.h
-rw-r--r--  1 liujun liujun   16 Jan 29 15:40 .svnignore
drwxr-xr-x  2 liujun liujun 4096 Jan 29 15:40 tests/

3.2 配置文件config.m4

dnl PHP_ARG_WITH(array_square_sum, for array_square_sum support,
dnl Make sure that the comment is aligned:
dnl [  --with-array_square_sum             Include array_square_sum support])

      去掉dnl

PHP_ARG_WITH(array_square_sum, for array_square_sum support,
Make sure that the comment is aligned:
[  --with-array_square_sum             Include array_square_sum support])

      这是./configure时能够调用enable-sample选项的最低要求.PHP_ARG_ENABLE的第二个参数将在./configure处理过程中到达这个扩展的配置文件时显示. 第三个参数将在终端用户执行./configure --help时显示为帮助信息

3.3 头文件

      修改php_array_square_sum.h,把confirm_array_square_sum_compiled改成confirm_array_square_sum,这个为我们以后实际调用的函数名字,当然你也可以直接加入函数confirm_array_square_sum,而不删除confirm_array_square_sum_compiled。

1

PHP_FUNCTION(confirm_array_square_sum_compiled);

      该成

1

PHP_FUNCTION(array_square_sum);

3.3 源代码

      修改 array_square_sum.c,把confirm_array_square_sum_compiled改成confirm_array_square_sum,这个是注册这个扩展的函数,如果在3.2中直接加入了confirm_array_square_sum,在这一步也直接加入confirm_array_square_sum就可以了。

1

2

3

4

const zend_function_entry array_square_sum_functions[] = { 

    PHP_FE(confirm_array_square_sum_compiled,   NULL)       /* For testing, remove later. */

    PHP_FE_END  /* Must be the last line in array_square_sum_functions[] */

};

      改成

1

2

3

4

const zend_function_entry array_square_sum_functions[] = { 

    PHP_FE(array_square_sum,    NULL)       /* For testing, remove later. */

    PHP_FE_END  /* Must be the last line in array_square_sum_functions[] */

};

      然后最为关键的一个步骤,重写confirm_array_square_sum,这个时候只需要把confirm_array_square_sum_compiled重写成confirm_array_square_sum(3.1中没有删除confirm_array_square_sum_compiled,就需要加入confirm_array_square_sum就好了)。

1

PHP_FUNCTION(confirm_array_square_sum_compiled)

      重写为

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

 PHP_FUNCTION(array_square_sum)

{

    zval* array_data;

    HashTable *ht_data;

    int ret;

    char* key;

    uint index;

    zval **pdata;

    double sum = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array_data) == FAILURE) {

        return;

    }   

    ht_data = Z_ARRVAL_P(array_data);

    zend_hash_internal_pointer_reset(ht_data);

    while ( HASH_KEY_NON_EXISTANT != (ret = zend_hash_get_current_key(ht_data, &key, &index, 0)) ) { 

        ret = zend_hash_get_current_data(ht_data, &pdata);

     

        if( Z_TYPE_P(*pdata) == IS_LONG){

            sum +=  Z_LVAL_P(*pdata) *  Z_LVAL_P(*pdata);

        }else {

            RETURN_FALSE;

        }   

        zend_hash_move_forward(ht_data);

    }   

    zend_hash_internal_pointer_end(Z_ARRVAL_P(array_data));

    RETVAL_DOUBLE(sum);

}

      php是一个弱类型语言,他的数据都存在结构体zval里面(具体请看更加专业资料,如"php扩展开发.pdf")。

1

2

3

4

5

6

7

8

9

10

typedef union _zval {

    long lval;

    double dval;

    struct {

        char *val;

        int len;

    } str;

    HashTable *ht;

    zend_object_value obj;

} zval;

      为了获得函数传递的参数,可以使用zend_parse_parameters()API函数。下面是该函数的原型:

1

zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, …);

     zend_parse_parameters()函数的前几个参数我们直接用内核里宏来生成便可以了,形式为:ZEND_NUM_ARGS() TSRMLS_CC,注意两者之间有个空格,但是没有逗号。从名字可以看出,ZEND_NUM_ARGS()代表这参数的个数。后面紧跟着是常见的参数类型(和C语言的printf类似),后面就是常见的参数列表。
     下表列出了常见的参数类型。

参数类型对象C类型说明
llong整数
bbool布尔
schar*字符串
ddouble浮点数
aarray(zval*)数组
zzval*不确定性zval


      此外数组是一个大型的hashtable来实现的,所以zend_hash_get_current_key可以遍历数组,使用宏Z_LVAL_P(zval*)获得实际的值。最终可以将结果放入到sum里面。RETVAL_DOUBLE(value)也是一个宏,返回结果为double,值则为value,具体可以参见" php扩展开发.pdf".

      最终完成了这个主函数的开发。

3.4 生成configure文件

      然后执行 ~/php/bin/phpize  

1

/home/liujun/php/bin/phpize

Configuring for:
PHP Api Version:         20121113
Zend Module Api No:      20121212
Zend Extension Api No:   220121212

      可以发现array_square_sum出现可执行脚本configure。

3.5 编译

      编译的时候最好带上php-config PATH,因为系统默认的php-config-path可能不是你目前使用的php路径。

liujun@ubuntu:~/test/php-5.5.8/ext/array_square_sum$ ./configure --with-php-config=/home/liujun/php/bin/php-config

      编译如果成功,终端会有如下提示:

creating libtool
appending configuration tag "CXX" to libtool
configure: creating ./config.status
config.status: creating config.h
config.status: config.h is unchanged

      查看array_square_sum目录的module目录,会发现里面生成array_square_sum.so,这个就是我们需要的扩展。

liujun@ubuntu:~/test/php-5.5.8/ext/array_square_sum$ ls modules/
array_square_sum.la  array_square_sum.so

4、使用扩展

4.1、配置扩展

      修改php的配置php.ini,加入一下配置内容。

[array_square_sum]
extension=array_square_sum.so

4.2、加入module

      php的扩展一般在 $PHP_PATH/lib/php/extensions/no-debug-non-zts-yyyymmdd,如果找不到,请自行百度or Google. 里面有很多.so文件。 

      把3.5生产的array_sum_square.so拷贝进去即可。

      如果使用fastcgi模式,需要重启php,这样我们php就应该有扩展array_square_sum,具体可以通过查看phpinfo(不会请自行百度orGoogle).

4.2、编写代码

      既然说编写扩展可以提高运行效率,因此在这里,我们通过使用扩展和直接使用php代码来进行对比,测试性能。多次实验可以减少误差,所以进行2000次对100000个数字求平方和。代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<?php

    $data array();

    $max_index = 100000;

    $test_time = 2000;

    for($i=0; $i<$max_index$i++){

        $data[] = $i

    }   

    $php_test_time_start = time();

    php_test($test_time$data);

    $php_test_time_stop = time();

    echo "php test ext time is ". ($php_test_time_stop $php_test_time_start). "\n";

    $c_test_time_start = time();

    c_test($test_time$data);

    $c_test_time_stop = time();

    echo "php test time is ". ($c_test_time_stop $c_test_time_start). "\n";

     

    function php_test($test_time$test_data){

        for($i=0; $i<$test_time$i++){

            $sum = 0;

            foreach($test_data as $data){

                $sum += $data $data;

            }   

        }   

    }   

     

    function c_test($test_time$test_data){

        for($i=0; $i<$test_time$i++){

            $sum = array_square_sum($test_data);

        }   

    }

      测试结果如下:

liujun@ubuntu:~/php$ ~/php/bin/php test.php 
php test ext time is 30
php test time is 2

       可以看到扩展要比直接使用php快15倍。随着业务逻辑变得更加复杂,这个差异化会越大。

     那么直接使用c语言来做这个事情呢。下面也给一个代码来测试下(测试条件完全一致):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

#include<stdio.h>

#include<sys/time.h>

#include<unistd.h>

#define TEST_TIME 2000

#define MAX_INDEX 100000

int main()

{

    int data[MAX_INDEX];

    double sum = 0;

    for(int i=0; i<MAX_INDEX; i++){

        data[i] = i;

    }   

    struct timeval start;

    struct timeval end;

     

    gettimeofday(&start,NULL);

     

    for(int i=0; i<TEST_TIME; i++){

        for(int j=0; j<MAX_INDEX; j++){

            sum += data[j] * data[j];

        }   

    }   

    gettimeofday(&end,NULL);

     

    double time=(end.tv_sec-start.tv_sec) + (end.tv_usec-start.tv_usec) * 1.0 /1000000;

    printf("total time is %lf\n"time );

    printf("sum time is %lf\n", sum);

    return 0;

}

      执行查看效果,可以看出直接使用C的时间只有0.261746,是使用C扩展的13.09%,是直接使用php的0.87%。当然如果涉及到IO等复杂操作,C/C++会比php快上万倍(测试过)。 

liujun@ubuntu:~/php$ g++ test.cpp  -O2 -o test
liujun@ubuntu:~/php$ ./test 
total time is 0.261746
sum time is 36207007178615872.000000

      因此,在实际对性能要求非常高的服务,如索引、分词等,可以使用C做一套底层服务,php去进行封装调用。

搜集整理学习路线&笔记icon-default.png?t=N4P3https://mp.weixin.qq.com/s/KQx_eIwdjCj3QdErxKb7ZQ

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/588584.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

00后实在太强了,98年的我被卷废了,太离谱了...

前言 最近在公司我真的感受到了什么叫“卷”&#xff0c;以往的我划划水日子过的轻轻松松&#xff0c;直到公司最近招了一个00后进来&#xff0c;真的让我感受到了危机&#xff0c;刚进来工资就和我差不多&#xff0c;我刚开始其实有点不太舒服&#xff0c;凭什么我辛辛苦苦干…

在pycharm中调用qt界面功能

目录 一、新建designer文件 1、打开pycharm中的designer 2、创建个widget 3、拖动几个简单按钮 4、保存一下 5、右击test1.ui 这边首先环境已经配置完毕&#xff0c;可以参考之前写的博客&#xff1a; 关于PyQt5的环境搭建_Littlehero_121的博客-CSDN博客 一、新建desi…

如何使用PHM技术提高汽车工业的效率和性能?

在汽车工业中&#xff0c;预测性健康管理&#xff08;PHM&#xff09;技术正日益受到关注。作为一种基于数据驱动的解决方案&#xff0c;PHM技术通过实时监测和分析设备和系统的状态&#xff0c;实现对设备健康状况的预测和管理。 图.汽车制造&#xff08;iStock&#xff09; 汽…

代码示范【FabEdge v0.8.0】配置 connector 公开端口

FabEdge项目简介&#xff1a; FabEdge是博云在2021年8月发起&#xff0c;基于Kubernetes 构建的专注于边缘计算场景的容器网络方案&#xff0c;支持 KubeEdge 、SuperEdge、OpenYurt 等主流边缘计算框架。旨在解决边缘计算场景下容器网络配置管理复杂、网络割裂互不通信、缺少…

hadoop单机版部署

1.下载hadoop wget --no-check-certificate https://mirrors.bfsu.edu.cn/apache/hadoop/common/hadoop-3.3.1/hadoop-3.3.1.tar.gz 2.解压重命名 tar -zxvf hadoop-3.3.1.tar.gz mv hadoop-3.3.1.tar.gz hadoop 3.编辑hosts vim /etc/hosts 172.17.1.1 hadoop925 4.进入配置…

4.Apache网页优化

文章目录 Apache网页优化网页压缩网页缓存隐藏版本信息Apache防盗链 Apache网页优化 Apache网页优化 网页压缩网页缓存 Apache安全优化 隐藏版本信息配置防盗链 网页压缩 配置Apache的网页压缩功能&#xff0c;是使用gzip压缩算法来对网页内容进行压缩后再传输到客户端浏览器…

LNMT架构之反向代理负载均衡

目录 一、实验前提环境配置 &#xff08;一&#xff09;关闭防火墙&#xff0c;安装本地yum &#xff08;二&#xff09;部署tomcat &#xff08;三&#xff09;部署Mariadb &#xff08;四&#xff09;部署nginx 二、反向代理负载均衡 方法一&#xff1a;&#xff08;轮…

【算法学习系列】07 - 无序数组中的局部最小值问题

文章目录 说明约束条件简单说下思路解决方案随机无序数组样本生成器算法实现验证代码进行大样本随机测试验证算法正确性 说明 在算法中&#xff0c;局部最小值是指一个函数在一个局部范围内的最小值。 具体而言&#xff0c;如果一个函数在一个小区间内的取值都比该区间内的其他…

C++:STL--priority_queue

文章目录 一.STL设计思想:容器适配器STL--stack的代码设计STL--queue的代码设计stack和queue的默认容器适配器deque的数据结构解析deque的存储结构示意图 二.C仿函数仿函数示例 三.STL--priority_queue(优先级队列)1.C优先级队列的数据结构2.priority_queue的实现框架比较函数(…

chatgpt赋能python:Python中创建画布的函数——matplotlib

Python中创建画布的函数——matplotlib Python作为一种强大的编程语言&#xff0c;拥有许多重要且广泛应用的模块和库。其中&#xff0c;matplotlib是一种用于制作高质量的图形和图表的库&#xff0c;而创建画布的函数便是其基础功能之一。 什么是matplotlib&#xff1f; Ma…

C语言---初始C语言

1、初始C语言 1、编译器主要有&#xff1a;Clang、GCC、WIN-TC、MSVC、Turbo C等 什么是编译&#xff1f; test.c----------------------------->test.exe 这个过程需要经过编译、链接等过程&#xff0c;而众多编译器实现的功能就是把我们写的test.c进行编译。 2、VS20…

如何把“困在”内网的数据释放,进行安全的流转传输呢?

互联网大时代&#xff0c;数据的生产使用与互联网紧密相关&#xff0c;但数据安全和网络安全却既有联系又互不相同。数据安全和网络安全的突出区别是核心主体不同&#xff0c;数据安全关注的数据全生命周期的安全&#xff0c;而网络安全则是侧重保障网络体系和网络环境的安全性…

硬卷完了!低代码打怪升级进阶成神之路(2023年最新版)

一、背景 应用开发周期长一直是IT部门和业务部门面临的问题。 IT部门总是被新的应用需求弄得不堪重负。他们不可能完成业务部门想要完成的每一个项目。同时&#xff0c;业务部门的用户厌倦了等待&#xff0c;并开始完全绕过IT部门。 今天&#xff0c;我们来探索一下“低代码开发…

制药企业高效过滤器检漏参考法规、方法及操作步骤

对制药企业来讲&#xff0c;高效过滤器检漏主要是现场检漏&#xff0c;通过DOP法来发现滤器本身及运输、安装过程中可能存在的问题。常使用气溶胶光度计及多分散气溶胶进行检漏。依据的标准是2010药品GMP指南(测试方法采用ISO14644-3)。 对于制药企业来说&#xff0c;高效过滤器…

自动驾驶TPM技术杂谈 ———— 边缘检测

文章目录 介绍边缘检测与微分运算离散信号的差分滤波Robert算子Prewitt算子Sobel算子拉普拉斯算子 介绍 计算机视觉&#xff08;Computer Vision&#xff0c;CV&#xff09;是一门使用计算机模拟生物视觉的学科&#xff0c;目的是使用计算机代替人眼实现对目标的识别、分类、跟…

3.2. 数学类(Math、BigInteger、BigDecimal)

1. Math类 Math类提供了一些基本的数学函数&#xff0c;如求平方根、绝对值、三角函数等。它是一个final类&#xff0c;并且所有的方法都是static的&#xff0c;因此无需创建对象&#xff0c;直接使用类名调用方法即可。 以下是Math类的一些常用方法&#xff1a; abs(double…

抖音seo源码-抖音搜索源码-抖音下拉词-抖音关键词排名系统搭建

为了优化抖音平台上的内容&#xff0c;开发抖音关键词排名系统成为了必要的措施。该系统可以针对搜索结果和下拉词进行分析&#xff0c;为用户提供更准确的搜索结果。为实现这一目标&#xff0c;开发团队进行了大量的市场调查和用户研究。 在开发过程中&#xff0c;团队利用了…

mysql8+忘记密码的详细解决方法

mysql8忘记密码的详细解决方法 不同的版本&#xff0c;可能处理的方式不一样&#xff0c;这里说一下8以上的版本处理密码忘记的问题&#xff0c;windows系统。 一.问题&#xff1a; 太久没用mysql &#xff0c;忘记了原先的root密码 二&#xff1a;解决 1.关闭mysql服务,我的…

代码随想录算法训练营第四十八天 | 力扣 198.打家劫舍, 213.打家劫舍II, 337.打家劫舍III

198.打家劫舍 题目 198. 打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警…

word文档生成PDF文档时候自动生成书签方法

0 Preface/Foreword 在日常工作中&#xff0c;经常需要写技术文档&#xff0c;为了排版美观&#xff0c;一般会选择word&#xff0c;这样就可以生成目录。 word文件可以很方便生产PDF文档&#xff0c;方便分享给同事。 在阅读PDF文档时&#xff0c;看到有些PDF文档在左侧有一…