Linux下PHP7扩展开发入门教程3:编写第一个函数

前一节对扩展中的函数结构进行了详细解释,遗憾的是没有亲自编写代码实现扩展。这一节将编码实现一个函数int array_max(array $arr)

开始之前

本文是PHP扩展开发系列教程第3篇:

  1. Linux下PHP7扩展开发入门教程1: 扩展开发流程
  2. Linux下PHP7扩展开发入门教程2: 函数结构详解
  3. Linux下PHP7扩展开发入门教程3: 编写第一个函数

如果没有浏览过前面的文章,可以先浏览。

编写第一个函数

目标

用扩展编写一个函数,获取一个整数数组的最大值(类似max()函数)。如果用PHP来编写,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function array_max(array $arr)
{
$max_num = 0;

for ($i = 0; $i < count($arr); $i++) {
$current_num = $arr[$i];
if ($i === 0) {
$max_num = $current_num;
}else{
if ($current_num > $max_num) {
$max_num = $current_num;
}
}
}

return $max_num;
}

echo array_max([1,8,2,0]); // 输出:8

接下来,用扩展的方式实现这个函数

建立扩展开发骨架

进入源代码的扩展目录,调用脚本创建一个array_util扩展:

1
2
3
cd php-7.4.4/ext/
php ext_skel.php --ext array_util
cd array_util/

编写array_max函数

  1. 首先把array_util.c中自动生成的两个函数array_util_test1array_util_test2删除;

  2. 要编写的函数array_max有1个必选参数,类型是数组,返回值是一个整数。函数原型如下:

    1
    int array_max( array $max)
  3. 编写array_max函数,如下:

    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
    /* {{{ int array_max( array $max)
    */
    PHP_FUNCTION(array_max){
    zval *arr; // 传入的数组参数
    zval *entry; // 用于数组循环时保存每一个元素
    zend_long current_num, max_num = 0;
    zend_ulong num_idx; // 数组下标(类型)
    zend_string *str_idx; // 数组下班(字符串类型)

    // 参数只有一个,是必选的,所以ZEND_PARSE_PARAMETERS_START的参数是1, 1
    ZEND_PARSE_PARAMETERS_START(1, 1)
    Z_PARAM_ARRAY(arr) // 参数,类型是数组
    ZEND_PARSE_PARAMETERS_END();

    // 数组循环,参数分别是:数组、下标(数字类型)、下标(字符串类型)、数组元素
    // arr是zval*类型的,要用Z_ARRVAL_P()访问数组的值
    ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(arr), num_idx, str_idx, entry){
    // 同理,entry是zval*类型的,用Z_LVAL_P()来获取zend_long类型的数值
    current_num = Z_LVAL_P(entry);
    if(num_idx == 0) {
    max_num = current_num;
    }else{
    if(current_num > max_num){
    max_num = current_num;
    }
    }
    }ZEND_HASH_FOREACH_END(); // 循环结束

    RETURN_LONG(max_num); // 返回一个整数
    }
  4. 定义函数参数信息。找到arginfo片段,改为:

    1
    2
    3
    4
    5
    6
    /* {{{ arginfo
    */
    ZEND_BEGIN_ARG_INFO(arginfo_array_max, 0)
    ZEND_ARG_INFO(0, arr)
    ZEND_END_ARG_INFO()
    /* }}} */

    ZEND_BEGIN_ARG_INFO的第一个参数是arginfo_函数名,第二个参数还没用到。
    ZEND_ARG_INFO的第一个参数指明参数类型是否是引用,第二个是参数名称。

  5. 定义扩展的函数列表。找到array_util_functions片段,改为:

    1
    2
    3
    4
    5
    6
    7
    /* {{{ array_util_functions[]
    */
    static const zend_function_entry array_util_functions[] = {
    PHP_FE(array_max, arginfo_array_max)
    PHP_FE_END
    };
    /* }}} */

编译测试

  1. 在扩展目录ext/array_util下运行make命令编译。

    1
    make

    编译后在modules目录会生成一个array_util.so文件。

  2. 修改php.ini,添加array_util扩展:

    1
    extension=/path/to/array_util.so
  3. 编写测试脚本:

    1
    2
    3
    4
    5
    <?php
    var_dump(array_max([1,2,5,7,4]));
    var_dump(array_max([1,2,3,4]));
    var_dump(array_max([4]));
    var_dump(array_max([]));
  4. 执行脚本:

    1
    2
    3
    4
    5
    root@ed78d89e5328:~/php-7.4.4/ext/array_util# php test.php 
    int(7)
    int(4)
    int(4)
    int(0)