PHP扩展开发教程6 - 扩展函数的参数类型(2)

PHP扩展是高级PHP程序员必须了解的技能之一,对于一个初入门的PHP扩展开发者,怎么才能开发一个成熟的扩展,进入PHP开发的高级领域呢?本系列开发教程将手把手带您从入门进入高级阶段。
本教程系列在linux下面开发(推荐使用centos),php版本用的是5.6,并假设您有必定的linux操做经验和c/c++基础。
有问题须要沟通的朋友请加QQ技术交流群32550793和我沟通。

继续上一节的内容,讲解扩展函数的参数类型,下面教程内容的相关源码已经上传到github上面,见param子目录下的演示代码。php

git clone https://github.com/elvisszhang/phpcpp_demo.git
cd param

1、代码演示:对象类做为参数的用法

咱们这里使用php的DateTime类做为扩展函数的参数来演示如何传入对象。
下面是该扩展函数的C++源码。linux

//演示时间类型操做
void pm_datetype(Php::Parameters &params)
{
    Php::Value time = params[0];
    Php::out <<"param type is : " << time.type() << std::endl;
    Php::out <<"current time is : " << time.call("format","Y-m-d H:i:s") << std::endl;
}

注册扩展函数的代码c++

myExtension.add<pm_datetype>("pm_datetype", {
    /****
        "time" : 表示参数名称,用于返回的异常信息中使用
        "DateTime":参数对象的类名
        true :表示该参数是必须的
    ****/
     Php::ByVal("time", "DateTime", true)
});

PHP测试代码(test/3.php)git

<?php
echo PHP_EOL . '-----TEST pm_datetype($time)-----' . PHP_EOL;
$time = new DateTime();
pm_datetype($time);

echo PHP_EOL . '-----TEST pm_datetype(\'2018-04-17\')-----' . PHP_EOL;
pm_datetype('2018-04-17');
?>

执行测试代码,结果以下程序员

# php test/3.php

-----TEST pm_datetype($time)-----
2018-04-17 19:57:57

-----TEST pm_datetype('2018-04-17')-----
PHP Catchable fatal error:  Argument 1 passed to pm_datetype() must be an instance of DateTime, string given in /data/develop/phpcpp_param/test/3.php on line 7

根据测试结果可见:github

  1. 参数类型指定为特定类的对象后,传入其余类型参数将触发生成一个fatal error
  2. 可使用 Php::Value的 call方法来执行对象类的函数方法,很是方便。

2、代码演示:匿名函数或函数名称做为参数类型

你们知道c++的模板类可让同一个类能够处理各类不一样数据类型,很是强大。
下面实现一个冒泡排序算法,使用匿名函数做为参数,让这个冒泡排序算法也可以对各类不一样类型元素的数组都能进行排序,并且无论正向反向,数字仍是文本或者是复杂结构元素都能排序。算法

下面是该扩展函数的C++源码centos

//演示通用的冒泡排序类
Php::Value pm_sort(Php::Parameters &params){
    int i,j;
    Php::Value array = params[0];
    Php::Value cmpfunc = params[1];
    int len = array.size();
    Php::Value result,temp;
    for(i=0;i<len;i++){
        for(j=i+1;j<len;j++){
            // Php::Value 类重载了运算符 (), 使得用起来就跟内置函数同样好用
             result = cmpfunc(array.get(i), array.get(j));
             if(result.boolValue()){ //若是比较结果为true则往上冒泡
                temp = array.get(i);
                array.set(i,array.get(j));
                array.set(j,temp);
             }
        }
    }
    return array;
}

注册该扩展函数的代码以下数组

myExtension.add<pm_sort>("pm_sort", {
     Php::ByVal("a", Php::Type::Array), //第一个是数组类型
     Php::ByVal("b", Php::Type::Callable) //第二个是函数类型
});

PHP测试代码(test/4.php)函数

<?php
echo PHP_EOL . '-----数字降序排列-----' . PHP_EOL;
$result = pm_sort(array(22,3,15),function($a,$b){
    //$b > $a则往上冒泡,因此是降序排列
    return $b > $a;
});

echo var_export($result);

