上一篇我们在hellozapi扩展中定义了几个常量,但是有用的扩展一定要有函数,没有函数的扩展是没用的。如果你认为很难定义函数,那你又错了,zendAPI的诞生就是为了让你的生活更美好而不让事情复杂化。说到函数,不得不说函数最重要的两个组成部分就是函数的参数和函数的返回值。因为C++是静态语言,我们函数的类型必须在编译时确定,不像PHP语言那么灵活。zendAPI主要支持以下函数原型:返回值、无参返回值、带参返回值、无返回值带变参、无返回值无参、无返回值带参、变参说明:zendAPIParameterpassingthatsupportsreferencetypes考虑到我们是新手派,本文就不介绍可变参数和引用传递了。我们将把这部分放在我们的高级教程部分。我们将在hellozapi中定义如下PHP原型函数:(PHP语言描述)print_project_name($prefix);print_develop_team();get_version();add_two_num($num1,$num2);下面我们声明对应的PHP函数C++函数原型;Variantget_version();Variantadd_two_num(constNumericVariant&num1,constNumericVariantnum2);背景知识学习上述C++函数的原型声明中有两个陌生的类Variant和NumericVariant。别着急,现在就来简单介绍一下这两个类。zapi::ds::Variant在zendAPI中,zapi::ds::Variant类的一个对象表示PHP中的一个变量。你可以把zapi::ds::Variant想象成一个容器,将常见的C++类型包装成一个zapi::ds::Variant对象,方便与zend引擎集成。你可以使用这个类来包装以下类型:普通整数(int,std::int8_t,std::int16_t,std::int32_t,long...)浮点类型(float,double)布尔类型(true,false)String(std::string,char*,char[])空指针(std::nullptr_t)上面说了,既然zapi::ds::Variant可以把所有需要的类型都包起来,这就够了吗?答案是否定的,虽然zapi::ds::Variant可以容纳C++的这些数据类型,但是它没有提供任何特定类型的计算,比如常见的四运算、字符串拼接、函数调用等等。那么问题又来了,你可能会问,为什么不提供这样的接口呢?接下来我会解释为什么zapi::ds::Variant中没有提供这些接口。原因如下:1.zapi::ds::Variant设计的目的是作为一个容器,方便zendAPI到zendengine的数据传递,强调数据传递而不是数据计算。2.zendAPI的设计理念是单一的类完成单一的任务,混合进行字符串操作,整形操作,甚至函数调用等等,都违背了这个理念。usingzapi::ds::Variant;VaraintnullVar(nullptr);VariantnumVar(123);VariantdoubleVar(3.14);zapi::ds::NumericVariant的使用示例根据上面的讨论,我就不用说了这个名字,大家能猜到这个类的作用吗,是的,你猜对了,这个就是对zapi::ds::Variant进行重新封装,为zapi::ds::Variant的数值类型提供数值计算能力,比如四次算术运算,大小比较运算。示例使用zapi::ds::NumericVariant;NumericVariantnum1(123);NumericVariantnum2(321);NumericVariantsum=num1+num2;longrawSum=sum.toLong();boolcmp=num1");//str1nowis=>hellozendAPI,hellozapi::ds::StringVariant参考手册了解了数据类型之后,让我们开始实现吧。第一步是打开hellozapi项目下的hellozapi/defs.h文件,在文件中输入我们C++函数的原型声明代码。#ifndefZAPI_HELLOZAPI_DEFS_H#defineZAPI_HELLOZAPI_DEFS_H#include"zapi/ZendApi.h"usingzapi::ds::Variant;使用zapi::ds::NumericVariant;使用zapi::ds::StringVariant;voidprint_project_name(constStringVariant&prefix);voidprint_develop_team();Variantget_version();Variantadd_two_num(constNumericVariant&num1,constNumericVariant&num2);#endif//ZAPI_HELLOZAPI_DEFS_H第二步打开hellozapi工程下的hellozapi/impls.cpp文件,在文件中输入我们C++函数的实现代码。#include"defs.h"#includevoidprint_project_name(constStringVariant&prefix){zapi::out<("print_project_name",{ValueArgument("prefix",zapi::lang::Type::String)});hellozapi.registerFunction("print_develop_team");hellozapi.registerFunction("get_version");hellozapi.registerFunction("add_two_num",{ValueArgument("num1",zapi::lang::Type::Numeric),ValueArgument("num2",zapi::lang::Type::数字)});returnhellozapi;}}这里的代码有点复杂,但是细心的同学会发现,其实代码很有规律,就是重复调用而已。在这段代码中,我们引入了几种新类型。下面我先解释一下这些类型,然后再解释这段代码zapi::lang::Type类型zendAPI用enumclass重新定义了zendengine的宏类型定义,方便C++类型检查的实现。例如常用的类型有:zapi::lang::Type::Undefinedzapi::lang::Type::Nullzapi::lang::Type::Falsezapi::lang::Type::Truezapi::lang::Type::Longzapi::lang::Type::Stringzapi::lang::Type参考手册zapi::lang::ValueArgumenttypezendAPI支持两种传参方式,传值传参和传参传参。zapi::lang::ValueArgument类型是为了支持按值传递参数的机制。它的构造函数非常简单。第一个参数是传递的参数名称,第二个参数是这个参数的类型,第三个参数是设置这个参数是否是必选参数。例如,在下面的代码中,我们定义了一个名为arg1的参数,其类型为string,是一个非必需参数ValueArgument("arg1",zapi::lang::Type::String,false);zapi::lang::ValueArgument参考手册zapi::lang::Extension::registerFunction函数接口为了支持不同类型的函数,zapi::lang::Extension::registerFunction被设计为模板函数,在本文中我们暂时使用registerfunction的一部分作为非成员函数指针。传递的模板参数有:函数类型(一般我们不定义函数的类型,使用decltype获取)函数指针值(运行时会被zendAPI调用)decltype参考手册传递的调用参数有:函数名函数接受std::initializer_list的参数列表其中zapi::lang::Argument是zapi::lang::ValueArgument的基类,一般不用直接地。zapi::lang::Extension::registerFunction参考手册std::initializer_list参考手册有了上面的背景知识,现在我们讲解函数注册代码就简单多了,大家也能轻松看懂。hellozapi.registerFunction("print_project_name",{ValueArgument("prefix",zapi::lang::Type::String)});这行代码将原型注册为print_project_name($prefix);PHP函数,当这个函数被zend引擎执行时,我们的C++函数voidprint_project_name(constStringVariant&prefix);将在运行时调用。hellozapi.registerFunction("print_develop_team");这行代码注册了一个原型为print_develop_team的PHP函数。当这个函数被zend引擎执行时,我们的C++函数voidprint_develop_team();将在运行时调用。hellozapi.registerFunction("get_version");这行代码注册了一个原型为get_version的PHP函数。当这个函数被zend引擎执行时,我们的C++函数Variantget_version();将在运行时调用。hellozapi.registerFunction("add_two_num",{ValueArgument("num1",zapi::lang::Type::Numeric),ValueArgument("num2",zapi::lang::Type::数字)});这行代码注册了一个原型为add_two_num的PHP函数。当这个函数被zend引擎执行时,我们的C++函数Variantadd_two_num(constNumericVariant&num1,constNumericVariant&num2);将在运行时调用。到了这里,函数注册就完成了。虽然有点长,但是你不坚持看完吗?让我们愉快地在PHP代码中调用它吧。