2.3 PHP 7源码结构初步介绍
源码目录一定程度上反映了编码者的代码组织思路。在详细介绍源码之前,我们还有必要了解PHP 7源码的目录结构。
PHP 7主要包含这些源码目录:sapi、Zend、main、ext、TSRM,本节会简要介绍各目录构成。
2.3.1 sapi目录源码
sapi目录是对输入和输出层的抽象,是PHP提供对外服务的规范。
PHP程序的输入可以是来自于命令行的标准输入,也可以是来自基于cgi/fastcgi协议的网络请求。同理,输出可以写到命令行的标准输出,也可以作为基于cgi/fastcgi协议的网络响应返回给客户端。
PHP为支持多场景交互,为不同的场景模式编写独立的程序。
例如,命令行模式对应的是二进制程序bin/php;内置模块的模式不需要提供二进制程序,作为普通函数供Apache或任意C/C++程序来调用即可;CGI模式对应的是二进制程序bin/cgi; FastCGI模式对应的是二进制程序sbin/php-fpm。同时,对多个模式抽象出了相同的模板(源码实现为结构体sapi_module_struct),其定义了模式启动、关闭、激活(处理请求前)、失效(处理请求后)等多个钩子函数指针。每一个模式将这些函数指针指向自己的函数,实现不同模式之间处理输入、输出的差异化。
有了这一层,可以轻松扩展PHP对外服务的方式。假设需要以一种二进制协议的方式来和PHP交互,只需要实例化一个新的sapi_module_struct,并实现需要的钩子函数即可。当然,你需要在钩子函数里面关心如何根据二进制协议解析出相应的脚本文件和参数等输入/输出问题。
下面介绍几种常用的SAPI。
1)apache2handler:Apache扩展,编译后生成动态链接库,配置到Apache下,当有http请求到Apache时,根据配置会调用此动态链接库,执行PHP代码,完成与PHP的交互。
2)cgi-fcgi:编译后生成支持CGI协议的可执行程序,webserver(通常为Apache或Nginx)通过CGI协议把请求传给CGI进程,执行代码将结果返回给webserver,退出进程。
3)fpm-fcgi:fpm全称为FastCGI Process Manager, PHP官方提供的FastCGI进程管理器。以Nginx服务器为例,当有http协议请求发送到Nginx服务器,Nginx按照FastCGI协议把请求交给php-fpm进程处理。
4)cli:Command Line Interface的简称,PHP的命令行交互接口。
2.3.2 Zend目录源码
Zend目录是PHP的核心代码。我们选取其中几个比较受关注的部分简要阐述。
1.内存管理模块
PHP实现了自己的内存管理器,主要操作实现在zend_alloc_sizes.h、zend_alloc.h、zend_alloc.c三个文件中。
1)zend_alloc_sizes.h:PHP的内存管理器也实行分级管理,即分配策略按照需要的大小共有三种规格,分配时会根据实际需要空间选择对齐,再进行分配。规格分级从小到大分别称为small、large和huge。该文件便定义了PHP内存分配的基本单位。这里简要说明下三种规格。
❑ small内存小于3072B。当PHP申请的内存小于3072字节,使用small分配策略;
❑ large内存介于3072B和4KB之间;
❑ huge内存大于2MB。
2)zend_alloc.h:主要是一些内存操作函数的声明。PHP内存管理器在C语言常见内存操作函数malloc()、free()等之上做了一层封装。
3)zend_alloc.c:定义了内存操作函数的实现以及PHP内存管理器的核心数据结构_zend_mm_heap等。
本书第9章会重点介绍PHP内存管理器的实现。
2.垃圾回收
为了解决循环引用问题,PHP引入了垃圾回收机制。PHP 7垃圾回收的实现主要包含在源文件zend_gc.h和zend_gc.c中,本书第3章将展开说明PHP 7的垃圾回收机制。
3.数组实现
数组是PHP工程实践中最重要、最常用的复杂类型之一。支撑数组的底层数据结构HashTable也经常在扩展开发中被开发者使用。
PHP 7数组的底层设计主要在zend_hash.c和zend_hash.h两个文件中实现。本书也在第5章,单独详尽地介绍PHP 7数组的实现。
2.3.3 main目录源码
main目录是SAPI层和Zend层的黏合剂。
Zend层实现了PHP脚本的编译和执行,sapi层实现了输入和输出的抽象,main目录则起到了承上启下的作用:承上,解析SAPI的请求,分析要执行的脚本文件和参数;启下,调用Zend引擎之前,完成必要的初始化等工作。如模块初始化——php_module_startup()。上文SAPI目录提到的模式启动的钩子函数都会调用这个API,再如脚本执行——php_execute_script(),它是执行PHP脚本的通用入口,可以在main目录中找到。
2.3.4 ext目录源码
ext是PHP扩展相关的目录,常用的array、str、pdo等系列函数都在这里定义。
PHP扩展开发在本书第14章有详尽介绍。
2.3.5 TSRM目录源码
PHP在早期更多的是单个进程、单线程模型运行的,在后期才引入了线程安全机制ZTS(Zend Thread Safety)。TSRM正是在这样的场景之下诞生的。
TSRM是Thread Safe Resource Manager的缩写——线程安全资源管理器。不同于本章其他小节在PHP应用开发中的广泛涉及与实践,PHP多线程实际开发场景要少得多。PHP提供了条件编译选项——enable-maintainer-zts,以激活定义ZTS常量,支持线程安全。
线程安全机制主要为了保证共享资源的安全。PHP的线程安全机制简洁直观——在多线程环境下,为每个线程提供独立的全局变量副本。具体实施是通过TSRM为每个线程分配(分配前加锁)一个独立ID(自增)作为当前线程的全局变量内存区索引,在以后的全局变量访问中,实现线程之间的完全独立。