后端开发干货:PHP源码阅读技巧
wptr33 2025-01-03 19:19 17 浏览
作者:张勇,腾讯IEG后台开发工程师
写在前面
时光匆匆 光阴似箭,今年已经是小张做码农的第N个年头了,得益于世界上最好的语言(PHP),小张的头发依旧茂密。但是项目写了一个又一个,PHP、swoole用到飞起,这些年的成长却大不如从前。
你是否也纠结于此,今天让我们换个角度看PHP,打开源码从C的角度看PHP,看一眼这个具有20年历史的庞大C项目是如何工作的。
从这一刻开始,你将荣升为C程序员,你可以去温习一下C的语法、结构体、指针和引用。
你也许会问,看个代码git拉一下用sublime打开不就好了吗,为什么要写个KM。确实这样可以看代码,但那叫看代码片段,不是看工程。PHP项目需要兼容Windows、Linux、Mac平台还要处理线程安全,其代码包含大量宏定义。我们需要借助IDE的自动跳转和提示才能更好的阅读。
我们需要一个IDE无错误提示的项目。(当你要看一个方法却无法跳转的时候,真是太难受了)
devCloud配置
devCloud是内部用于开发调试的linux虚拟机。没有的小伙伴可以自己准备一台可以ssh的CentOS虚拟机,本地虚拟机或云平台都可以。(最好网络能好一点,后面要传不少东西)
cmake re2c
后面我们需要使用Clion作为C项目的IDE,Clion需要使用cmake作为项目自动化构建工具。所以我们首先需要安装cmake devCloud上的yum默认的cmake版本为2.8.12.2符合要求,这里就直接用yum安装。
yum install cmake
re2c 是语法生成器
yum install re2c
gdb
devCloud的gdb版本过低,这里需要手动安装一个符合要求的版本,我选了7.8.2
wget http://ftp.gnu.org/gnu/gdb/gdb-7.8.2.tar.gz
tar zxvf gdb-7.8.2.tar.gz
cd gdb-7.8.2
./configure
make
make install
代码拉取
选择一个php版本,这里我用7.1.33版本。 有几个理由:
1.项目中使用了这个版本。
2.php7对内核做了大量的改动,之前的php5已经不适合学习了。
3.后续会介绍的书籍也是基于php7.0的版本和php7.1比较接近。
4.php8目前还在测试,且和php7比较接近。
注意:本地远程都拉取一份,要保持tag一致,两边都要执行下面的cmake改造
git clone https://github.com/php/php-src.git
cd php-src
git tag -l
git checkout php-7.1.33
Clion配置
工欲善其事必先利其器,要看代码需要选一个好的IDE,多年使用PhpStorm的我推荐使用全家桶里面的Clion。一般我们不会直接使用Linux做桌面系统,这里使用Clion的远程部署功能配合devCloud做环境部署。我的本地是Windows环境,构建编译在远端的devCloud进行。(用WSL也是一样的,但是PHP的git仓库太大在WSL中做git操作非常卡,应该是WSL文件系统的bug)
PHP cmake改造
这一步是阻止大部分同学使用Clion查看PHP源码的主要原因。PHP项目由于历史悠久一直使用autoconfig作为项目自动管理工具。然而Clion并不支持autoconfig,它需要项目使用cmake构建,因此我们需要对php的源码做cmake构建改造。
https://wiki.php.net/internals/cmake 官网有一份cmake的改造文档。
https://github.com/gloob/php-cmake github上也有对应的代码。
但是这个文档和代码都太老了,应该是PHP5年代的代码。我对这个项目做了一些升级,兼容了PHP7(水平有限可能存在问题,欢迎提PR)。
在本地执行如下命令,clone php-cmake项目,切换分支,拷贝php-cmake/cmake中的所有文件到php源代码目录。注意目录合并。
git clone https://github.com/bobzhangyong/php-cmake
cd php-cmake
git checkcout feature/support_php_7_1
cp -r cmake/* ../php-src //这里是php源码的路径
Toolchains配置
这里设置远程机器的信息,设置相关工具的路径。一般会自己识别,如果不能识别可以手动输入。会检查版本,默认devCloud上的版本好像都不正确需要自己升级。
Deployment配置
这里需要设置代码同步的目录,设置好以后点按√好设置为默认。完成上面Toolchains设置后,这里可能默认出现一个devCloud的信息,但是Mappings 里面的Deployment path是/tmp/目录下面,你可以删除自己配置,也可以直接修改Deployment path到你需要的项目目录。
注意:这里的Local path如果是windows上需要使用/分割路径,不是windows默认的\,否则在执行cmake构建的时候会提示目录无法找到。
在Options里面可以设置 不要同步.phpt文件,这是php项目中的测试用例文件。(我们在拉代码的时候其实已经会手动保证本地和远程机器的文件一致,但是Clion还会自己同步一次,设置这个可以加速同步速度。)
同步代码到devCloud。首次同步真的很慢,你需要耐心等待。
执行cmake构建,看看报错不?
由于PHP cmake构建会在远端生成一些文件,我们需要同步这些文件到本地,否则打开代码会因为这部分.c .h文件不存在导致代码没有提示,各种报错。
选择比对方式 用Size adn Timestamp 会快一点,这里比较慢,耐心等待一下。
分析完成以后,就可以看到远程比本地多的文件了,我们需要把他们同步到本地。最上面的.clion.source.upload.marker 不需要同步,可以点击红框中的箭头取消。 之后点击同步。
打开php_cli.c看看源码,是不是干干净净一个红线都没有。
断点
光看肯定是不行的,最好能把代码跑起来 打个断点可以更深入的了解运行机制。你可以用gdb去断点。我这里既然用了clion就直接用clion的断点功能,用起来更直观。
1、准备一个yong.php 写一点代码用来做测试文件。
2、这里配置运行配置。
3、设置 arguments 和 Working directory
4、把yong.php文件同步到远端。执行以下看看。
5、打断点。真的很爽~~
开始阅读
目录结构
PHP项目有很多目录,大概功能如下:
├── CMake //用于cmake构建的文件,我们做cmake 改造的时候拷贝进去的。
├── TSRM //线程安全相关,你如果刚开始看就先忽略 不要看了
├── Zend //Zend内核的代码
├── appveyor //一些脚本 忽略
├── build //autoconf 构建用脚本 忽略
├── cmake-build-debug //cmake构建后产物 忽略
├── ext //扩展目录,这块可以找感兴趣的扩展看一下
├── main //主要的php逻辑
├── netware //用于兼容Netware操作系统的 直接忽略
├── pear //忽略
├── sapi //入口部分,fpm cgi 都在这里,我们可以先看一下cgi目录
├── scripts //脚本忽略
├── tests //测试用例 忽略
├── travis //构建用 忽略
└── win32 //兼容windows系统 忽略
其实和源代码相关的 只有这几个目录, 在Clion中标记为Source和Header目录方便IDE构建索引。
main
ext\standard
sapi\cli
Zend
从哪里开始
准备工作都做好了,还有借口不看代码吗?如果你还是不知道从哪里开始,那我推荐从main函数开始吧。 它在sapi\cli\php_cli.c的最后面。 附图一张可以和源代码结合起来看。 图片引自:https://github.com/pangudashu/php7-internal/blob/master/1/base_process.md
最后推荐一本书 秦朋 的 《PHP7内核剖析》
- 上一篇:你根本不懂的黑客神技,如何利用PHP-FPM远程代码执行
- 下一篇:PHP技能评测
相关推荐
- 一篇文章带你了解PHP的学习使用(php的教程)
-
ThinkPHP5实战...
- 在memcached管理php的session(memcached libevent)
-
PHP的session(会话管理)一般是以文件形式进行,而在多个Web服务器之间进行session管理时memecached会比文件管理方式更加方便。在这里介绍如何使用memcached管理PHP的s...
- php传值和传引用的区别(php 传值和传引用)
-
php传值:在函数范围内,改变变量值得大小,都不会影响到函数外边的变量值。PHP传引用:在函数范围内,对值的任何改变,在函数外部也有所体现,因为传引用传的是内存地址。传值:和copy是一样的。【打个比...
- PHP 常量详解教程(php常量和变量)
-
常量类似变量,但是常量一旦被定义就无法更改或撤销定义。PHP常量常量是单个值的标识符(名称)。在脚本中无法改变该值。有效的常量名以字符或下划线开头(常量名称前面没有$符号)。注释:与变量不同,常...
- php自学零基础入门小知识(php新手入门教程)
-
我们就把PHP入门当成一个苹果吧!一口一口的吃掉他!不啰嗦了!开始了1、嵌入方法:类似ASP的<%,PHP可以是<?php或者是<?,结束符号是?>,当然您也可以自己指定。2、...
- PHP 语法详解(php语法大全)
-
PHP脚本在服务器上执行,然后向浏览器发送回纯HTML结果。基础PHP语法PHP脚本可放置于文档中的任何位置。PHP脚本以<?php开头,以?>结尾:<?php...
- PHP笔记(一)PHP基础知识(php必背知识点)
-
创建PHP程序PHP代码框架<?php>2.文件命名规则...
- PHP 8新特性之Attributes(注解),你掌握了吗?
-
PHP8的Alpha版本,过几天就要发布了,其中包含了不少的新特性,当然我自己认为最重要的还是JIT,这个我从2013年开始参与,中间挫折无数,失败无数后,终于要发布的东东。不过,今天呢,我不打算谈J...
- PHP基本语法之标记与注释(php注释规范)
-
1、标记由于PHP是嵌入式脚本语言,它在实际开发中经常会与HTML内容混在一起,所以为了区分HTML与PHP代码,需要使用标记对PHP代码进行标识。如:<html>...
- php注解(PHP注解 性能)
-
目标了解和使用php注解,如果你已经掌握其他一种具有注解的语言,例如:java、python等,你在本文中只需要了解点语法就行。示例php8以前的版本,注解写在注释里,如果你掌握其他语言的注解,你是不...
- 数据丢失?别慌!MySQL备份恢复攻略
-
想象一下,某个晴朗的午后,你正享受着咖啡,突然接到紧急电话:你的网站或APP彻底挂了!系统崩溃,界面全白。虽然心头一紧,但你或许还能安慰自己:系统崩溃只是暂停服务,数据还在,修复修复就好了。然而,如果...
- MySQL 日志:undo log、redo log、binlog
-
今天来和大家分享MySQL的三个日志文件,可以说MySQL的多数特性都是围绕日志文件实现,而其中最重要的有以下三种:...
- MySQL三大日志:binlog、redolog、undolog全解析
-
binlog概述在MySQL数据库中,binlog可是个相当重要的存在,它的全称为binarylog,也就是二进制日志。它就像是数据库的“记忆本”,记录了所有的DDL(数据定义语言)和...
- 1、MySQL数据库介绍(mysql数据库简单介绍)
-
1.1数据库的核心定义数据库的本质数据库乃存储数据对象之容器,涵盖如下关键组件:表(Table)...
- MySQL 日志双雄:实时监控与历史归档实战优化
-
MySQL日志双雄:实时监控+历史归档实战用这招让你家日志系统再也不卡不爆炸MySQL十亿级日志处理:从洪峰到归档全攻略手把手教你用MySQL搞定ELK级日志监控在微服务架构大行其道的今天,日志系统早...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)