标签归档:TIPI

TIPI0102–PHP源码结构、阅读代码方法

第二节 PHP源码结构、阅读代码方法

PHP源码目录结构:


俗话讲:大巧不工。PHP的源码在结构上非常清晰甚至简单。下面,先简单介绍一下PHP源码的目录结构。

  • build: 源码编译相关文件,包括buildconft等sh文件,还有一些awk的脚本。
  • ext 官方扩展目录,包括了绝大多数PHP的函数的定义和实现,如array系列,pdo系列,spl系列等函数的实现,都在此处相对的子目录中。
  • main PHP宏定义与实现,在需要扩展PHP时,经常要使用的PHP_*系列宏就在这里定义。
  • Zend 包含Zend引擎文件,Zend API宏的定义和实现。
  • pear “PHP 扩展与应用仓库”, 包含PEAR的核心文件。
  • sapi 包含了各种服务器抽象层的代码,以目录区分。
  • TSRM “线程安全资源管理器” (TSRM) 目录。
  • tests 测试脚本目录。
  • win32 Windows 下编译PHP相关的脚本。

PHP源码阅读方法:


使用VIM + Ctags查看并追踪源码:

VIM是一个非常给力的编辑器,在纯命令终端下,它几乎是无可替代的(Emacs?)。
ctags可以将源代码中的各种函数、宏等信息做上标记。这样,使用VIM就可以很方便的查看源码。
简洁使用说明:

#在PHP源码目录(假定为/server/php-src)执行:
$ cd /server/php-src
$ ctags -R

#在~/.vimrc中添加:
set tags+=/server/php-src/tags

再用vim打开各种php源码文件时,将光标移到想查看的函数、宏、变量上面, 使用 Ctrl+p 就可以自动跳转至定义,Ctrl+o 可以返回上一次查看位置。

使用Visual Studio + editplus查看并追踪源码:

看源码还是用IDE舒服一些,windows下我们还是用Visual Studio 2010看吧。 在win32目录下已经存在了可以直接打开的工程文件,如果由于版本原因无法打开,可以在此源码目录上新建一个基于现有文件的Win32 Console Application工程。
常用快捷键

F12 转到定义
CTRL + F12转到声明

F3: 查找下一个
Shift+F3: 查找上一个

Ctrl+G: 转到指定行

CTRL + -向后定位
CTRL + SHIFT + -向前定位

对于一些搜索类的操作,可以考虑使用editplus或其它文本编辑工具进行,这样的搜索速度相对来说会快一些。
如果使用editplus进行搜索,一般是选择 【搜索】 中的 【在文件中查找…】

作者:TIPI Team

TIPI0103–PHP实现中的常用代码

第三节 PHP实现中的常用代码

在PHP的源码中经常会看到一些宏或一些对于刚开始看源码的童鞋比较纠结的代码。这里提取中间的一些进行说明。

1. 关于##和#


在PHP的宏定义中,最常见的要数双井号。

双井号

在C语言的宏中,”##”被称为 连接符(concatenator),用来把两个语言符号(Token)组合成单个语言符号。这里的语言符号不一定是宏的变量。并且双井号不能作为第一个或最后一个元素存在。如下所示源码:

#define PHP_FUNCTION            ZEND_FUNCTION
#define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_FN(name) zif_##name
#define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, \
zval *this_ptr, int return_value_used TSRMLS_DC

PHP_FUNCTION(count);

//  预处理器处理以后, PHP_FUCNTION(count);就展开为如下代码
void zif_count(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC)

宏ZEND_FN(name)中有一个”##”,它的作用一如之前所说,是一个连接符,将zif和宏的变量name得值连接起来。

单井号

“#”的功能是将其后面的宏参数进行 字符串化操作 ,简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号,用比较官方的话说就是将语言符号(Token)转化为字符串。

2. 关于宏定义中的do-while


如下所示为PHP5.3新增加的垃圾收集机制中的一段代码:

#define ALLOC_ZVAL(z)                                   \
do {                                                \
    (z) = (zval*)emalloc(sizeof(zval_gc_info));     \
    GC_ZVAL_INIT(z);                                \
} while (0)

