PHP 7源码结构初步介绍

源码目录一定程度上反映了编码者的代码组织思路。在详细介绍源码之前,我们还有必要了解 PHP 7 源码的目录结构。

PHP 7 主要包含这些源码目录:sapiZendmainextTSRM,本节会简要介绍各目录构成。

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 的命令行交互接口。

Zend目录源码

Zend 目录是 PHP 的核心代码。我们选取其中几个比较受关注的部分简要阐述。

内存管理模块

PHP 实现了自己的内存管理器,主要操作实现在 zend_alloc_sizes.hzend_alloc.hzend_alloc.c 三个文件中。

  1. zend_alloc_sizes.h:PHP 的内存管理器也实行分级管理,即分配策略按照需要的大小共有三种规格,分配时会根据实际需要空间选择对齐,再进行分配。规格分级从小到大分别称为 smalllargehuge。该文件便定义了 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 内存管理器的实现。

本书第 9 章会重点介绍 PHP 内存管理器的实现。

垃圾回收

为了解决循环引用问题,PHP 引入了垃圾回收机制。PHP 7 垃圾回收的实现主要包含在源文件 zend_gc.hzend_gc.c 中,本书第 3 章将展开说明 PHP 7 的垃圾回收机制。

数组实现

数组是 PHP 工程实践中最重要、最常用的复杂类型之一。支撑数组的底层数据结构 HashTable 也经常在扩展开发中被开发者使用。

PHP 7 数组的底层设计主要在 zend_hash.czend_hash.h 两个文件中实现。本书也在第 5 章,单独详尽地介绍 PHP 7 数组的实现。

main目录源码

main 目录是 SAPI 层和 Zend 层的黏合剂。

Zend 层实现了 PHP 脚本的编译和执行,sapi 层实现了输入和输出的抽象,main 目录则起到了承上启下的作用:承上,解析 SAPI 的请求,分析要执行的脚本文件和参数;启下,调用 Zend 引擎之前,完成必要的初始化等工作。如模块初始化—— php_module_startup()。上文 SAPI 目录提到的模式启动的钩子函数都会调用这个 API,再如脚本执行—— php_execute_script(),它是执行 PHP 脚本的通用入口,可以在 main 目录中找到。

ext目录源码

extPHP 扩展相关的目录,常用的 arraystrpdo 等系列函数都在这里定义。

PHP 扩展开发在本书第 14 章有详尽介绍。

TSRM目录源码

PHP 在早期更多的是单个进程、单线程模型运行的,在后期才引入了线程安全机制 ZTS(Zend Thread Safety)TSRM 正是在这样的场景之下诞生的。

TSRM 是 Thread Safe Resource Manager 的缩写——线程安全资源管理器。不同于本章其他小节在 PHP 应用开发中的广泛涉及与实践,PHP 多线程实际开发场景要少得多。PHP 提供了条件编译选项—— enable-maintainer-zts,以激活定义 ZTS 常量,支持线程安全。

线程安全机制主要为了保证共享资源的安全。PHP 的线程安全机制简洁直观——在多线程环境下,为每个线程提供独立的全局变量副本。具体实施是通过 TSRM 为每个线程分配(分配前加锁)一个独立 ID(自增)作为当前线程的全局变量内存区索引,在以后的全局变量访问中,实现线程之间的完全独立。

线程安全资源管理器

PHP 的线程安全实现是通过 “线程安全资源管理器”(Thread-Safe Resource Manager, TSRM)机制来实现的。TSRM 是一个管理全局和静态变量访问的抽象层,用于确保多线程环境下的线程安全。以下是 PHP 线程安全实现原理的详细解释:

TSRM 的基本原理

在多线程环境中,每个线程需要自己独立的一套环境变量和数据,以防止数据竞争和数据不一致。TSRM 提供了一种机制,使得每个线程都有自己的资源副本。

具体实现

TSRMLS 宏
  • 在 PHP 的多线程版本中(例如使用 Apache 的 mod_php 时),很多 PHP 内部函数使用了 TSRMLS 宏。这些宏的作用是在函数调用时,确保正确的上下文切换。

  • 典型的宏包括 TSRMLS_DC 和 TSRMLS_CC。TSRMLS_DC(Declare Context)用于声明一个函数需要上下文,而 TSRMLS_CC(Call Context)用于在调用函数时传递上下文。

线程本地存储(Thread Local Storage, TLS)
  • TSRM 使用 TLS 技术来确保每个线程都有自己的数据副本。

  • 在每个线程启动时,会分配一块独立的存储空间,这块空间用来存放这个线程独有的变量。

资源管理
  • TSRM 管理所有需要线程安全的全局变量和静态变量。它通过在每个线程中分配这些变量的独立副本,来确保线程之间的数据隔离。

  • 例如,当一个线程需要访问某个全局变量时,TSRM 会提供该线程专有的变量副本,而不是访问共享的全局变量。

初始化和销毁
  • 在 PHP 初始化阶段,TSRM 系统会初始化,并为每个线程分配存储空间。

  • 在线程结束时,TSRM 系统会清理这些线程专有的存储空间,防止内存泄漏。

示例代码

假设我们有一个需要在多线程环境中安全访问的全局变量:

#ifdef ZTS
# define MY_G(v) TSRMG(my_globals_id, zend_my_globals *, v)
#else
# define MY_G(v) (my_globals.v)
#endif

typedef struct _zend_my_globals {
    int my_global_value;
} zend_my_globals;

ZEND_BEGIN_MODULE_GLOBALS(my)
    zend_my_globals *my_globals;
ZEND_END_MODULE_GLOBALS(my)

ZEND_DECLARE_MODULE_GLOBALS(my)

static void php_my_init_globals(zend_my_globals *my_globals) {
    my_globals->my_global_value = 0;
}

PHP_MINIT_FUNCTION(my) {
    ZEND_INIT_MODULE_GLOBALS(my, php_my_init_globals, NULL);
    return SUCCESS;
}

netware 目录源码

在 PHP 源码中,netware 一般是指针对 NetWare 操作系统的支持。NetWare 是 Novell 开发的一种网络操作系统,主要用于文件和打印服务。PHP 中的 NetWare 相关代码和功能主要用于在这种操作系统上运行 PHP 脚本。

具体来说,netware 相关代码可能涉及以下几个方面:

  1. 系统调用和环境:处理 NetWare 系统特有的系统调用和环境变量。

  2. 文件系统:处理 NetWare 特有的文件系统操作,例如路径处理、文件权限等。

  3. 网络通信:处理 NetWare 的网络通信协议和接口。

  4. 编译和配置:在编译 PHP 源代码时,根据目标操作系统是 NetWare,调整编译选项和配置。

PHP 中可能会有一些条件编译的代码,例如:

#ifdef NETWARE
// NetWare 特定的代码
#endif

这种代码块会在编译时根据是否定义了 NETWARE 这个宏来决定是否包含其中的代码。

总的来说,netware 相关的代码和功能是为了确保 PHP 能在 NetWare 操作系统上正常运行,并且能够利用该操作系统的特性。

scripts 目录源码

在 PHP 源代码中,scripts 目录通常包含各种脚本,这些脚本在 PHP 源代码的编译、安装、配置和测试过程中发挥重要作用。具体来说,scripts 目录可能包含以下几类脚本:

构建脚本
  • 这些脚本用于编译和构建 PHP 源代码。例如,configure 脚本用于配置编译选项,生成适用于当前系统的 Makefile。

  • 典型的构建脚本有:configure, buildconf, genfiles 等。

安装脚本
  • 这些脚本用于安装 PHP 和相关扩展。它们通常在构建完成后运行,负责将编译好的二进制文件和库文件复制到适当的安装目录。

  • 例如:install-pear-nozlib.phar 用于安装 PEAR 包管理系统。

测试脚本
  • 这些脚本用于运行 PHP 的测试套件,以确保代码的正确性和稳定性。它们可以在开发过程中运行,也可以在持续集成环境中运行。

  • 例如:run-tests.php 用于运行 PHP 的单元测试。

工具脚本
  • 这些脚本提供各种辅助工具,用于开发和维护 PHP 代码库。例如,格式化代码、生成文档、检查代码质量等。

  • 例如:pharcmdphpize 等。

配置脚本
  • 这些脚本用于生成或修改配置文件,设置 PHP 环境和选项。例如,生成 php.ini 文件,配置扩展等。

  • 例如:php-config 脚本用于提供 PHP 配置信息。

典型的 scripts 目录结构和内容可能如下:

php-src/
└── scripts/
    ├── configure
    ├── buildconf
    ├── genfiles
    ├── install-pear-nozlib.phar
    ├── run-tests.php
    ├── pharcmd
    ├── phpize
    ├── php-config
    └── other-scripts...

这些脚本在 PHP 的开发和部署过程中扮演着重要的角色,确保代码可以正确地编译、配置、安装和运行。

win32 目录源码

在 PHP 源代码中,win32 目录通常包含与在 Windows 操作系统上构建和运行 PHP 相关的代码和文件。这个目录的主要作用是提供特定于 Windows 平台的支持,以确保 PHP 能够在 Windows 上正常编译、安装和运行。

具体来说,win32 目录的作用包括以下几个方面:

  1. Windows 平台特定的实现:

    • 包含用于在 Windows 上实现 PHP 功能的代码。例如,文件系统操作、网络通信、进程管理等,这些操作在 Windows 上可能与其他操作系统有所不同。

  2. 编译和构建支持:

    • 包含用于在 Windows 上编译和构建 PHP 的脚本和配置文件。这些文件配置了 Windows 编译器(如 MSVC)和构建工具(如 nmake)的选项和参数。

    • 例如:buildconf.js, config.w32 等脚本和配置文件。

  3. Windows 特定的扩展:

    • 包含一些特定于 Windows 平台的 PHP 扩展的源代码和配置。这些扩展可能利用 Windows 特有的功能或 API。

    • 例如:对 COM 和 DCOM 的支持,Windows 服务等。

  4. 安装和部署脚本:

    • 包含用于在 Windows 上安装和配置 PHP 的脚本和工具。这些脚本帮助用户在 Windows 系统上正确设置 PHP 环境。

    • 例如:phpize.bat, php-config.bat 等。

通过这些文件和目录,PHP 源代码可以在 Windows 平台上顺利地编译和运行,同时利用 Windows 系统的特性和功能。