echo PHP_EOL . '-----数字升序排列-----' . PHP_EOL;
$result = pm_sort(array(22,3,15),function($a,$b){
    //$b < $a 则往上冒泡,因此是升序排列
    return $b < $a;
});
echo var_export($result);

echo PHP_EOL . '-----学生成绩降序排列-----' . PHP_EOL;
$score = array(
    array('name' => '张三', 'score'=>78),
    array('name' => '李四', 'score'=>98),
    array('name' => '王五', 'score'=>88),
);
$result = pm_sort($score,function($a,$b){
    //$b['score'] > $a['score'] 则往上冒泡,因此是按成绩进行降序排列
    return $b['score'] > $a['score'];
});
echo var_export($result);

echo PHP_EOL . '-----字符串按长度升序排列-----' . PHP_EOL;
function cmp_strlen($a,$b){
    //strlen($b) < strlen($a) 则往上冒泡,因此是按字符串长度进行升序排列
    return strlen($b) < strlen($a);
}
$result = pm_sort(array('country','I','love','my'),'cmp_strlen');
echo var_export($result);

echo PHP_EOL . '-----名字按首字母升序排列-----' . PHP_EOL;
class MyNameSort{
    public static function cmpLetter($a,$b){
        //首字母asscii码小的,则往上冒泡,因此是按首字母进行升序排列
        return ord($b[0]) < ord($a[0]);
    }
}
$result = pm_sort(array('Jack','Tom','Michael','Smith'),'MyNameSort::cmpLetter');
echo var_export($result);
?>

运行测试代码,输出结果以下

# php test/4.php

-----数字降序排列-----
array (
  0 => 22,
  1 => 15,
  2 => 3,
)
-----数字升序排列-----
array (
  0 => 3,
  1 => 15,
  2 => 22,
)
-----学生成绩降序排列-----
array (
  0 =>
  array (
    'name' => '李四',
    'score' => 98,
  ),
  1 =>
  array (
    'name' => '王五',
    'score' => 88,
  ),
  2 =>
  array (
    'name' => '张三',
    'score' => 78,
  ),
)
-----字符串按长度升序排列-----
array (
  0 => 'I',
  1 => 'my',
  2 => 'love',
  3 => 'country',
)
-----名字按首字母升序排列-----
array (
  0 => 'Jack',
  1 => 'Michael',
  2 => 'Smith',
  3 => 'Tom',
)

根据上述测试代码可见

  1. 函数类型的参数能够是匿名函数,
  2. 函数类型的参数也能够是字符串类型的函数名称
  3. 函数类型的参数还能够是类的静态函数的函数名
  4. 使用函数类型参数传入有助于实现高效简洁的代码

3、代码演示:引用类型的参数类型

按照官网文档的说法,PHP-CPP是支持引用类型的,并且官方文档还给了一个swap(参数值对换)的演示代码。咱们按官网的文档进行一下实验。很遗憾,你们会发现对于PHP5.x这个特性是不支持的,PHP7.x系列也许支持,有条件的能够试验一下看看。

下面是该扩展函数的C++源码

//测试引用类型参数
void pm_swap(Php::Parameters &params)
{
    Php::Value temp = params[0];
    params[0] = params[1];
    params[1] = temp;
}

注册该扩展函数的代码以下

myExtension.add<pm_swap>("pm_swap", {
        Php::ByRef("a", Php::Type::Numeric),
        Php::ByRef("b", Php::Type::Numeric)
});

PHP测试代码(test/5.php)

$a = 123;
$b = 456;
echo 'before swap: $a = ' . $a . ' $b = ' . $b . PHP_EOL;
pm_swap($a,$b);
echo 'after swap: $a = ' . $a . ' $b = ' . $b . PHP_EOL;

// 若是直接输入常量,会致使类型检测通不过,触发php error。
//pm_swap(10,20);

运行测试代码,输出结果以下

before swap: $a = 123 $b = 456
after swap: $a = 123 $b = 456

根据测试结果可见,$a,$b的值仍是保持原样,没有交换过来,因此引用类型的参数在PHP5.x事实上不支持。PHP7.x是否支持还须要进一步实验。

4、参考文献

PHP-CPP官网 - 关于函数参数
PHP-CPP官网 - 关于Lambda函数

相关文章
相关标签/搜索