第一个参数是HashTable,在1.2.3节提到Zend使用HashTable来存储PHP函数,function_table用于指 定从哪个HashTable中获取函数。通常应该用CG(function_table),展开就是 compiler_globals.function_table,compiler_globals是一个用来存储编译器数据的全局数据结构(与其对应 的还有个EG宏,即executor_globals,它用来存储执行器数据)。compiler_globals.function_table里面存 储了所有我们可以在PHP页面里面调用的函数,包括Zend内建函数、PHP标准库函数、模块导出的函数以及用户使用PHP代码定义的函数。
object_pp是一个对象,当指定该值时,Zend会从对象的函数表中获取函数,这里不予讨论,总是设为NULL。
function_name
必须是string型的zval,存储我们希望调用的函数的名称。为什么使用zval而不是直接用char*,是因为Zend考虑到大部分情况下,我们都

是从用户那获得参数,然后再调用call_user_function_ex的,这样就可以不作处理直接把用户参数传给该函数。当然,我们也可以手动创建
一个string型zval传给它。
retval_ptr_ptr用于获取函数的返回值,Zend执行完指定的函数后,它就将返回值的指针填充到这里。
param_count和params用于指定函数的参数,params是个zval **这点可能让人感到奇怪,但考虑到该函数的常见用法(见下面的示例)以及2.2.2节关于函数参数的介绍,就一点也不奇怪了。
no_separation用于指定是否在必要时执行zval分离(参见1.1.3),这在写入非引用zval时发生。应该总是将其设为0,表示执行zval分离,否则可能破坏数据。
symbol_table用于指定目标函数的active_symbol_table(参见1.2.3),通常应该使用NULL,这样Zend会为目标函数生成一个空的符号表。
说了这么多,该动动手了,下面的程序片段简单实现了PHP API call_user_func的功能:

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ZEND_FUNCTION(call)
{
    int num_args = ZEND_NUM_ARGS();
    if(num_args < 1)
        WRONG_PARAM_COUNT;
    zval ***args = (zval***)emalloc(sizeof(zval**)*num_args);
zval *ret_zval;
// 获取传入的参数
if(zend_get_parameters_array_ex(num_args, args TSRMLS_CC)
== FAILURE)
    {
        efree(args);
        return;
}
// 第一个参数作为函数名,后面的作为函数参数
if(call_user_function_ex(CG(function_table), NULL, **args,
    &ret_zval, num_args - 1, args + 1, 0, NULL TSRMLS_CC)
== FAILURE)
    {
        efree(args);
        zend_error(E_ERROR, "Function call failed");
}
// 将函数返回值反馈给用户
    *return_value = *ret_zval;
    efree(args);
}

 

1.6访问PHP变量

1.6.1  设置

1.2.3节提到Zend使用HashTable来存储全局和局部变量符号,因此访问PHP变量,其实就是操作HashTable。当然,我们不需要手工去做,Zend提供了一组宏完成这些工作。
PHP变量的创建共有三步,首先需要创建一个zval结构,可使用如下的宏:

双击代码全选
1
MAKE_STD_ZVAL(zval*)

 

这个宏先调用emalloc分配一块zval,然后将其refcount设为1、is_ref设为0。
之后就是设置zval的值,同样,我们不需要直接操作zval的成员,Zend已经提供了如下的宏:

可能你会发现,这个表格和2.3节里面的返回值宏表格很相似,不错,返回值宏就是直接调用的ZVAL_xxx。
既然有了zval,下面把它添加到变量符号表里就可以了,可以使用如下的一组宏:

 
1
2
ZEND_SET_SYMBOL(symtable, name, var)
ZEND_SET_GLOBAL_VAR(name, var)

 

symtable
用来指定你想插入的符号表,一般使用EG(active_symbol_table),表示访问当前调用者的活动符号表。如果想强制访问全局符号表,可以
用&EG(symbol_table),这也正是ZEND_SET_GLOBAL_VAR(name,
var)所做的。这两个宏的最终效果和执行PHP赋值语句name = var完全一样。
如果只是访问全局变量,可以使用单个宏代替上述三步:

 
1
2
3
4
SET_VAR_STRING(name, value)
SET_VAR_STRINGL(name, value, length)
SET_VAR_LONG(name, value)
SET_VAR_DOUBLE(name, value)

 