如上所示的代码,在宏定义中使用了 do{ }while(0) 语句格式。如果我们搜索整个PHP的源码目录,会发现这样的语句还有很多。那为什么在宏定义时需要使用do-while语句呢? 我们知道do-while循环语句是先执行再判断条件是否成立, 所以说至少会执行一次。当使用do{ }while(0)时代码肯定只执行一次, 肯定只执行一次的代码为什么要放在do-while语句里呢? 这种方式适用于宏定义中存在多语句的情况。如下所示代码:

#define TEST(a, b)  a++;b++;

if (expr)
    TEST(a, b);
else
    do_else();

代码进行预处理后,会变成:

if (expr)
    a++;b++;
else
    do_else();

这样if-else的结构就被破坏了: if后面有两个语句, 这样是无法编译通过的, 那为什么非要do-while而不是简单的用{}括起来呢.这样也能保证if后面只有一个语句。 例如上面的例子,在调用宏TEST的时候后面加了一个分号, 虽然这个分号可有可无, 但是出于习惯我们一般都会写上. 那如果是把宏里的代码用{}括起来,加上最后的那个分号. 还是不能通过编译. 所以一般的多表达式宏定义中都采用do-while(0)的方式.

3. #line 预处理


#line 838 "Zend/zend_language_scanner.c"

#line预处理用于改变当前的行号和文件名。
如上所示代码,将当前的行号改变为838,文件名Zend/zend_language_scanner.c
它的作用体现在编译器的编写中,我们知道编译器对C 源码编译过程中会产生一些中间文件,通过这条指令,可以保证文件名是固定的,不会被这些中间文件代替,有利于进行调试分析。

作者:TIPI Team

TIPI0101-环境搭建

第一节 环境搭建

在开始学习PHP实现之前, 我们首先需要一个实验和学习的环境. 下面介绍一下怎样在*nix环境下准备和搭建PHP环境. (*nix指的是类Unix环境,比如各种Linux发行版,FreeBSD, OpenSolaris, Mac OS X等操作系统)

1.获取PHP源码

为了学习PHP的实现,首先我们要下载源代码.下载源码首选是去PHP官方网站 http://php.net/downloads.php下载, 如果你喜欢是用类似svn/git这些版本控制软件,喜欢svn的读者可以去http://www.php.net/svn.php上 签出源代码,或者如果你喜欢用git, 则可以去http://github.com/php/php-src上clone一个. 个人比较喜欢用版本控制软件签出代码, 这样的好处是能看到php每次修改的内容及日志信息, 如果自己修改了其中的某些内容也能快速的查看到.

2.准备编译环境

在*nix环境下,首先需要gcc编译构建环境. 如果你是用的是Ubuntu或者是用apt做为包管理的系统,可以通过如下命令快速安装.

sudo apt-get install build-essential

如果用的是Mac OS的话,则需要安装Xcode。Xcode可以在Mac OS X的安装盘中找到,如果你有Apple ID的话,也可以登陆苹果开发者网站http://developer.apple.com/下载。

3. 编译

下一步就可以开始编译了,本文只简单介绍基本的编译过程,不包含apache的php支持以及mysql等模块的编译。相关资料请百度或google之。 假设源代码下载到了~/php-src的目录中,执行buildconf命令以生成所需要的Makefile文件

cd ~/php-src
./buildconf

执行完以后就可以开始configure了, configure有很多的参数, 比如指定安装目录, 是否开启相关模块等选项

./configure --help # 查看可用参数

为了尽快得到可以测试的环境,我们就不加其他参数了.直接执行./configure就可以了. 以后如果需要其他功能可以重新编译. 如果configure命令出现错误,可能是缺少php所依赖的库,各个系统的环境可能不一样. 出现错误可根据出错信息上网搜索. 直到完成configure. configure完成后我们就可以开始编译了.

make

在*nix下编译过程序的读者应该都熟悉经典的configure make, make install吧. 执行make之后是否需要make install就取决于你了. 如果install的话 最好在configure的时候是用prefix参数指定安装目录, 不建议安装到系统目录, 避免和系统原有的php冲突.

在make 完以后,~/php-src目录里就已经有了php的可以执行文件. 执行一下命令:

cd ~/php-src
./sapi/cli/php -v

如果看到输出php版本信息则说明咱成功了. 如果是make install的话则执行 $prefix/bin/php 这个路径的php, 当然如果是安装在系统目录或者你的prefix 目录在$PATH环境变量里的话,直接执行php就行了.

后续的学习中可能会需要重复configure make 或者 make && make install 这几个步骤。

作者:TIPI Team