在上一篇中咱們在hellozapi
擴展中咱們定義了幾個常量,可是一個有用的擴展,必須得有函數,沒有函數的擴展啥用沒有,若是您以爲定義函數很難的話,您又錯了,zendAPI
就是爲了讓您生活變得美好而生的,而不會讓事情變得複雜。
說到函數,我們就不得不說函數最重要的兩個組成部分,一個是函數的參數,另外一個是函數的返回值。由於C++
是靜態語言,因此我們的函數的類型必須在編譯時就要肯定,不像PHP
語言中那麼靈活。zendAPI
主要支持以下幾種函數原型:php
說明:
zendAPI
支持引用類型的參數傳遞html
考慮到咱們是新手學堂,在本篇中咱們就不介紹可變參數和引用傳參了,這部分咱們放在咱們的高級教程部分講。
咱們會在hellozapi
中定義如下PHP
原型的函數:(PHP 語言描述)ios
下面咱們聲明這幾個PHP
函數對應的C++
函數原型api
using zapi::ds::Variant; using zapi::ds::NumericVaraint; using zapi::ds::StringVariant; void print_project_name(const StringVariant &prefix); void print_develop_team(); Variant get_version(); Variant add_two_num(const NumericVariant &num1, const NumericVariant num2);
在上面的C++
函數的原型聲明中出現兩個陌生的類Variant
和NumericVariant
, 不要擔憂,如今咱們簡單介紹一下這兩個類。函數
在zendAPI
中,zapi::ds::Variant
類的一個對象就表明PHP
的一個變量,您能夠將zapi::ds::Variant
想象成一個容器,它將常見的C++
類型包裝成一個zapi::ds::Variant
對象,方便跟zend engine
整合。
您能夠用這個類去包裝以下類型:學習
上面說的既然zapi::ds::Variant
能夠包裝一切必要的類型,是否是就夠了呢?答案是否認的,雖然zapi::ds::Variant
能夠容納C++
的這些數據類型,可是它不提供任何特定類型的計算,好比常見的四則運算,字符串鏈接,函數調用等等。
那麼問題又來了,你可能會問,爲何不提供這樣的接口呢?接下來我就來解釋下爲何不在zapi::ds::Variant
爲何不提供這些接口,緣由有以下幾點:
1.zapi::ds::Variant
設計的目的就是充當一個容器,方便zendAPI
向zend engine
進行數據傳遞,它強調的數據傳遞而不是數據的計算。
2.zendAPI
的設計理念是,單一的類完成單一的任務,把字符串操做和整形操做甚至函數調用等等雜在一塊兒違背了這個理念。spa
using zapi::ds::Variant; Varaint nullVar(nullptr); Variant numVar(123); Variant doubleVar(3.14);
根據上面討論的,看着名字不用我說,你們都能猜出這個類的做用吧,沒錯,您猜的是對的,這個是對zapi::ds::Variant
再次封裝,爲數值類型的zapi::ds::Variant
提供數值計算的能力,好比四則運算, 大小比較運算。設計
using zapi::ds::NumericVariant; NumericVariant num1(123); NumericVariant num2(321); NumericVariant sum = num1 + num2; long rawSum = sum.toLong(); bool cmp = num1 < num2; // cmp is true std::int32_t raw32int1 = 123; std::int16_t raw32int2 = 23; NumericVariant num3(raw32int1); // value is 123 NumericVariant num4(raw32int2); // value is 23 sum = num3 + num4; // sum is 146
這個類跟zapi::ds::NumericVariant
同樣,看名字咱們就知道這個類是爲字符串操做而設計的,它爲咱們提供了常見的字符串接口,拼接,子串查找,替換等等。下面咱們就舉幾個常見的使用的範例:code
using zapi::ds::StringVariant; StringVariant str1("hello zapi"); // str1 is hello zapi str1 += ", hello"; // now hello zapi, hello char c = str1[0]; // c is h std::string upperStr1 = str1.toUpperCase(); str1.replace("zapi", "zendAPI"); // str1 is hello zendAPI, hello str1.prepend("=> "); // str1 now is => hello zendAPI, hello
好了數據類型瞭解完畢,咱們下面開始進入實現環節。
打開hellozapi
項目下的hellozapi/defs.h
文件,在文件中輸入咱們的C++
函數的原型聲明代碼。
#ifndef ZAPI_HELLOZAPI_DEFS_H #define ZAPI_HELLOZAPI_DEFS_H #include "zapi/ZendApi.h" using zapi::ds::Variant; using zapi::ds::NumericVariant; using zapi::ds::StringVariant; void print_project_name(const StringVariant &prefix); void print_develop_team(); Variant get_version(); Variant add_two_num(const NumericVariant &num1, const NumericVariant &num2); #endif // ZAPI_HELLOZAPI_DEFS_H
打開hellozapi
項目下的hellozapi/impls.cpp
文件,在文件中輸入咱們的C++
函數的實現代碼。
#include "defs.h" #include <iostream> void print_project_name(const StringVariant &prefix) { zapi::out << prefix << " " << "hellozapi" << std::endl; } void print_develop_team() { zapi::out << "qcoreteam" << std::endl; } Variant get_version() { return "v1.0.2"; } Variant add_two_num(const NumericVariant &num1, const NumericVariant &num2) { return num1 + num2; }
將咱們的實現的C++
函數與zend engine
進行整合。打開咱們的入口文件hellozapi/entry.cpp
,輸入咱們的函數註冊代碼。
#include "zapi/ZendApi.h" #include "defs.h" using zapi::lang::Constant; using zapi::lang::ValueArgument; extern "C" { ZAPI_DECL_EXPORT void *get_module() { static zapi::lang::Extension hellozapi("hellozapi", "1.0"); Constant hellozapiVersionConst("HELLO_ZAPI_VERSION", 0x010002); Constant hellozapiNameConst("HELLO_ZAPI_NAME", "Hello zendAPI!"); Constant helloDebugModeConst("HELLO_DEBUG_MODE", true); Constant helloPiConst("HELLO_ZAPI_PI", 3.14); hellozapi.registerConstant(std::move(hellozapiVersionConst)); hellozapi.registerConstant(std::move(hellozapiNameConst)); hellozapi.registerConstant(std::move(helloDebugModeConst)); hellozapi.registerConstant(std::move(helloPiConst)); hellozapi.registerFunction<decltype(print_project_name), print_project_name> ("print_project_name", { ValueArgument("prefix", zapi::lang::Type::String) }); hellozapi.registerFunction<decltype(print_develop_team), print_develop_team> ("print_develop_team"); hellozapi.registerFunction<decltype(get_version), get_version>("get_version"); hellozapi.registerFunction<decltype(add_two_num), add_two_num> ("add_two_num", { ValueArgument("num1", zapi::lang::Type::Numeric), ValueArgument("num2", zapi::lang::Type::Numeric) }); return hellozapi; } }
到這裏,代碼稍稍有些複雜了,可是細心的同窗會發現,其實代碼是頗有規律的,只是重複調用而已,在這段代碼中咱們引入了幾個新的類型,下面我先將這樣類型作些講解,而後咱們再對這個代碼段進行解釋。
zendAPI
對zend engine
的宏類型定義從新用enum class
進行了從新定義,方便實施C++
的類型檢查,好比經常使用的類型有:
zendAPI
支持的參數傳遞有兩種,按值傳參和按引用傳參。zapi::lang::ValueArgument
類型就是爲了支持按值傳遞參數機制,它的構造函數很簡單,第一個參數是傳遞的參數的名字,第二個參數是這個參數的類型,第三個參數設置這個參數是不是必須的參數。
好比下面的代碼咱們定義了一個名叫arg1
的參數,類型是字符串而且是非必要參數
ValueArgument("arg1", zapi::lang::Type::String, false);
爲了支持不一樣類型的函數,zapi::lang::Extension::registerFunction
被設計成了一個模板函數,在這篇文章中咱們暫時使用了用於註冊非成員函數指針的部分。
傳遞的模板參數有:
decltype
進行獲取)zendAPI
在運行時進行調用)傳遞的調用參數有:
std::initializer_list<zapi::lang::Argument>
這裏的 zapi::lang::Argument 是 zapi::lang::ValueArgument 的基類,通常不直接使用。
zapi::lang::Extension::registerFunction 參考手冊
std::initializer_list 參考手冊
有了上面的背景知識,如今咱們解釋函數註冊代碼就簡單多了,您也很容易就能理解。
hellozapi.registerFunction<decltype(print_project_name), print_project_name> ("print_project_name", { ValueArgument("prefix", zapi::lang::Type::String) });
這行代碼註冊一個原型爲print_project_name($prefix);
的PHP
函數,當這個函數被zend engine
執行的時候,咱們的C++
函數void print_project_name(const StringVariant &prefix);
將被運行時調用。
hellozapi.registerFunction<decltype(print_develop_team), print_develop_team> ("print_develop_team");
這行代碼註冊一個原型爲print_develop_team
的PHP
函數,當這個函數被zend engine
執行的時候,咱們的C++
函數void print_develop_team();
將被運行時調用。
hellozapi.registerFunction<decltype(get_version), get_version>("get_version");
這行代碼註冊一個原型爲get_version
的PHP
函數,當這個函數被zend engine
執行的時候,咱們的C++
函數Variant get_version();
將被運行時調用。
hellozapi.registerFunction<decltype(add_two_num), add_two_num> ("add_two_num", { ValueArgument("num1", zapi::lang::Type::Numeric), ValueArgument("num2", zapi::lang::Type::Numeric) });
這行代碼註冊一個原型爲add_two_num
的PHP
函數,當這個函數被zend engine
執行的時候,咱們的C++
函數Variant add_two_num(const NumericVariant &num1, const NumericVariant &num2);
將被運行時調用。
咱們走到這裏,函數註冊就完成了,雖然有些小長,可是您不也堅持看完了嗎?
下面讓咱們在PHP
代碼中愉快的進行調用吧。
<?php if (function_exists("print_project_name")) { print_project_name("nb, "); } if (function_exists("print_develop_team")) { print_develop_team(); } if (function_exists("get_version")) { $version = get_version(); echo $version; } echo "\n"; if (function_exists("add_two_num")) { $sum = add_two_num(1, 2); echo $sum; } // you will get output: // nb, hellozapi // qcoreteam // v1.0.2 // 3
怎麼樣,實現函數也不過如此吧,根本沒啥難度,哈哈哈,您到時候也能自豪的說,我也能沒事的試試寫寫擴展啦,給PHP
語言添加幾個原生函數了。下一篇,咱們來點更刺激的,教你們怎麼實現原生的Class
。