上述宏分别用于创建全局的string、long和double变量,它们在内部执行了以上三步,当然,最后调用的是ZEND_SET_GLOBAL_VAR宏。

1.6.2 获取

如果想获取已有的PHP变量,则只能直接访问HashTable,Zend并没有提供相应的操作:

 
1
2
3
4
int zend_hash_find(
HashTable *ht,
char *arKey, uint nKeyLength,
void **pData)

 

这个函数从HashTable中查找元素,pData用于获取结果值,Bucket.pData将被放到这里(如果找到的话)。函数成功则返回SUCCESS,否则返回FAILURE。
下面是个示例:

 
1
2
3
4
5
6
zval **ppzval; // Bucket.pData里存放的是zval**
if(zend_hash_find(EG(active_symbol_table),"var", 4,
(void**)&ppzval) == SUCCESS)
printf("var.refcount = %dn", (*p)->refcount);
    else
        printf("Not Foundn");

 

这段代码从活动符号表中查找名为var的变量,需要注意的是nKeyLength是4,必须包括结尾的0。
获得变量后,拿来读是没有问题的,但是写操作就应该小心对待了。只有当refcount为1或者is_ref为1,才可以写入;否则应该进行zval分离,具体参见1.2.3节。

1.6.3  常量

PHP常量的内部定义如下:

双击代码全选
1
2
3
4
5
6
7
typedef struct _zend_constant {
    zval value;
    int flags;
    char *name;
    uint name_len;
    int module_number;
} zend_constant;

 

常 量的值依然使用zval存储,但这里的zval是私有的,不会和其他变量或常量共享,其refcount和is_ref被忽略。
module_number是模块号,在启动函数中可以获取该值(参见2.4),当模块被卸载时,Zend会使用模块号查找和删除所有该模块注册的常量。
如果希望在模块被卸载后,常量依然有效,可以将module_number设为0。另一个注意点是,name_len需要包含结尾的0。
flags值可以是如下两个,可以使用”|”联用:

HashTable里,其key是常量名称,value是zend_constant,注意不是zend_constant*,因此HashTable会复制一份zend_constant作为value。
获取一个常量非常简单,只要传递常量名和接受常量值的zval:

 
1
2
int zend_get_constant(char *name, uint name_len, zval *result
    TSRMLS_DC);

 

设置常量稍微复杂一点,需要先填写一个zend_constant结构,要注意的是,常量只能是long、double和string。然后使用如下函数将其加入常量表:
同时,Zend也为我们提供了如下的宏,可以直接创建常量:

 
1
int zend_register_constant(zend_constant *c TSRMLS_DC);

 


上 述宏的MAIN版本用于创建module_number为0的宏,在模块被卸载后,常量依然有效。而非MAIN版本则假设存在一个名为
module_number的int变量,并拿来给zend_constant.module_number赋值,可见这组宏原本就是为在模块启动函数里
调用而设计的。另外,当创建string型常量时,Zend也会dup一份字符串,因此可以直接使用C串指定常量值。
最后需要指出的是,上述函数和宏都无法改变已有的常量,如果发现已经存在同名常量,则函数失败。如果想修改的话,只能通过HashTable操作。

1.7输出信息

Zend提供了两个函数用于向浏览器输出信息:

 
1
2
int zend_printf(const char *format, ...);
void zend_error(int type, const char *format, ...);

 

zend_printf用法和C的printf一样;zend_error用于输出错误信息,type可以指定错误的性质,对于不同的错误,Zend将作不同处理:
该函数会同时输出出错的文件和行号,类似这样:

1Fatal error: no memory in /home/wiki/zdj/ext/test.php on line 6

