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函數

相關文章
相關標籤/搜索