我们首先找到快速上手文章里面关于函数定义的代码,以此说明然后开发PHP的函数
//php_myext.h
PHP_FUNCTION(myext_hello);//函数申明,所有在myext.c文件定义的函数,都会在这个文件里有一个申明 //myext.c
static zend_function_entry myext_functions[] = {
PHP_FE(myext_hello, NULL)//每个函数一行,第一个参数与PHP_FUNCTION(name)的name一样
{NULL, NULL, NULL}//固定语法
};
PHP_FUNCTION(myext_hello)
{
php_printf("hello you are success");
}

从上面的代码可以看出,开发一个php的函数需要3个步骤:

1,申明函数的原型

2,定义函数,书写函数的代码

3,把函数注册到本扩展模块里

我们先来看2点。

 PHP_FUNCTION(myext_hello)
{
php_printf("hello you are success");
} #define PHP_FUNCTION ZEND_FUNCTION
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_FN(name) zif_##name
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC //根据Zend/zend.h 和 Zend/zend_API.h中的宏定义,逐步展开 PHP_FUNCTION(myext_hello)
ZEND_FUNCTION(myext_hello)
ZEND_NAMED_FUNCTION(ZEND_FN(myext_hello))
ZEND_NAMED_FUNCTION(zif_myext_hello_name)
void zif_myext_hello_name(INTERNAL_FUNCTION_PARAMETERS)
void zif_myext_hello_name( int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC)

INTERNAL_FUNCTION_PARAMETERS 代替的参数说明

名称和类型 说明 使用方法
int ht 传递的参数数量 ZEND_NUM_ARGS()
zval *return_value 函数的返回值,默认是IS_NULL. RETVAL_*,RETURN_*
zval **return_value_ptr 当你需要返回引用的时候,把它赋值你要返回的变量,但是官方文档不建议返回引用.  
zval *this_ptr 如果当期函数是一个类方法,该指针指向对应的class,相当于PHP代码中的$this. getThis()
int return_value_used 函数的调用者是否使用了返回值.  

通过编写myext_example_args函数,了解了int ht , zval *return_value , int return_value_used三个参数的使用方法。

PHP_FUNCTION(myext_example_args);//php_myext.h

PHP_FE(myext_example_args, NULL)//static zend_function_entry myext_functions[] 在里面增加一个注册函数

//函数定义myext.c
PHP_FUNCTION(myext_example_args){

   php_printf("zend_num_args() => %d,ht=>%d\n",ZEND_NUM_ARGS(),ht);//zend_num_args() => 3,ht=>3

    if(return_value_used){
php_printf("返回值已经使用到了\n"); //$result = myext_example_args()
}else{
php_printf("返回值没有被使用\n"); //myext_example_args()
} RETURN_LONG();//返回值 }

我们来看函数注册的代码,

PHP_FE(myext_hello, NULL)//第二个参数是用来对函数的传参进行控制的,比如参数总数控制,是否传递引用,是否允许NULL,是否指定对象类,是否指定数组类型等,传递NULL表示不做控制
//相关的宏定义
#define PHP_FE ZEND_FE
#define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, //arg_info的结构体
typedef struct _zend_arg_info {
const char *name;//参数名称
zend_uint name_len;//名称长度
const char *class_name;//参数类名称
zend_uint class_name_len;//参数类名称长度
zend_uchar type_hint;//不知道
zend_uchar pass_by_reference;//是否传递引用
zend_bool allow_null;//是否允许NULL值
zend_bool is_variadic;//不知道
} zend_arg_info;//每一个参数的具体规则#define ZEND_ARG_INFO(pass_by_ref, name) { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 0 },
#define ZEND_ARG_PASS_INFO(pass_by_ref)                             { NULL, 0, NULL, 0, 0, pass_by_ref, 0, 0 },
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, IS_OBJECT, pass_by_ref, allow_null, 0 },
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, sizeof(#name)-1, NULL, 0, IS_ARRAY, pass_by_ref, allow_null, 0 },
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, sizeof(#name)-1, NULL, 0, type_hint, pass_by_ref, allow_null, 0 },
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 1 },

//开始和结束的固定用法
//四个参数说明(变量名称,没有用到,是否返回引用,要求的参数数量)
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)  \

