php扩展开发入门

我们要开发的扩展的php原型是:

<?php
// 调用一个可变参函数,输出函数名和参数,返回调用后的结果
function calltask(callable $func[, mixed $arg1[, ...]]);
// 原型可变参写法
function calltask(... $args) {
    $func = array_shift($args);
    if (!is_callable($func)) {
        printf("the function is not callable:%s\n", $func);
        return false;
    }
    // 输出
    printf("you call func:%s\n", $func);
    foreach ($args as $i=>$arg) {
        printf("arg[%d] type=%d len=%d val=%s \n", $i, typeof($arg), strlen($arg), strval($arg));
    }

    return $func(... $args);
}

我的开发环境是:

系统: CentOS 7
PHP: 7.0+
gcc :4.8.4
PHP已经提供了工具用来创建扩展,并初始化代码:ext_skel

$ cd php-src/ext
$ ./ext_skel --extname=hello

工具会在当前目录生成 hello 文件夹。

修改配置文件
cd到hello,工具已经初始化了目录,打开配置文件 config.m4:

dnl If your extension references something external, use with:

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

dnl Otherwise use enable:

dnl PHP_ARG_ENABLE(hello, whether to enable hello support,
dnl Make sure that the comment is aligned:
dnl [  --enable-hello           Enable hello support])

dnl 是注释符,表示当前行是注释。这段话是说如果此扩展依赖其他扩展,去掉PHP_ARG_WITH段的注释符;否则去掉PHP_ARG_ENABLE段的注释符。显然我们不依赖其他扩展或lib库,所以去掉PHP_ARG_ENABLE段的注释符:

PHP_ARG_ENABLE(hello, whether to enable hello support,
Make sure that the comment is aligned:
[  --enable-hello           Enable hello support])

书写代码
工具生成的hello.c,写上我们的实现:

/* proto function calltask(callable $func[, mixed $arg1[, ...]]);*/
PHP_FUNCTION(calltask)
{
    int i, status, argc = ZEND_NUM_ARGS();
    zval *args = NULL;
    zval retval;

    // use FAST_ZPP get any count of args, args use original args pointer, no need to free here
#ifndef FAST_ZPP
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
        RETURN_FALSE;
    }   
#else
    ZEND_PARSE_PARAMETERS_START(1, -1) 
        Z_PARAM_VARIADIC('+', args, argc)
    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
#endif

    if (argc < 1) {
        WRONG_PARAM_COUNT;
    }   

    // 第一个参数为函数名 取出来
    zval *thefunc = &(args[0]);
    php_printf("you will call: %s argc=%d is_callable=%d\n", Z_STRVAL(*thefunc), argc, zend_is_callable(thefunc, IS_CALLABLE_CHECK_NO_ACCESS, NULL));
    if (!zend_is_callable(thefunc, IS_CALLABLE_CHECK_NO_ACCESS, NULL)) {
        php_printf("the function is not callable:%s\n", Z_STRVAL(*thefunc));
        RETURN_FALSE;
    }   

    // 从第二个参数开始,把参数打印出来
    for (i=1; i<argc; i++) {
        convert_to_string_ex(&args[i]);
        php_printf("arg[%d] type=%d len=%d val=%s \n", i, Z_TYPE(args[i]), Z_STRLEN(args[i]), Z_STRVAL(args[i]));
    }   

    // 使用zend_API.h中的call_user_function_ex调用
    status = call_user_function_ex(EG(function_table), NULL, thefunc, &retval, argc-1, (argc>1?args+1:NULL), 0, NULL);
    // 判断返回值
    if (status == SUCCESS && !Z_ISUNDEF(retval)) {
        php_printf("the function success:%d\n", status);
        //zval_ptr_dtor(return_value);
        //ZVAL_COPY_VALUE(return_value, &retval);
    } else {
        php_printf("the function failed:%d\n", status);
        //zval_ptr_dtor(return_value);
        //ZVAL_NULL(return_value);
    }   
    php_printf("the call over: %s argc=%d status=%d\n", Z_STRVAL(*thefunc), argc);
    //efree(args);// Z_PARAM_VARIADIC 的args指针来自于复制函数原参数指针,不需要free
    RETURN_ZVAL(&retval, 1, 1);//RETURN_TRUE;// 返回该返回值的类型
}

添加到编译列表里:

const zend_function_entry hello_functions[] = {
    PHP_FE(hello, NULL)  /*添加这行*/
    PHP_FE(confirm_hello_compiled,  NULL)       /* For testing, remove later. */
    PHP_FE_END  /* Must be the last line in hello_functions[] */
};

编译与安装

$ phpize
$ ./configure --with-php-config=/usr/local/php7/bin/php-config
$ make && make install

修改php.ini,开启扩展,若找不到可以用phpinfo()查看使用哪个配置文件.
make install会输出你的php扩展的路径:

Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-non-zts-20151012/

其实不用关心这个路径。相信来学习扩展开发的同学都知道哪里配置php.ini,如何重启对应的apache或者fpm,这里不多说。

extension=hello.so

写个脚本测试,此脚本在ext_skel会自动生成, hello.php:

<?php
$br = (php_sapi_name() == "cli")? "":"<br>";

if(!extension_loaded('hello')) {
    dl('./module/hello.' . PHP_SHLIB_SUFFIX);
}
$module = 'hello';
/* 测试打印扩展中的函数列表
$functions = get_extension_funcs($module);
echo "Functions available in the test extension:$br\n";
foreach($functions as $func) {
    echo $func."$br\n";
}
echo "$br\n";
$function = 'confirm_' . $module . '_compiled';
if (extension_loaded($module)) {
    $str = $function($module);
} else {
    $str = "Module $module is not compiled into PHP";
}
echo "called in script: $str\n";
*/
//$ret = calltask($function, 100, 2.2);
$func2 = function(... $args) {
    $args[0] = "make a str:".$args[0];
    return sprintf(... $args);
};
$func1 = 'printf';
//$ret = calltask($func1, 'FORMAT_FROM_PARAM:%s:%d\n', date('Y-m-d H:i:s'), __LINE__);
$ret = calltask($func2, 'FORMAT_FROM_PARAM:%s:%d\n', date('Y-m-d H:i:s'), __LINE__);
echo "the ret in php=";
var_dump($ret);

输出:

you will call: ??argc=4 is_callable=1
arg[1] type=6 len=25 val=FORMAT_FROM_PARAM:%s:%d\n 
arg[2] type=6 len=19 val=2017-08-01 19:09:14 
arg[3] type=6 len=2 val=28 
the function success:0
the call over: ??argc=4 status=8
the ret in php=string(53) "make a str:FORMAT_FROM_PARAM:2017-08-01 19:09:14:28\n"

运行成功。
祝小伙伴们开发顺利。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值