sapi zend vm 扩展
zend vm是php的虚拟机,与jvm类似,都是各自语言的编译/执行的核心。它们都会把各自的代码先编译为一种中间代码,php的通常叫opcode,java通常叫bytecode,不同的是php的opcode直接被zend vm的执行单元调用对应的c函数执行(php7加入了ast,会先生成ast,再生成opcode),不会显示保留下来(可以cache保留),而java通常是生成class文件保留下来。而这一点可能也是php interpreter的名称的由来吧。其实相对严格的c/c++等编译型语言,php和java更多的是结合了编译型和解释性的风格。
sapi可以看作是zend vm向外界提供编译/执行php代码服务的方式和规范。无论是作为cli/cgi/fastcgi/apache_mod与其他程序交互,还是嵌入其他语言中如c/c++等,都可以通过sapi的规范实现。它的一个重要数据结构就是sapi_module_struct(main/sapi.h line 217)
扩展部分可以看作是搭建在zend vm和sapi之上的库,为php开发人员提供性能和易用性上的保证。java的各种包/python的各种模块功能类似,不同的是php中为了性能是用c扩展来实现的,类似的在java中可以通过jni来实现,python中如_socket和_select(多路复用)都不是原生python实现。
关于各种sapi或者php本身的生命周期,可能会和其他组件如apache耦合,后续再细谈。关于php扩展的生命周期,这里借用一张图。流程应该是很容易明白的,关于这个过程,网上也有很多资料,不再赘述。我们开发扩展需要注意的几个地方也可以对应到图中的某些节点:
全局变量的定义,通常是zend_modulename_globals 模块的初始化,包括资源/类/常量/ini配置等模块级的初始化 请求的初始化,包括与单次请求相关的一些初始化 请求的结束,清理单次请求相关的数据/内存 模块的卸载,清理模块相关的数据/内存
基本上我们要做的就是按照上面的流程,实现相关的内置函数,定义自己的资源/全局变量/类/函数等。值得注意的地方是在在嵌入其他语言如python或者被嵌入其他组件如apache时,要小心多进程多线程相关的问题。
使用php-src/ext/ext_skel可以生成php扩展的框架
比较重要的文件是config.m4(当然还有源码),config.m4文件可以使用phpize命令生成configure文件,其中说明了我们是否开启模块,以及外部依赖的库。