static const zend_arg_info name[] = { \
{ NULL, , NULL, required_num_args, , return_reference, , },
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
ZEND_BEGIN_ARG_INFO_EX(name, , ZEND_RETURN_VALUE, -)
#define ZEND_END_ARG_INFO() };
//我们来看一个demo,str_replace函数的参数定义
ZEND_BEGIN_ARG_INFO_EX(arginfo_str_replace, 0, 0, 3)
ZEND_ARG_INFO(0, search)
ZEND_ARG_INFO(0, replace)
ZEND_ARG_INFO(0, subject)
ZEND_ARG_INFO(1, replace_count)
ZEND_END_ARG_INFO()

我们编写myext_example_arginfo函数,来演示一下arginfo变量的使用

PHP_FUNCTION(myext_example_arginfo);// php_myext.h

ZEND_BEGIN_ARG_INFO_EX(arginfo_arginfo, ,,)//定义arginfo_arginfo
ZEND_ARG_INFO(, str) //使用引用传参
ZEND_END_ARG_INFO() PHP_FE(myext_example_arginfo, arginfo_arginfo)//每个函数一行,第一个参数与PHP_FUNCTION(name)的name一样 PHP_FUNCTION(myext_example_arginfo){
zval *z;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z) == FAILURE) {
return;
}
convert_to_string(z);
ZVAL_STRING(z,"引用参数的值已经改变",);
return ;
} /*php 代码*/

$ref = "我是初始值";
myext_example_arginfo($ref);
var_dump($ref);//string(30) "引用参数的值已经改变"

在刚才的例子里,我们接受了一个php参数,我们来认真了解一个参数的接受

//有两个主要的函数用于在扩展函数中结果传递过来的参数,两者的区别就是ex结尾的多了一个flag参数
ZEND_API int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, const char *type_spec, ...)
//flags 是一个标识位,当参数接收出错的时候,是否抑制警告,目前只有一个常亮可以起作用,ZEND_PARSE_PARAMS_QUIET
//num_args 参数数量
//变量类型
ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, const char *type_spec, ...)
//num_args 参数数量
//变量类型

type_spec参数有点类似于printf的格式化字符参数

参数   类型   变量类型
a 数组 zval*
A 数组或对象 zval*
b 布尔值 zend_bool
C zend_class_entry*
d 浮点型 double
f 函数 zend_fcall_info*zend_fcall_info_cache*
h 数组 HashTable*
H 数组或对象 HashTable*
l 长整型 long
L 超出范围的长整型 long
o 对象 zval*
O 对象或指定类的对象 zval*zend_class_entry*
p 字符串(一个有效的路径) char*int
r 资源类型 zval*
s 字符串 char*int
z zval*变量 zval*
Z zval**变量 zval**

在这些参数列表里,大部分的参数都是对应着一个变量,也就是你前面写了一个参数,后面对应的位置用一个变量去接受,有几个特殊的(f,O,p,s),每一个参数对应两个变量。

除了参数类型,还有几个特殊用途的字符可以实现参数接收的特殊功能。

* 一共>=0个参数

+ 一共>=1个参数

| 在|之前的参数为必填参数,在|之后的参数为可选参数

/ 用来修饰前面一个参数,如果不是引用,则重新拷贝一个新的变量传递进来

! 用来修饰前面一个参数,如果传递过来的参数值为NULL,则直接转化成C语言的NULL,而不是zval的IS_NULL变量,区别在于后者增加了计算和赋值,浪费了更多的资源

看到这里,你很可能一头雾水,完全不明白这些到底怎么使用,没有关系,接着来我们来看几个例子,都是php标准扩展里面用到的,结合你在php中使用的经验,你应该一下子就会有所领悟。

//mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &search, &replace, &subject, &zcount) == FAILURE)
//在str_replace中,内核用了前3个ZZZ来接受必要的三个参数,接收到的变量是zval**类型,然后有一个|,表示后面的参数是可选的,如果传递了,还是用一个Z(zval**)变量接受它 //int array_push ( array &$array , mixed $var [, mixed $... ] )
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE)
//在array_push中,内核用了a(zval*)来接受第一个参数,然后用了一个+表示后面至少得有一个参数传递,或者更多。 //array range ( mixed $start , mixed $limit [, number $step = 1 ] )
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/|z/", &zlow, &zhigh, &zstep) == FAILURE)
//在range中,一共出现了3个z/,每个z/表示一个参数,说明是用z(zval*)来接受变量,同时对非引用的变量做强制拷贝后再传参

//mixed sscanf ( string $str , string $format [, mixed &$... ] )
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss*", &str, &str_len, &format, &format_len,&args, &num_args) == FAILURE)
//在sscanf中,每个s表示一个字符串,得用两个变量来接受,一个char*类型,指向字符串,另一个是int型,等于字符串的长度。&str, &str_len接受第一个s,&format, &format_len接受第二个s,最后一个*表示后面还可以有0到多个参数

