说到函数,咱们就不得不说函数最主要的两个组成部分,一个是函数的参数,另一个是函数的返回值。由于 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。