截止到现在,我们已经熟悉了PHP语言中的字符串、数字、布尔以及数组的数据类型了,接下来,我们将接触另外一种PHP独特的数据类型——资源(Resource)。
我们用了很长的篇幅在这一章描述内核中的HashTable结构以及PHP中的数组实现。在接下来的时间中,我们会在它的基础上学习一下内核是怎样实现与管理PHP语言中的资源与类的。
当你在扩展中使用HashTable时候,95%是要存储用户端的变量,就像PHP语言中数组那样。为此,内核中已经准备好了相应的工具,来让我们更加的方便的操作HashTable存储zval*,也就是PHP语言中的数组,即IS_ARRAY常量代表的zval,以下用{数组}来代替PHP语言中的数组这个词。
Zend把与HashTable有关的API分成了好几类以便于我们寻找,这些API的返回值大多都是常量SUCCESS或者FAILURE。创建HashTable下面在介绍函数原型的时候都使用了ht名称,但是我们在编写扩展的时候,一定不要使用这个名称,因为一些PHP宏展开后会声明这个名称的变量,进而引发命名冲突。
我们在评选各种数据结构时,往往会考虑我们需要处理的数据规模以及需要的性能。下面让我们简要的看一下看C语言中数组和链表的一些事情。数组作者这里用的不是Array,而是Vector,可能指的是C++里的Vector,它与数组几乎是完全一样的,唯一的不同便是可以实现动态存储。本节下文都是用数组一词代替之,请各位注意。
在C语言中,我们可以自定义各种各样的数据结构,用来把很多数据保存在一个变量里面,但是每种数据结构都有自己的优缺点,PHP内核规模如此庞大,是否已经找到了一些非常棒的解决方法呢?
现在我们已经可以编写一个更真实的函数了,既可以接收用户传递过来的参数,也可以返回数据给调用者。为了写出高质量的代码,还需要我们多花点心思在zval的写时复制等特殊机制上,否则便会在接收参数和返回数据时留下一些bug。下面的章节里,让我们去看一下PHP语言里强大的数组类型是如何在内核中实现的,去探究内核中的HashTable结构,从而能编写出更强大的PHP扩展。
在前面的章节中我们已经介绍过arg info了,下面我们看一下如何通过其实现类型绑定,但这个特性只能在Zend Engine 2也就是PHP5中使用。让我们再回顾一下ZE2's argument info结构。每一个arg info结构的声明都是通过ZEND_BEGIN_ARG_INFO()或者ZEND_BEGIN_ARG_INFO_EX()宏函数开始的,然后紧跟着几行ZENDARG*INFO()宏函数,最终以ZEND_END_ARG_INFO()宏函数结束。
最简单的获取函数调用者传递过来的参数便是使用zend_parse_parameters()函数。zend_parse_parameters()函数的前几个参数我们直接用内核里宏来生成便可以了,形式为:ZEND_NUM_ARGS() TSRMLS_CC,注意两者之间有个空格,但是没有逗号。从名字可以看出,ZEND_NUM_ARGS()代表着参数的个数。
前面的章节我们look了一下如何在扩展中定义函数,它们的实现大都比较简单,但是在实际工作中,肯定会碰到函数接收参数的问题,而它就是我们这一章要讲解的内容。
在这一章里,我们集中讨论了如何把函数执行的结果返回给调用者,通过return语句、引用返回、通过参数返回等等,而且还初步了解了一下zend_arg_info。在下面的章节中,我们将去看一下内核是如何接收调用者传递的参数的。
一个函数的执行结果要返回给调用者,除了使用return功能,还有一种办法,那就是以引用的形式传递参数,然后在内部修改这个参数的值。前一种方法往往只能返回一个值,如果我们的函数执行结果具有多种数据,便需要把这些数据打包到一个数组、类等复合类型的变量中才能得以实现;但后一种方法相比而言就简单一些了。
你也许会认为扩展中定义的函数应该直接通过return关键字来返回一个值,比如由你自己来生成一个zval并返回,就像下面这样:ZEND_FUNCTION(sample_long_wrong){ zval *retval; MAKE_STD_ZVAL(retval); ZVAL_LONG(retval, 42); return retval;}但是,上面的写法是无效的!
PHP语言中函数的返回值是通过return来完成的,就像下面的程序:<?phpfunction sample_long() { return 42;}$bar = sample_long();C语言也一样使用return关键字int sample_long(void) { return 42;}int main(void) { int bar = sample_long(); return 1;}那我们在扩展中编写的PHP函数如何把返回值回馈给用户端的函数调用者呢?
在这一章里,我们学会了如何创建一个PHP框架并为其添加函数,并编译到PHP中供用户在PHP语言中调用。在接下来的章节里,我们将陆续看到许多高级的PHP内核特性,从而使我们编写出更好的PHP扩展。编译PHP源码的环境会随着平台与时间的不同而变化,如果本章讲述的知识无法使你顺利的编译PHP,那你可以给我发信,或者去php.
前面我们已经生成好了一份扩展框架,但它是没有什么实际作用的。一个扩展的作用可大了去了,既可以操作PHP中的变量、常量,还可以定义函数、类、方法、资源等。先让我们从函数说起吧!ZEND_FUNCTION()宏函数ZEND_FUNCTION()宏函数也可以写成PHP_FUNCTION(),但ZEND_FUNCTION()更前卫、标准一些,但两者是完全相同的。
我们检查一下PHP语言中get_loaded_extensions()函数的输出,会发现有一些扩展并没有php.ini文件中调用,而它们确实也已经加载到PHP里去了,可以让我们在PHP语言中使用,如standard、Reflection、Core等。它们便是静态编译的,它们没有被编译成so或者dll文件供PHP动态调用,而是直接和PHP主程序编译到一起。在*nix上执行静态编译现在,先让我们执行一下PHP源码根目录下的.
我们已经在上一节准备好了需要编译的源文件,接下来需要的便是把它们编译成目标文件了。因为在*nix平台和win平台下的编译步骤有些差异,所以这个地方需要分成两块介绍,很不幸,win部分还没有整理,请随时关注本项目。在*nix下编译第一步:我们需要根据config.
配置文件才开始,我们先用最快的(不是最标准的)的方式来建立一个代码最少的扩展。在php源码文件夹的ext目录下创建一个新的文件,这里我取的名字叫做walu,它往往就是我们扩展的名字。其实这个文件夹可以放在任何一个位置,但是为了我们在后面介绍win32的编译与静态编译,我们还是把它放在php源码的ext目录下。现在,我们在这个目录下创建一个config.
每一个PHP扩展都至少需要两个文件:一个配置文件和一个源文件。配置文件用来告诉编译器应该编译哪几个文件,以及编译本扩展是否需要的其它lib。
关注时代Java