看了以上这些,你应该能了解zend_parse_parameters函数的使用了,我们继续通过几个函数例子,让你完整的理解参数的接受和使用的完整过程,由于*和+涉及到多参数的使用,会有一点点复杂,在这里我们先跳过,日后再来进一步理解。

//一个类似strlen的函数,接受一个字符串,返回字符串的长度

PHP_FUNCTION(myext_example_strlen){
char *str;
int len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str,&len) == FAILURE) {
return;
}
RETURN_LONG(len);
} /*
$str = "1234567";
var_dump(myext_example_strlen($str));
int(7)
*/
//输入两个参数,返回最大值

PHP_FUNCTION(myext_example_max_num){
long i1;
long i2;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &i1 ,&i2) == FAILURE) {
return;
}
if(i1 > i2){
RETURN_LONG(i1);
}else{
RETURN_LONG(i2);
}
} /*
var_dump(myext_example_max_num(5,4));
var_dump(myext_example_max_num(4,5));
var_dump(myext_example_max_num("3","2"));//当输入参数不是long型时,zend_parse_parameters会做自动转换
var_dump(myext_example_max_num(1,false));
int(5)
int(5)
int(3)
int(1)
*/

php扩展开发-函数的更多相关文章

  1. postgres扩展开发

    扩展开发的基本组成 demo--1.0.sql demo.c demo.control Makefile demo.c当中包含了自定义函数的实现,纯C语言,目录下可包含多个.c文件.demo-1.0. ...

  2. Chrome扩展开发之二——Chrome扩展中脚本的运行机制和通信方式

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  3. PHP扩展开发相关总结

    1.线程安全宏定义 在TSRM/TSRM.h文件中有如下定义 #define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, ...

  4. Chrome浏览器扩展开发系列之十四

    Chrome浏览器扩展开发系列之十四:本地消息机制Native messaging 时间:2015-10-08 16:17:59      阅读:1361      评论:0      收藏:0    ...

  5. Chrome扩展开发(Gmail附件管理助手)系列之〇——概述

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  6. Chrome扩展开发之一——Chrome扩展的文件结构

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  7. Chrome扩展开发之三——Chrome扩展中的数据本地存储和下载

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  8. Chrome扩展开发之四——核心功能的实现思路

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  9. php扩展开发初探

    2015年2月26日 15:44:41 原因: 想用PHP实现一个布隆过滤器算法, 其中要用到位运算, 但是PHP的内置的int类型不给力, 不能支持大整数的位运算 数据一旦太大, 就会变为浮点数表示 ...

随机推荐

  1. Just a Hook(线段树区间更新)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1698 In the game of DotA, Pudge’s meat hook is actual ...

  2. Bellman_Ford算法(求一个点到任意一点的最短距离)

    单源最短路问题是固定一个起点,求它到任意一点最短路的问题. 记从起点出发到顶点 i 的最短距离为d[i],则有以下等式成立 d[i]=min{d[j]+(从j到 i 的边的权值) 看代码 #inclu ...

  3. Linux利用iptables实现真-全局代理

    对于经常要浏览油管等被墙网站的人而言,利用代理来实现fq是非常有必要的.现在fq的方法中,最为主流的应该要数ssr了,因此本教程都是基于ssr的socks5代理而言的. 在windows中,ssr客户 ...

  4. Kotlin容器

    1. 容器 可变/不可变 List<out T> 只读list; MutableList<T>; Set<out T>/MutableSet<T> Ma ...

  5. P1868 饥饿的奶牛

    题目描述 有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字. 现用汉语翻译为: 有N个区间,每个区间x,y表示提供的x~y共y-x+1堆优质牧草.你可以选择任意区间但不能有 ...

  6. jQuery判断动画是否执行完成

    JS $(function() { $("#myDiv").bind("click", function() { if ($(this).css("t ...

  7. KBEngine warring项目源码阅读(一) 项目简介和注册

    首先介绍下warring项目,是kbe自带的一个演示示例,大部分人了解kbe引擎也是从warring项目开始的. 项目地址:https://github.com/kbengine/kbengine_u ...

  8. 一、基础知识 React API 一览

    1.10 Hooks 参考文章:https://juejin.im/post/5be3ea136fb9a049f9121014 demo: /** * 必须要react和react-dom 16.7以 ...

  9. css钻石旋转实现

    css钻石旋转实现: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> &l ...

  10. Unity光晕剑效果的Shader简单实现

    最近遇到了一个需求,想要一种在刀剑上带有光晕的酷炫效果,甚至是还想要有闪烁呼吸效果,于是就写了一个简单的叫LightSwrod的Shader去实现,先上图看看效果吧. 简单展示 这是剑本身的样子 这是 ...