PHP内核介绍及扩展开发指南—Extensions 的编写(下)的更多相关文章

  1. PHP内核介绍及扩展开发指南—Extensions 的编写

    Extensions 的编写 理解了这些运行机制以后,本章着手介绍Extensions 的编写,但凡写程序的人都知道hello world,那好,就从hello world开始. 1.1Hello W ...

  2. 基于Asterisk的VoIP开发指南——Asterisk 模块编写指南(1)

    原文:基于Asterisk的VoIP开发指南--Asterisk 模块编写指南(1) 1 开源项目概述 Asterisk是一个开源的软件包,通常运行在Linux操作系统平台上.Asterisk可以用三 ...

  3. HarmonyOS三方件开发指南(14)-Glide组件功能介绍

    <HarmonyOS三方件开发指南>系列文章合集 引言 在实际应用开发中,会用到大量图片处理,如:网络图片.本地图片.应用资源.二进制流.Uri对象等,虽然官方提供了PixelMap进行图 ...

  4. 谷歌拼音输入法扩展API开发指南

    为了帮助开发者在谷歌拼音输入法的基本输入功能基础上,开发和定义更丰富的扩展输入功能,谷歌拼音输入法提供了以Lua脚本编程语言为基础的输入法扩展API.利用输入法扩展API,开发者可以编写自定义的输入功 ...

  5. 开发指南专题十四:JEECG微云高速开发平台MiniDao 介绍

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zhangdaiscott/article/details/27068645   开发指南专题十四:J ...

  6. MongoDB介绍及开发指南

    目录 一.MongoDB介绍 二.搭建MongoDB 三.Java With MongoDB 四.Spring Session MongoDB 五.MongoDB开发规范及示例 六.MongoDB + ...

  7. HarmonyOS三方件开发指南(15)-LoadingView功能介绍

    目录: 1. LoadingView组件功能介绍2. Lottie使用方法3. Lottie开发实现4.<HarmonyOS三方件开发指南>系列文章合集 1. LoadingView组件功 ...

  8. 基于Asterisk的VoIP开发指南——(1)实现基本呼叫功能

    原文:基于Asterisk的VoIP开发指南--(1)实现基本呼叫功能 说明: 1.本文档探讨基于Asterisk如何实现VoIP的一些基本功能,包括基本呼叫功能的方案选取.主叫号码透传.如何编写As ...

  9. [翻译]现代java开发指南 第二部分

    现代java开发指南 第二部分 第二部分:部署.监控 & 管理,性能分析和基准测试 第一部分,第二部分 =================== 欢迎来到现代 Java 开发指南第二部分.在第一 ...

随机推荐

  1. pgrep 和 pkill 使用小记

    在停止指定进程时,经常使用如下命令: kill `ps aux | grep -w program_name | grep -v grep | awk '{print $2}'` 使用 pgrep 和 ...

  2. 21天学通C++_Day4

    0.迭代器 昨天晚上3G移动通信实验的时候,需要写一些简单的C程序,用到for循环的时候,发现在不同的for循环中,若定义标识符相同的变量名时,会有报错,环境是VC6: 可是一想到在for语句声明的迭 ...

  3. 接口测试基础——第2篇smtplib发送带附件的邮件

    我先给大家补充一个用QQ发送纯文本电子邮件的代码,用QQ的朋友可以参考一下: # coding=utf-8 import smtplib from email.mime.text import MIM ...

  4. 剑指offer-第六章面试中的各项能力之总结

  5. Oracle Sql Developer 连接oracle

    PL/Sql 初次使用需要配置文件内容,对于我这种Oracle新手来说各种配置有点凌乱,所以果断选择Sql Developer. 选择它是因为初次使用的时候它不用想PL/Sql那样配置文件,而只需要添 ...

  6. UVA12296 Pieces and Discs

    题意 PDF 分析 可以看成直线切割多边形,直接维护. 对每个多边形考虑每条边和每个点即可. 时间复杂度?不过\(n,m \leq 20\)这种数据怎么都过了.据说是\(O(n^3)\)的,而且常数也 ...

  7. yarn 管理nextjs 项目

     预备环境 nodejs npm 1. yarn 安装 npm install -g yarn 2. nextjs 项目初始化 yarn add next react react-dom 3. 配置n ...

  8. 解决ubantu中sublime不支持中文的方法

    更新然后将系统升级到最新版本,在linux终端输入 sudo apt-get update && sudo apt-get 在本地目录中克隆此repo:    如果你没有git的话就安 ...

  9. Python学习系列(四)(列表及其函数)

    Python学习系列(四)(列表及其函数) Python学习系列(一)(基础入门) Python学习系列(二)(基础知识) Python学习系列(三)(字符串) 一.基本概念 1,列表是什么?     ...

  10. hadoop之 Zookeeper 分布式应用程序协调服务

    (1) Zookeeper 在 Hadoop 集群中的作用 Zookeeper 是分布式管理协作框架,Zookeeper 集群用来保证 Hadoop 集群的高可用,(高可用的含义是:集群中就算有一部分 ...