深入PHP内核之参数
1、看一下一个扩展中的简单代码
ZEND_BEGIN_ARG_INFO(params_add_arginfo, 0)
ZEND_ARG_INFO(0, a)
ZEND_ARG_INFO(0, b)
ZEND_END_ARG_INFO() PHP_FUNCTION(params_add) {
long a,b;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &a, &b) == FALSE) {
return;
}
RETURN_LONG(a+b);
}
2、参数相关宏的定义 (Zend/zend_API.h)
#define ZEND_ARG_INFO(pass_by_ref, name)\
{ #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 0 },
//声明普通参数,可以用来表示PHP中的int, float, double, string等基本数组类型 #define ZEND_ARG_PASS_INFO(pass_by_ref)\
{ NULL, 0, NULL, 0, 0, pass_by_ref, 0, 0 },
//pass_by_ref为1时,强制设置后续的参数为引用类型 #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, 0, NULL, required_num_args, 0, return_reference, 0, 0 },
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
#define ZEND_END_ARG_INFO() };
3、展开ZEND_BEGIN_ARG_INFO语句
static const zend_arg_info params_add_arginfo[] = {
{NULL, 0, NULL, -1, 0, ZEND_RETURN_VALUE, 0},
{a, sizeof(a)-1, NULL, 0, 0, 0, 0},
{b, sizeof(b)-1, NULL, 0, 0, 0, 0},
};
4、参数在zend中的定义(Zend/zend_compile.h)
typedef struct _zend_arg_info {
const char *name; //参数的名称
zend_uint name_len; //参数名称的长度
char *class_name; //当参数类型为类时,指定类的名称
zend_uint class_name_len; //类名称的长度
zend_uchar type_hint;
zend_bool allow_null; //是否允许设置为null
zend_bool pass_by_reference; //是否设置为引用,即使用&操作符
} #define PHP_FUNCTION ZEND_FUNCTION //函数的实现
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(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
5、zend_parse_parameters的参数
Boolean b zend_bool //布尔值
Long l long //长整数
Double d double //双精度浮点数
String s char *, int //符串 (也可能是空字节)和其长度
Resource r zval* // 资源, 保存在 zval*
Array a zval* //数组, 保存在 zval*
Object o zval* //任何类的)对象, 保存在 zval*
zval z zval* //实际的 zval*
zval O zval* //由class entry 指定的类的)对象, 保存在 zval*
HashTable h HashTable* //数组的哈希表
Function f char *, int //函数,方法名 (版本 > php5.1) | -表明剩下的参数都是可选参数。如果用户没有传进来这些参数值,那么这些值就会被初始化成默认值。
/ -表明参数解析函数将会对剩下的参数以 SEPARATE_ZVAL_IF_NOT_REF() 的方式来提供这个参数的一份拷贝,除非这些参数是一个引用。
! -表明剩下的参数允许被设定为 NULL(仅用在 a、o、O、r和z身上)。如果用户传进来了一个 NULL 值,则存储该参数的变量将会设置为 NULL。
看看官方文档中提供的几个例子:
/* 取得一个长整数,一个字符串和它的长度,再取得一个 zval 值。 */
long l;
char *s;
int s_len;
zval *param; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsz", &l, &s, &s_len, ¶m) == FAILURE) {
return;
} /* 取得一个由 my_ce 所指定的类的一个对象,另外再取得一个可选的双精度的浮点数。 */
zval *obj;
double d = 0.5;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|d", &obj, my_ce, &d) == FAILURE) {
return;
} /* 取得一个对象或空值,再取得一个数组。如果传递进来一个空对象,则 obj 将被设置为 NULL。*/
zval *obj;
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!a", &obj, &arr) == FAILURE) {
return;
} /* 取得一个分离过的数组。*/
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &arr) == FAILURE) {
return;
} /* 仅取得前 3 个参数(这对可变参数的函数很有用)。*/
zval *z;
zend_bool b;
zval *r;
if (zend_parse_parameters(3, "zbr!", &z, &b, &r) == FAILURE) {
return;
}
在接收参数时还有一个可用的函数zend_parse_parameters_ex,允许我们传入一些flags来控制解析参数的动作,使用方式如下所示:
int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);
目前flags仅能传入ZEND_PARSE_PARAMS_QUIET这个值,表示函数不输出任何错误信息,如下面的示例:
long l1, l2, l3;
char *s; if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS() TSRMLS_CC,
"lll", &l1, &l2, &l3) == SUCCESS) {
/* manipulate longs */
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS(), "s", &s, &s_len) == SUCCESS) {
/* manipulate string */
} else {
php_error(E_WARNING, "%s() takes either three long values or a string as argument",
get_active_function_name(TSRMLS_C));
return;
}
可变参数
由于PHP支持可变参数,所以在接收可变参数时,使用前面介绍的两个方法就不太合适,我们可以用zend_get_parameters_array_ex()来代替,如下面的示例:
zval **parameter_array[4]; /* 取得参数个数 */
argument_count = ZEND_NUM_ARGS(); /* 看一下参数个数是否满足我们的要求:最少 2 个,最多 4个。 */
if(argument_count < 2 || argument_count > 4)
{
WRONG_PARAM_COUNT;
} /* 参数个数正确,开始接收。 */
if(zend_get_parameters_array_ex(argument_count, parameter_array) != SUCCESS)
{
WRONG_PARAM_COUNT;
}
6、获取参数数量
PHP无法根据函数的显式声明来对调用进行语法检查,而且它还支持可变参数,所以我们就不得不在所调用函数的内部来获取参数个数。我们可以使用宏ZEND_NUM_ARGS来获取参数个数,如下面的代码:
if(ZEND_NUM_ARGS() != 2)
{
WRONG_PARAM_COUNT
}
这段代码使用宏WRONG_PARAM_COUNT抛出一个参数个数错误
7、更多的参数类型
#define Z_TYPE_P //获取参数类型
//IS_NULL|IS_BOOL|IS_LONG|IS_DOUBLE|IS_STRING|IS_ARRAY|IS_RESOURCE|IS_OBJECT
#define Z_BVAL_P //获取bool类型参数的值
#define Z_LVAL_P //获取long类型参数的值
#define Z_DVAL_P //获取float类型参数的值
#define Z_STRVAL_P //获取string类型参数的值
#define Z_STRLEN_P //获取string类型参数的长度
#define Z_ARRVAL_P //获取array类型参数的值
#define Z_RESVAL_P //获取resource类型参数的值
#define Z_OBJCE_P //获取object类型参数的值
深入PHP内核之参数的更多相关文章
- linux内核启动参数
Linux内核启动参数 Console Options 参数 说明 选项 内核配置/文件 console=Options 用于说明输出设备 tt ...
- Linux 实例常用内核网络参数介绍与常见问题处理
本文总结了常见的 Linux 内核参数及相关问题.修改内核参数前,您需要: 从实际需要出发,最好有相关数据的支撑,不建议随意调整内核参数. 了解参数的具体作用,且注意同类型或版本环境的内核参数可能有所 ...
- 阿里云(四)Linux 实例常用内核网络参数介绍与常见问题处理
Linux 实例常用内核网络参数介绍与常见问题处理 https://help.aliyun.com/knowledge_detail/41334.html
- uboot 如何向内核传递参数
a.uboot 向内核传递的参数有两种类型 1.一个是bootargs 2.一个是环境参数, 而环境参数的设置靠的是 Y:\junda\JdLinuxApp\A1801_uboot\source\u- ...
- u-boot 内核 启动参数
kernel如何得到uboot启动信息: http://blog.sina.com.cn/s/blog_89d9bec60101bzen.html u-boot向linux内核传递启动参数: http ...
- linux更新grub内核启动参数的方法
#!/bin/bash set -x set -e export PS4=+{$LINENO:${FUNCNAME[0]}} trap 'echo "---NEWKERNARGS=$NEWK ...
- Linux 内核引导参数简介
概述 内核引导参数大体上可以分为两类:一类与设备无关.另一类与设备有关.与设备有关的引导参数多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导参数.比如,如果你想知道可以向 AHA ...
- DDOS防御----CENTOS 内核TCP参数优化
0x01 环境 attact作为攻击发起方,安装有hping server作为被攻击方,修改内核TCP参数.使用操作系统CENTOS7 0x02 步骤 一.发起攻击 修改TCP最大SYN连接数 使用a ...
- 优化Linux下的内核TCP参数来提高服务器负载能力
http://blog.renhao.org/2010/07/setup-linux-kernel-tcp-settings/ /proc/sys/net目录 所有的TCP/IP参数都位于/proc/ ...
- 一份针对nginx的内核优化参数
首先,需要修改/etc/sysctl.conf来更改内核参数.例如,最常用的配置: # ·file-max:这个参数表示进程(比如一个worker进程)可以同时打开的最大句柄数,这个参数直接限制最大并 ...
随机推荐
- ubuntu C++开发环境
最近在VM中装了Ubuntu,为了开发程序,于是在网上找了些由于C/C++开发环境搭建的资料,供大家参考. 以下文字主要讲如何搭建Code::Blocks+wxWidgets. 搭建步骤: 1.安装编 ...
- Facebook数据库工具Flashcache初探
Flashcache是Facebook技术团队的又一力作,最初是为加速MySQL设计的.Flashcache是在Linux层面的,所以任何受磁盘IO困绕的软件或应用都可以方便的使用之. 1. Why ...
- [MAC OS] XCode中的Debug View Hierarchy功能
reference to : http://blog.csdn.net/liujinlongxa/article/details/46490949 前言 做iOS开发经常会遇见这种情况,产品汪拿着你做 ...
- 采用Operator-sdk轻松将helm chart转为Operator
去年就接触Operator,从Oracle发布的WebLogic Operator到mySQL Operator,构建的源码一大堆,但感觉一直缺少合适的开发框架能够避免复杂性快速生成, 随着技术的日益 ...
- 使用spring中的@Transactional注解时,可能需要注意的地方
前情提要 在编写业务层方法时,会遇到很多需要事务提交的操作,spring框架为我们提供很方便的做法,就是在需要事务提交的方法上添加@Transactional注解,比起我们自己开启事务.提交以及控制回 ...
- 机器学习简史brief history of machine learning
BRIEF HISTORY OF MACHINE LEARNING My subjective ML timeline (click for larger) Since the initial sta ...
- uestc 360(区间合并)
题意:有一个长度为n的序列.然后有两种操作,Q a b是输出区间a b内最长上升子序列的长度.A a b c是把区间a b内全部数字加上c. 题解:用线段树维护区间的最长上升子序列长度,那么一个区间的 ...
- Flatten Binary Tree to Linked List leetcode java
题目: Given a binary tree, flatten it to a linked list in-place. For example, Given 1 / \ 2 5 / \ \ 3 ...
- WebSettings 文档 API 翻译 常用设置
. setDefaultFontSize(int size) Sets the default font size. The default is 16. setDefaultTextEncodin ...
- 怎么才能成为一名PHP专家?
本文作者Bruno Skvorc是一名资深的Web开发者.在这篇文章里主要是讲述成为一名专业的PHP专家所要经历的过程,以及在这个过程里要如何学习掌握技巧和对工具的舍取.(以下为编译内容) 当阅读各种 ...