June 2007


抽空编译了这个 windows 版(附件已更新为 1.0.0 版链接)。这个版本主要是根据 hightman 大牛的 xNix 代码 移植而来。主要改动集中在处理了一些 GCC 与 VC 的兼容性代码,Lock 部分借鉴了 PHP 在跨平台方面的一些成果,去掉了 MMAP 支持,改为普通文件操作。

这个是 SCWS 扩展 PHP 4.4.x 版二进制文件

这个是 SCWS 扩展 PHP 5.2.x 版二进制文件

这个是 SCWS 扩展源码及 VC6 工程文件

这里是 SCWS – 简易中文分词系统简介

源码分析,首先得看源码。记事本可以看,EditPlus 也可以看,但显然具有语法着色功能的 EditPlus 要比记事本爽。更显然,M$ 的 Visual C++ 比 EditPlus 还要爽。:D 因此我推荐的第一款神兵就是 M$ Visual C++(Sorry, Linuxer~)。既然用了 Visual C++ 那么有一款它的插件我们不得不提:Visual Assist X,还没有她的朋友尽快拥有她吧~

OK,现在源码是可以看了,而且是很舒服地在看,但有时我们还想实地进行单步调试以一窥究竟。要调试自然得先编译。很不幸,PHP 在 Windows 环境下的编译和调试并不像想象中的那么简单。我们至少得比在 xNix 环境下多做一下几个步骤:

  1. 下载 PHP 使用的 DNS 解析器的源代码(http://www.php.net/extra/bindlib_w32.zip)并将其编译输出一个名为 resolv.lib 的库文件。
  2. 下载 win32 编译工具 http://www.php.net/extra/win32build.zip ,解压至某一目录(假定为 $work)。在 $work\lib 目录里面也有个 resolv.lib,我们把步骤 1 产生的 resolv.lib 复制到这个目录,覆盖之。
  3. 设置编译环境。把 $Work\bin 添加到系统 PATH 环境变量和 Visual C++ 的 Executable files 目录;把 $work\lib 添加到 Library files 目录;把 $Work\include 添加到 Include files 目录。
  4. 下载 PHP 源文件并解压至 $work 。
  5. 整理 PHP 的项目工程文件。PHP 4.x 的 VC 工程文件($PHP\win32\php4ts*.dsw)是当时一直在维护的,因此可以直接拿来就用。PHP 5.x 增加了一种新的基于命令行的类 Unix 编译系统,使得编译 PHP 更为简单。于是相应的工程文件 php5ts*.dsw 便不再被 PHP 团队维护,也不能直接使用编译 PHP 5 了。但我们恰恰不想单纯的编译,我们想的是在 Visual C++ 里面进行编译然后下断点单步调试,因此我们就需要命令行编译系统的编译流程重建这个项目工程文件(可能还需要对某些文件做些必要的改动)。

为方便大家不做重复性劳动,我把我现在用的 PHP 调试环境打包发上来,大家下载后直接解压到某一目录即可。所需另外做的也只是根据你解压的目录设置一下编译的环境变量(见步骤 3),之后就可直接进入 $work\win32 打开 php5ts.dsw。

这里是该 PHP win32 VC 编译项目包。

工程文件说明:这其实是个“精简版”PHP 的工程文件,里面的 PHP 是 php 5.2.8 的源码,但只包含 date、filter、pcre、reflection、session、standard、tokenizer 这几个内建扩展。另外 php5apache2 的 sapi 不能编译,要想编译它得再去下一份 Apache 2 的源码。除此之外还删除了大部分的测试脚本。

最后再说一下分析 PHP 源代码所需要一些基础知识。首先当然得有一定的 C 语言基础。在最后分析 Zend Engine 时可能还需要一些编译器方面知识,最好能看懂一些 lex&yacc 的语法文件(PHP 采用的是 flex 和 bison,但在语法层面相差无几)。最后是能有一定的调试技术。包括在 VC++ 环境下的有源码调试技术和无源码的二进制代码调试技术(这种情况下我一般用OllyDbg)。其中 C 语言基础是必须的,而后面的两种知识/技术则可有可无,但有的话可以达到事半功倍之效。

目前在 PHP 社区尤其是国内的 PHP 社区对 PHP 内核这方面讨论的比较少。我平时常看一些 PHP 源码,对 PHP 的运行机制算是有一些认识吧,因此我打算写一些这方面的文章,算是抛砖引玉。最终目标我希望可以做成一个关于 PHP 内核的中文百科全书。应该说这是一个不算太小的工程,依我个人之力几乎不可能完成。更何况老子曾经曰过:“知者不言,言者不知”,相信潜在水下面的大牛(水牛?^_^)还有 N 多。所以希望大家群策群力,共同完成这个项目。

整个项目我初步打算以 PHP 5.2.x 的源码为研究对象,侧重于对 Zend Engine 的表述,兼顾一些 SAPI 层。下面是我列出的一份清单,大家看看还有没有什么遗漏或者内容编排不合理之处。请诸位畅所欲言,有啥说啥,即使跑题也没关系~ 😀

    PHP 源代码分析 V0.0.1

    第一章 构建系统

  1. 准备工具、库及需要具备的基础知识;
  2. 如何编译不同平台的版本?编译时的各个选项是什么含义?源码的目录结构。
  3. 如何创建一个 PHP 扩展/模块?如何创建一个 Zend 扩展?
  4. 如何调试 PHP?如何调试 PHP/Zend 扩展?
    第二章 PHP 与 SAPI 的生命周期

  1. 脚本的运行周期,一切从 main() 开始;
  2. 模块/脚本的起始与终止函数;
  3. PHP SAPI 协议;
  4. 嵌入式 PHP 设计。
    第三章 内存管理

  1. Zend 的内存管理器框架;
  2. 内存申请与释放流程,垃圾回收;
  3. 持久化(persistence)
    第四章 线程安全

  1. 为何会有这个问题?Zend Engine 是如何解决的?
  2. 我是否该启用 ZTS?各有什么优缺点?
  3. 如何构建一个 ZTS 的程序/扩展?
    第五章 变量与常量

  1. PHP 中的数据类型。
  2. 变量、常量与静态变量。
  3. 引用计数机制;
  4. 资源的创建与回收;
  5. 未来字符串的 UNICODE 支持、JIT 支持。
    第六章 函数

  1. 函数的内部布局;
  2. 函数的定义;
  3. 如何获取函数的参数、可选参数、参数默认值;
  4. 函数的返回值;
    第七章 类与对象

  1. 类的内部布局(属性、方法);
  2. 构造函数与析构函数;
  3. 类的继承与转换(up casting 与 down casting);
  4. 接口(轻量级的类),微观上与类的差别;
  5. 类之间的 up casting 和 down casting。
  6. stdClass。
    第八章 错误与异常

  1. 什么是错误、什么是异常。两者的区别;
  2. 如何创建和抛出异常;
  3. try/catch 的设计与实现;
    第九章 流(Streams)支持

  1. 这方面我接触较少,内容待定;
    第十章 虚拟机

  1. 脚本编译机制(词法分析、语法分析);
  2. 脚本的执行机制(CALL|SWITCH|GOTO);
  3. 各个符号表的作用;
  4. 开发 OPCode 缓存器;
  5. 开发 PHP 调试器;
    附录

  1. 完整的 PHP API、Zend API 以及宏(Micro)参考(长期工程)
  2. Zend Engine 1 的主要特性,与 Zend Engine 2的主要差别;
  3. Zend Engine 3 的主要特性,与 Zend Engine 2的主要差别;
  4. 相关资源
  • 项目发起:Ben (ben.yan at msn dot com
  • 项目参与:Ben (ben.yan at msn dot com,http://www.yAnbiN.org
  • 项目启动:2007/06/09 (希望可以在明年奥运会开幕前完成 :D)
  • 项目进度:
    1. 2007/06/09 项目启动,讨论项目规划;
    2. 2007/xx/xx 待续……

以前每当一个 Zend Studio 的新版本发布时都会同时发布一个新版的 Zend Studio Server 组件,这个组件可以让我们很方便地进行远程调试。但是自从 Zend 发布了 Zend Platform 以后他们就不再更新 Zend Studio Server 组件了。这就导致我们只能远程调试 PHP 5.1.x 的环境,而不能调试 PHP 5.2.x。要想调试 PHP 5.2.x 只能装一个庞大的 Zend Platform。:(

因此我一直在找一个“轻量级”的解决方案。近日在逛 Zend.com 时发现了一个好东西:Zend Studio Web Debugger,直觉告诉我,这就是我想要的。果不其然,今天试验成功!

下面就说一说我的试验步骤:

  1. 到这里下载 Zend Studio Web Debugger,然后将其解压到某一目录,比如:C:\Program Files\Zend,这就会在该目录里面新建一个 ZendDebugger-5.2.14-Windows-i386 子目录,里面有 4_3_x_comp、4_4_x_comp、5_2_x_comp 等目录,将这些 x_y_z_comp 分别改为 php-x.y.z(比如将目录 5_2_x_comp 改为 php-5.2.x);
  2. 确保已经加载了 Zend Extension Manager,如果安装了 Zend Optimizer 则会自动安装 Zend Extension Manager,若没有安装请先安装 Zend Optimizer 。或者你可以把 Zend Optimizer 中 Zend Extension Manager.dll 给提取出来,然后手工在 php.ini 中添加一行:
    zend_extension_ts="C:\Program Files\Zend\ZendOptimizer\ZendExtensionManager.dll"
    其中 ZendExtensionManager.dll 的位置请根据你的实际情况填写;
  3. 在 Web Server 的 php.ini 添加下面几行:
    zend_extension_manager.debug_server_ts="C:\Program Files\Zend\ZendDebugger-5.2.14-Windows-i386"
    zend_debugger.expose_remotely=allowed_hosts
    zend_debugger.allow_hosts=127.0.0.1/32,192.168.1.0/16
    zend_debugger.allow_tunnel=127.0.0.1/32

    zend_extension_manager.debug_server_ts 的值请根据你的实际情况填写,就是 php-x.y.z 的父目录。
  4. 把 ZendDebugger-5.2.14-Windows-i386 目录下的 dummy.php 复制到你的 Web 站点根目录。
  5. 重启你的 Web Server,OK!

简单总结一下:Zend Studio 的远程调试功能是由 Zend Studio Server 组件(ZendDebuger.dll)提供的。本质上这是一个 Zend 扩展,因此你只要能把这个 Zend 扩展启用就可以了。只是 Zend 公司出品的 Zend 扩展只能由那个 Zend Extension Manager 负责加载,所以我们才需要做一些额外的步骤,否则只需简单地加一行 zend_extension_ts = xxxxxx 而已。

Zend Extension Manager 是一个 Zend 公司用于统一管理该公司出品的各种 Zend 扩展的 Zend 扩展。一般来说 Zend 扩展都是高度依赖 Zend Engine 版本的,但是 Zend Extension Manager 却可以不依赖任何具体版本的 PHP 运行库,并且会根据不同的运行环境自动加载不同产品相应版本的 Zend 扩展。相信通过学习 Zend Extension Manager 的实现会对我们统一开发部署 Zend 扩展提供一些帮助。

附件就是 Zend Extension Manager v1.2.0 版本的源代码和 VC++ 工程文件。源代码是根据 ZendOptimizer-3.2.8-Windows-i386 中的 ZendExtensionManager.dll 逆向出来的。编译出来的 ZendExtensionManager.dll 可完全替代原始文件(事实上也没有任何区别)。

点击下载 Zend Extension Manager v1.2.0 源代码

'