没有合适的资源?快使用搜索试试~ 我知道了~
首页深入理解PHP内核:构建与解析
深入理解PHP内核:构建与解析
需积分: 10 0 下载量 179 浏览量
更新于2024-07-17
收藏 3.18MB PDF 举报
"TIPI_2014-04-29_V0.8.3.pdf" 是一本关于深入理解PHP内核的书籍,旨在帮助读者了解PHP的内部工作机制。书中介绍了如何获取和编译PHP源代码,构建调试环境,并讲解了PHP源码的结构。此外,还讨论了在不同操作系统,如*nix和Windows下阅读源码的方法。书中提到了PHP的历史,包括其诞生、发展、应用和不同版本的变化,特别提到了PHP6的停止开发及其原由,如Unicode支持和向后不兼容性设计。 在开始学习PHP内核之前,环境搭建是非常重要的一步。在*nix环境下,可以通过访问PHP官方网站下载源码,或使用Git从官方仓库克隆最新代码。书中的示例给出了使用Git从两个不同的地址获取源码的命令。获取源码后,可以通过编译过程建立调试环境,这通常包括配置、编译和安装步骤,以便在本地环境中运行和测试PHP。 对于PHP源码的结构,书中可能涵盖了目录布局、主要组件的组织方式,以及如何定位和理解关键功能的实现。在阅读源码时,可能会遇到的一些常见语句和概念,如C、Java、Perl的语法混合,以及PHP特有的语法特性,如动态页面的执行效率和编译后的代码优化,都会被详细解析。 此外,书中可能还会涉及PHP与HTML的集成,CGI和Perl的比较,以及PHP在Web开发中的作用。对于初学者,了解这些背景知识有助于更好地理解和应用PHP。通过学习这本书,读者可以期待提升对PHP内部工作原理的理解,从而更好地优化代码、解决性能问题,并进行更高级的定制化开发。
资源详情
资源推荐
![](https://csdnimg.cn/release/download_crawler_static/11238449/bg10.jpg)
module_shutdown_func⽅法,即PHP_RSHUTDOWN_FUNCTION宏产⽣的那个函数。
在关闭所有的模块后,PHP继续销毁全局函数表,销毁全局类表、销售全局变量表等。 通过zend_shutdown_extensions遍历
zend_extensions所有元素,调⽤每个扩展的shutdown函数。
多进程SAPI⽣命周期
通常PHP是编译为apache的⼀个模块来处理PHP请求。Apache⼀般会采⽤多进程模式, Apache启动后会fork出多个⼦进程,每个进程的
内存空间独⽴,每个⼦进程都会经过开始和结束环节, 不过每个进程的开始阶段只在进程fork出来以来后进⾏,在整个进程的⽣命周期内可能
会处理多个请求。 只有在Apache关闭或者进程被结束之后才会进⾏关闭阶段,在这两个阶段之间会随着每个请求重复请求开始-请求关闭的环
节。 如图2.2所示:
图2.2 多进程SAPI⽣命周期
多线程的SAPI⽣命周期
多线程模式和多进程中的某个进程类似,不同的是在整个进程的⽣命周期内会并⾏的重复着 请求开始-请求关闭的环节
图2.3 多线程SAPI⽣命周期
Zend引擎
Zend引擎是PHP实现的核⼼,提供了语⾔实现上的基础设施。例如:PHP的语法实现,脚本的编译运⾏环境, 扩展机制以及内存管理
等,当然这⾥的PHP指的是官⽅的PHP实现(除了官⽅的实现, ⽬前⽐较知名的有facebook的hiphop实现,不过到⽬前为⽌,PHP还没有⼀个
标准的语⾔规范), ⽽PHP则提供了请求处理和其他Web服务器的接⼝(SAPI)。
⽬前PHP的实现和Zend引擎之间的关系⾮常紧密,甚⾄有些过于紧密了,例如很多PHP扩展都是使⽤的Zend API, ⽽Zend正是PHP语⾔
本身的实现,PHP只是使⽤Zend这个内核来构建PHP语⾔的,⽽PHP扩展⼤都使⽤Zend API, 这就导致PHP的很多扩展和Zend引擎耦合在
⼀起了,在笔者编写这本书的时候PHP核⼼开发者就提出将这种耦合解开,
⽬前PHP的受欢迎程度是毋庸置疑的,但凡流⾏的语⾔通常都会出现这个语⾔的其他实现版本, 这在Java社区⾥就⾮常明显,⽬前已经
有⾮常多基于JVM的语⾔了,例如IBM的Project Zero就实现了⼀个基于JVM的PHP实现, .NET也有类似的实现,通常他们这样做的原因⽆⾮
是因为:他们喜欢这个语⾔,但⼜不想放弃原有的平台, 或者对现有的语⾔实现不满意,处于性能或者语⾔特性等(HipHop就是这样诞⽣
的)。
TIPI:深⼊理解PHP内核 TIPI_2014-04-29_V0.8.3
![](https://csdnimg.cn/release/download_crawler_static/11238449/bg11.jpg)
很多脚本语⾔中都会有语⾔扩展机制,PHP中的扩展通常是通过Pear库或者原⽣扩展,在Ruby中则这两者的界限不是很明显, 他们甚⾄
会提供两套实现,⼀个主要⽤于在⽆法编译的环境下使⽤,⽽在合适的环境则使⽤C实现的原⽣扩展, 这样在效率和可移植性上都可以保证。
⽬前这些为PHP编写的扩展通常都⽆法在其他的PHP实现中实现重⽤, HipHop的做法是对最为流⾏的扩展进⾏重写。如果PHP扩展能和
ZendAPI解耦,则在其他语⾔中重⽤这些扩展也将更加容易了。
参考⽂献
Extending and Embedding PHP
第⼆节 SAPI概述
前⼀⼩节介绍了PHP的⽣命周期,在其⽣命周期的各个阶段,⼀些与服务相关的操作都是通过SAPI接⼝实现。 这些内置实现的物理位置
在PHP源码的SAPI⽬录。这个⽬录存放了PHP对各个服务器抽象层的代码, 例如命令⾏程序的实现,Apache的mod_php模块实现以及fastcgi
的实现等等。
在各个服务器抽象层之间遵守着相同的约定,这⾥我们称之为SAPI接⼝。 每个SAPI实现都是⼀个_sapi_module_struct结构体变量。
(SAPI接⼝)。 在PHP的源码中,当需要调⽤服务器相关信息时,全部通过SAPI接⼝中对应⽅法调⽤实现, ⽽这对应的⽅法在各个服务器抽
象层实现时都会有各⾃的实现。
由于很多操作的通⽤性,有很⼤⼀部分的接⼝⽅法使⽤的是默认⽅法。
如图2.4所示,为SAPI的简单示意图。
图2.4 SAPI的简单示意图
以cgi模式和apache2服务器为例,它们的启动⽅法如下:
cgi_sapi_module.startup(&cgi_sapi_module) // cgi
模式
cgi/cgi_main.c
⽂件
apache2_sapi_module.startup(&apache2_sapi_module);
// apache2
服务器
apache2handler/sapi_apache2.c
⽂件
这⾥的cgi_sapi_module是sapi_module_struct结构体的静态变量。 它的startup⽅法指向php_cgi_startup函数指针。在这个结构体中除了
startup函数指针,还有许多其它⽅法或字段。 其部分定义如下:
struct _sapi_module_struct {
char *name; //
名字(标识⽤)
char *pretty_name; //
更好理解的名字(⾃⼰翻译的)
int (*startup)(struct _sapi_module_struct *sapi_module); //
启动函数
int (*shutdown)(struct _sapi_module_struct *sapi_module); //
关闭⽅法
int (*activate)(TSRMLS_D); //
激活
int (*deactivate)(TSRMLS_D); //
停⽤
int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC);
//
不缓存的写操作
(unbuffered write)
void (*flush)(void *server_context); // flush
struct stat *(*get_stat)(TSRMLS_D); // get uid
char *(*getenv)(char *name, size_t name_len TSRMLS_DC); // getenv
void (*sapi_error)(int type, const char *error_msg, ...); /* error handler */
int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op,
sapi_headers_struct *sapi_headers TSRMLS_DC); /* header handler */
TIPI:深⼊理解PHP内核 TIPI_2014-04-29_V0.8.3
![](https://csdnimg.cn/release/download_crawler_static/11238449/bg12.jpg)
/* send headers handler */
int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC);
void (*send_header)(sapi_header_struct *sapi_header,
void *server_context TSRMLS_DC); /* send header handler */
int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC); /* read POST data */
char *(*read_cookies)(TSRMLS_D); /* read Cookies */
/* register server variables */
void (*register_server_variables)(zval *track_vars_array TSRMLS_DC);
void (*log_message)(char *message); /* Log message */
time_t (*get_request_time)(TSRMLS_D); /* Request Time */
void (*terminate_process)(TSRMLS_D); /* Child Terminate */
char *php_ini_path_override; //
覆盖的
ini
路径
...
...
};
其中⼀些函数指针的说明如下:
startup 当SAPI初始化时,⾸先会调⽤该函数。如果服务器处理多个请求时,该函数只会调⽤⼀次。 ⽐如Apache的SAPI,它是以
mod_php5的Apache模块的形式加载到Apache中的, 在这个SAPI中,startup函数只在⽗进程中创建⼀次,在其fork的⼦进程中不会调
⽤。
activate 此函数会在每个请求开始时调⽤,它会再次初始化每个请求前的数据结构。
deactivate 此函数会在每个请求结束时调⽤,它⽤来确保所有的数据都,以及释放在activate中初始化的数据结构。
shutdown 关闭函数,它⽤来释放所有的SAPI的数据结构、内存等。
ub_write 不缓存的写操作(unbuffered write),它是⽤来将PHP的数据输出给客户端, 如在CLI模式下,其最终是调⽤fwrite实现向标准输
出输出内容;在Apache模块中,它最终是调⽤Apache提供的⽅法rwrite。
sapi_error 报告错误⽤,⼤多数的SAPI都是使⽤的PHP的默认实现php_error。
flush 刷新输出,在CLI模式下通过使⽤C语⾔的库函数fflush实现,在php_mode5模式下,使⽤Apache的提供的函数函数rflush实现。
read_cookie 在SAPI激活时,程序会调⽤此函数,并且将此函数获取的值赋值给SG(request_info).cookie_data。 在CLI模式下,此函数
会返回NULL。
read_post 此函数和read_cookie⼀样也是在SAPI激活时调⽤,它与请求的⽅法相关,当请求的⽅法是POST时,程序会操作$_POST、
$HTTP_RAW_POST_DATA等变量。
send_header 发送头部信息,此⽅法⼀般的SAPI都会定制,其所不同的是,有些的会调服务器⾃带的(如Apache),有些的需要你⾃
⼰实现(如 FastCGI)。
以上的这些结构在各服务器的接⼝实现中都有定义。如Apache2的定义:
static sapi_module_struct apache2_sapi_module = {
"apache2handler",
"Apache 2.0 Handler",
php_apache2_startup, /* startup */
php_module_shutdown_wrapper, /* shutdown */
...
}
在PHP的源码中实现了很多的实现,⽐如IIS的实现以及⼀些⾮主流的Web服务器实现,其⽂件结构如图2.5所示:
TIPI:深⼊理解PHP内核 TIPI_2014-04-29_V0.8.3
![](https://csdnimg.cn/release/download_crawler_static/11238449/bg13.jpg)
图2.5 SAPI⽂件结构图
⽬前PHP内置的很多SAPI实现都已不再维护或者变的有些⾮主流了,PHP社区⽬前正在考虑将⼀些SAPI移出代码库。 社区
对很多功能的考虑是除⾮真的⾮常必要,或者某些功能已近⾮常通⽤了,否则就在PECL库中, 例如⾮常流⾏的APC缓存扩展将
进⼊核⼼代码库中。
整个SAPI类似于⼀个⾯向对象中的模板⽅法模式的应⽤。 SAPI.c和SAPI.h⽂件所包含的⼀些函数就是模板⽅法模式中的抽象模板, 各个
服务器对于sapi_module的定义及相关实现则是⼀个个具体的模板。
这样的结构在PHP的源码中有多处使⽤, ⽐如在PHP扩展开发中,每个扩展都需要定义⼀个zend_module_entry结构体。 这个结构体的
作⽤与sapi_module_struct结构体类似,都是⼀个类似模板⽅法模式的应⽤。 在PHP的⽣命周期中如果需要调⽤某个扩展,其调⽤的⽅法都是
zend_module_entry结构体中指定的⽅法, 如在上⼀⼩节中提到的在执⾏各个扩展的请求初始化时,都是统⼀调⽤request_startup_func⽅
法, ⽽在每个扩展的定义时,都通过宏PHP_RINIT指定request_startup_func对应的函数。 以VLD扩展为例:其请求初始化为
PHP_RINIT(vld),与之对应在扩展中需要有这个函数的实现:
PHP_RINIT_FUNCTION(vld) {
}
所以, 我们在写扩展时也需要实现扩展的这些接⼝,同样,当实现各服务器接⼝时也需要实现其对应的SAPI。
Apache模块
Apache是Apache软件基⾦会的⼀个开放源代码的Web服务器, 可以在⼤多数电脑操作系统中运⾏,由于其跨平台和安全性被⼴泛使⽤,
是最流⾏的Web服务器端软件之⼀。 Apache⽀持许多特性,⼤部分通过模块扩展实现。常⻅的模块包括mod_auth(权限验证)、
mod_ssl(SSL和TLS⽀持) mod_rewrite(URL重写)等。⼀些通⽤的语⾔也⽀持以Apache模块的⽅式与Apache集成。 如
Perl,Python,Tcl,和PHP等。
当PHP需要在Apache服务器下运⾏时,⼀般来说,它可以mod_php5模块的形式集成, 此时mod_php5模块的作⽤是接收Apache传递过
来的PHP⽂件请求,并处理这些请求, 然后将处理后的结果返回给Apache。如果我们在Apache启动前在其配置⽂件中配置好了PHP模块
(mod_php5), PHP模块通过注册apache2的ap_hook_post_config挂钩,在Apache启动的时候启动此模块以接受PHP⽂件的请求。
除了这种启动时的加载⽅式,Apache的模块可以在运⾏的时候动态装载, 这意味着对服务器可以进⾏功能扩展⽽不需要重新对源代码进
⾏编译,甚⾄根本不需要停⽌服务器。 我们所需要做的仅仅是给服务器发送信号HUP或者AP_SIG_GRACEFUL通知服务器重新载⼊模块。
但是在动态加载之前,我们需要将模块编译成为动态链接库。此时的动态加载就是加载动态链接库。 Apache中对动态链接库的处理是通过模
块mod_so来完成的,因此mod_so模块不能被动态加载, 它只能被静态编译进Apache的核⼼。这意味着它是随着Apache⼀起启动的。
Apache是如何加载模块的呢?我们以前⾯提到的mod_php5模块为例。 ⾸先我们需要在Apache的配置⽂件httpd.conf中添加⼀⾏:
LoadModule php5_module modules/mod_php5.so
TIPI:深⼊理解PHP内核 TIPI_2014-04-29_V0.8.3
![](https://csdnimg.cn/release/download_crawler_static/11238449/bg14.jpg)
这⾥我们使⽤了LoadModule命令,该命令的第⼀个参数是模块的名称,名称可以在模块实现的源码中找到。 第⼆个选项是该模块所处的
路径。如果需要在服务器运⾏时加载模块, 可以通过发送信号HUP或者AP_SIG_GRACEFUL给服务器,⼀旦接受到该信号,Apache将重新
装载模块, ⽽不需要重新启动服务器。
在配置⽂件中添加了所上所示的指令后,Apache在加载模块时会根据模块名查找模块并加载, 对于每⼀个模块,Apache必须保证其⽂件
名是以“mod_”开始的,如PHP的mod_php5.c。 如果命名格式不对,Apache将认为此模块不合法。Apache的每⼀个模块都是以module结构体
的形式存在, module结构的name属性在最后是通过宏STANDARD20_MODULE_STUFF以__FILE__体现。 关于这点可以在后⾯介绍
mod_php5模块时有看到。这也就决定了我们的⽂件名和模块名是相同的。 通过之前指令中指定的路径找到相关的动态链接库⽂件
后,Apache通过内部的函数获取动态链接库中的内容, 并将模块的内容加载到内存中的指定变量中。
在真正激活模块之前,Apache会检查所加载的模块是否为真正的Apache模块, 这个检测是通过检查module结构体中的magic字段实现
的。 ⽽magic字段是通过宏STANDARD20_MODULE_STUFF体现,在这个宏中magic的值为MODULE_MAGIC_COOKIE,
MODULE_MAGIC_COOKIE定义如下:
#define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */
最后Apache会调⽤相关函数(ap_add_loaded_module)将模块激活, 此处的激活就是将模块放⼊相应的链表中(ap_top_modules链表:
ap_top_modules链表⽤来保存Apache中所有的被激活的模块,包括默认的激活模块和激活的第三⽅模块。)
Apache加载的是PHP模块,那么这个模块是如何实现的呢到我们以Apache2的mod_php5模块为例进⾏说明。
Apache2的mod_php5模块说明
Apache2的mod_php5模块包括sapi/apache2handler和sapi/apache2filter两个⽬录 在apache2_handle/mod_php5.c⽂件中,模块定义的相
关代码如下:
AP_MODULE_DECLARE_DATA module php5_module = {
STANDARD20_MODULE_STUFF,
/*
宏,包括版本,⼩版本,模块索引,模块名,下⼀个模块指针等信息,其中模块名以
__FILE__
体现
*/
create_php_config, /* create per-directory config structure */
merge_php_config, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
php_dir_cmds, /*
模块定义的所有的指令
*/
php_ap2_register_hook
/*
注册钩⼦,此函数通过
ap_hoo_
开头的函数在⼀次请求处理过程中对于指定的步骤注册钩⼦
*/
};
它所对应的是Apache的module结构,module的结构定义如下:
typedef struct module_struct module;
struct module_struct {
int version;
int minor_version;
int module_index;
const char *name;
void *dynamic_load_handle;
struct module_struct *next;
unsigned long magic;
void (*rewrite_args) (process_rec *process);
void *(*create_dir_config) (apr_pool_t *p, char *dir);
void *(*merge_dir_config) (apr_pool_t *p, void *base_conf, void *new_conf);
void *(*create_server_config) (apr_pool_t *p, server_rec *s);
void *(*merge_server_config) (apr_pool_t *p, void *base_conf, void *new_conf);
const command_rec *cmds;
void (*register_hooks) (apr_pool_t *p);
}
上⾯的模块结构与我们在mod_php5.c中所看到的结构有⼀点不同,这是由于STANDARD20_MODULE_STUFF的原因, 这个宏它包含了
前⾯8个字段的定义。STANDARD20_MODULE_STUFF宏的定义如下:
/** Use this in all standard modules */
#define STANDARD20_MODULE_STUFF MODULE_MAGIC_NUMBER_MAJOR, \
MODULE_MAGIC_NUMBER_MINOR, \
-1, \
__FILE__, \
NULL, \
TIPI:深⼊理解PHP内核 TIPI_2014-04-29_V0.8.3
剩余205页未读,继续阅读
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![pdf](https://img-home.csdnimg.cn/images/20210720083512.png)
![pdf](https://img-home.csdnimg.cn/images/20210720083512.png)
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![docx](https://img-home.csdnimg.cn/images/20210720083331.png)
![ipynb](https://img-home.csdnimg.cn/images/20210720083646.png)
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![](https://profile-avatar.csdnimg.cn/default.jpg!1)
qq_29883803
- 粉丝: 0
- 资源: 1
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助
![](https://csdnimg.cn/release/wenkucmsfe/public/img/voice.245cc511.png)
最新资源
- 构建Cadence PSpice仿真模型库教程
- VMware 10.0安装指南:步骤详解与网络、文件共享解决方案
- 中国互联网20周年必读:影响行业的100本经典书籍
- SQL Server 2000 Analysis Services的经典MDX查询示例
- VC6.0 MFC操作Excel教程:亲测Win7下的应用与保存技巧
- 使用Python NetworkX处理网络图
- 科技驱动:计算机控制技术的革新与应用
- MF-1型机器人硬件与robobasic编程详解
- ADC性能指标解析:超越位数、SNR和谐波
- 通用示波器改造为逻辑分析仪:0-1字符显示与电路设计
- C++实现TCP控制台客户端
- SOA架构下ESB在卷烟厂的信息整合与决策支持
- 三维人脸识别:技术进展与应用解析
- 单张人脸图像的眼镜边框自动去除方法
- C语言绘制图形:余弦曲线与正弦函数示例
- Matlab 文件操作入门:fopen、fclose、fprintf、fscanf 等函数使用详解
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
![](https://img-home.csdnimg.cn/images/20220527035711.png)
![](https://img-home.csdnimg.cn/images/20220527035711.png)
![](https://img-home.csdnimg.cn/images/20220527035111.png)
安全验证
文档复制为VIP权益,开通VIP直接复制
![](https://csdnimg.cn/release/wenkucmsfe/public/img/green-success.6a4acb44.png)