《PHP 7擴展開發之原生函數定義》要點:
本文介紹了PHP 7擴展開發之原生函數定義,希望對您有用。如果有疑問,可以聯系我們。
在上一篇中我們在 hellozapi 擴展中我們定義了幾個常量,但是一個有用的擴展,必需得有函數,沒有函數的擴展啥用沒有,如果您覺得定義函數很難的話,您又錯了,zendAPI 就是為了讓您生活變得美好而生的,而不會讓事情變得復雜.
說到函數,咱們就不得不說函數最重要的兩個組成部分,一個是函數的參數,另一個是函數的返回值.因為 C++ 是靜態語言,所以咱們的函數的類型必需在編譯時就要確定,不像 PHP 語言中那么靈活.
zendAPI 主要支持如下幾種函數原型:
有返回值, 無參數
有返回值, 有參數
有返回值, 可變參數
無返回值, 無參數
無返回值, 有參數
無返回值, 可變參數
說明:zendAPI 支持引用類型的參數傳遞
考慮到我們是新手學堂,在本篇中我們就不介紹可變參數和引用傳參了,這部分我們放在我們的高級教程部分講.
我們會在 hellozapi 中定義以下 PHP 原型的函數:(PHP 語言描述)
print_project_name($prefix);
print_develop_team();
get_version();
add_two_num($num1, $num2);
下面我們聲明這幾個 PHP 函數對應的 C++ 函數原型
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, 不要擔心,現在我們簡單介紹一下這兩個類.
zapi::ds::Variant
在 zendAPI 中,zapi::ds::Variant 類的一個對象就代表 PHP 的一個變量,您可以將 zapi::ds::Variant 想象成一個容器,它將常見的 C++ 類型包裝成一個 zapi::ds::Variant 對象,便利跟 zend engine 整合.
您可以用這個類去包裝如下類型:
常見的整形 (int, std::int8_t, std::int16_t, std::int32_t, long … )
浮點型 (float, double)
布爾型 (true, false)
字符串 (std::string, char *, char [])
空指針 (std::nullptr_t)
上面說的既然 zapi::ds::Variant 可以包裝一切必要的類型,是不是就夠了呢?答案是否定的,雖然 zapi::ds::Variant 可以容納 C++ 的這些數據類型,但是它不提供任何特定類型的計算,好比常見的四則運算,字符串連接,函數調用等等.
那么問題又來了,你可能會問,為什么不提供這樣的接口呢?接下來我就來解釋下為什么不在 zapi::ds::Variant 為什么不提供這些接口,原因有如下幾點:
zapi::ds::Variant 設計的目的就是充當一個容器,便利 zendAPI 向 zend engine 進行數據傳遞,它強調的數據傳遞而不是數據的計算.
zendAPI 的設計理念是,單一的類完成單一的任務,把字符串操作和整形操作甚至函數調用等等雜在一起違背了這個理念.
使用范例
using zapi::ds::Variant;
Varaint nullVar(nullptr);
Variant numVar(123);
Variant doubleVar(3.14);
zapi::ds::NumericVariant
根據上面討論的,看著名字不用我說,大家都能猜出這個類的作用吧,沒錯,您猜的是對的,這個是對 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 參考手冊 http://zendapi.org/api/classzapi_1_1ds_1_1_numeric_variant.html
zapi::ds::StringVariant
這個類跟 zapi::ds::NumericVariant 一樣,看名字我們就知道這個類是為字符串操作而設計的,它為我們提供了常見的字符串接口,拼接,子串查找,替換等等.下面我們就舉幾個常見的使用的范例:
使用范例
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
zapi::ds::StringVariant 參考手冊 http://zendapi.org/api/classzapi_1_1ds_1_1_string_variant.html
好了數據類型了解完畢,我們下面開始進入實現環節.
第一步
打開 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;
}
}
到這里,代碼稍稍有些復雜了,但是細心的同學會發現,其實代碼是很有規律的,只是重復調用罷了,在這段代碼中我們引入了幾個新的類型,下面我先將這樣類型做些講解,然后我們再對這個代碼段進行解釋.
zapi::lang::Type 類型
zendAPI 對 zend engine 的宏類型定義重新用 enum class 進行了重新定義,方便實施 C++ 的類型檢查,好比常用的類型有:
zapi::lang::Type::Undefined
zapi::lang::Type::Null
zapi::lang::Type::False
zapi::lang::Type::True
zapi::lang::Type::Long
zapi::lang::Type::String
zapi::lang::Type 參考手冊
http://zendapi.org/api/namespacezapi_1_1lang.html#1a15bd083a614363a9decc92d672454ffa
zapi::lang::ValueArgument 類型
zendAPI 支持的參數傳遞有兩種,按值傳參和按引用傳參.zapi::lang::ValueArgument 類型就是為了支持按值傳遞參數機制,它的構造函數很簡單,第一個參數是傳遞的參數的名字,第二個參數是這個參數的類型,第三個參數設置這個參數是否是必需的參數.
好比下面的代碼我們定義了一個名叫 arg1 的參數,類型是字符串并且是非必要參數
ValueArgument("arg1", zapi::lang::Type::String, false);
zapi::lang::ValueArgument 參考手冊 http://zendapi.org/api/classzapi_1_1lang_1_1_value_argument.html
zapi::lang::Extension::registerFunction 函數接口
為了支持不同類型的函數,zapi::lang::Extension::registerFunction 被設計成了一個模板函數,在這篇文章中我們暫時使用了用于注冊非成員函數指針的部分.
傳遞的模板參數有:
函數的類型 (一般我們不定義函數的類型,使用 decltype 進行獲取)
函數指針值 (會被 zendAPI 在運行時進行調用)
decltype 參考手冊 http://en.cppreference.com/w/cpp/language/decltype
傳遞的調用參數有:
函數的名字
函數接受的參數列表 std::initializer_list<zapi::lang::Argument>
這里的 zapi::lang::Argument 是 zapi::lang::ValueArgument 的基類,一般不直接使用.
zapi::lang::Extension::registerFunction 參考手冊
http://zendapi.org/api/classzapi_1_1lang_1_1_extension.html#1a82065cc95e784e659a3cc697f34bfece
std::initializer_list 參考手冊 http://en.cppreference.com/w/cpp/utility/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.
維易PHP培訓學院每天發布《PHP 7擴展開發之原生函數定義》等實戰技能,PHP、MYSQL、LINUX、APP、JS,CSS全面培養人才。