August 2006


我们先来创建一个非常简单的扩展,这个扩展除了一个将其整形参数作为返回值的函数外几乎什么都没有。下面(“例3-2 一个简单的扩展”)就是这个样例的代码:

例3.2 一个简单的扩展


/* include standard header */
#include "php.h"

/* declaration of functions to be exported */
ZEND_FUNCTION(first_module);

/* compiled function list so Zend knows what‘s in this module */
zend_function_entry firstmod_functions[] =
{
ZEND_FE(first_module, NULL)
{NULL, NULL, NULL}
};

/* compiled module information */
zend_module_entry firstmod_module_entry =
{
STANDARD_MODULE_HEADER,
"First Module",
firstmod_functions,
NULL,
NULL,
NULL,
NULL,
NULL,
NO_VERSION_YET,
STANDARD_MODULE_PROPERTIES
};

/* implement standard "stub" routine to introduce ourselves to Zend */
#if COMPILE_DL_FIRST_MODULE
ZEND_GET_MODULE(firstmod)
#endif

/* implement function that is meant to be made available to PHP */
ZEND_FUNCTION(first_module)
{
long parameter;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &parameter) == FAILURE) {
return;
}

RETURN_LONG(parameter);
}

这段代码已经包含了一个完整的 PHP 模块。稍后我们会详细解释这段代码,现在让我们先讨论一下构建过程。(在我们讨论 API 函数前,这可以让心急的人先实验一下。)

模块的编译

模块的编译基本上有两种方法:

1、在 Ext 目录内使用“make” 机制,这种机制也可以编译出动态可加载模块。
2、手动编译源代码。

第一种方法明显受到人们的偏爱。自 PHP 4.0 以来,这也被标准化成了一个的复杂的构建过程。这种复杂性也导致了难于被理解这个缺点。在本章最后我们会更详细的讨论这些内容,但现在还是让我们使用默认的 make 文件吧。

第二种方法很适合那些(因为某种原因而)没有完整 PHP 源码树的或者是很喜欢敲键盘的人。虽然这些情况是比较罕见,但为了内容的完整性我们也会介绍一下这种方法。

使用 make 进行编译

为了能够使用这种标准机制流程来编译这些代码,让我们把它所有的子目录都复制到 PHP 源码树的 Ext 目录下。然后运行 buildconf 命令,这将会创建一个新的包含了与我们的扩展相对应的选项的 configure 脚本。默认情况下,样例中的所有代码都是未激活的,因此你不用担心会破坏你的构建程序。在 buildconf 执行完毕后,再使用 configure –help 命令就会显示出下面的附加模块:

--enable-array_experiments BOOK: Enables array experiments
--enable-call_userland BOOK: Enables userland module
--enable-cross_conversion BOOK: Enables cross-conversion module
--enable-first_module BOOK: Enables first module
--enable-infoprint BOOK: Enables infoprint module
--enable-reference_test BOOK: Enables reference test module
--enable-resource_test BOOK: Enables resource test module
--enable-variable_creation BOOK: Enables variable-creation module

前面样例(“例3-2 一个简单的扩展”)中的模块(first_module)可以使用 –enable-first_module 或 –enable-first_module=yes 来激活。

手动编译

手动编译需要运行以下命令:

动作 命令
编译 cc -fpic -DCOMPILE_DL_FIRST_MODULE=1 -I/usr/local/include -I. -I.. -I../Zend -c -o <your_object_file> <your_c_file>
连接 cc -shared -L/usr/local/lib -rdynamic -o <your_module_file> <your_object_file(s)>

编译命令只是简单的让编译器产生一些中间代码(不要忽略了-fpic 参数),然后又定义了COMPILE_DL 常量来通知代码这是要编译为一个动态可加载的模块(通常用来测试,我们稍后会讨论它)。这些选项后面是一些编译这些源代码所必须包含的库文件目录。

注意:本例中所有 include 的路径都是都是 Ext 目录的相对路径。如果您是在其他目录编译的这些源文件,那么还要相应的修改路径名。编译所需要的目录有 PHP 目录,Zend 目录和模块所在的目录(如果有必要的话)。

连接命令也是一个非常简单的把模块连接成一个动态模块的命令。

你可以在编译指令中加入优化选项,尽管这些已经在样例中忽略了(不过你还是可以从前面讨论的 make 模版文件中发现一些)。

注意,手动将模块静态编译和连接到 PHP 二进制代码的指令很长很长,因此我们在这里不作讨论。(手动输入那些指令是很低效的。)

PHP 提供了一套非常灵活的自动构建系统(automatic build system),它把所有的模块均放在 Ext 子目录下。每个模块除自身的源代码外,还都有一个用来配置该扩展的 config.m4 文件(详情请参见http://www.gnu.org/software/m4/manual/m4.html )。

包括 .cvsignore 在内的所有文件都是由位于 Ext 目录下的 ext_skel 脚本自动生成的,它的参数就是你想创建模块的名称。这个脚本会创建一个与模块名相同的目录,里面包含了与该模块对应的一些的文件。

下面是操作步骤:

:~/cvs/php4/ext:> ./ext_skel --extname=my_module
Creating directory my_module
Creating basic files: config.m4 .cvsignore my_module.c php_my_module.h CREDITS EXPERIMENTAL tests/001.phpt my_module.php [done].

To use your new extension, you will have to execute the following steps:
1. $ cd ..
2. $ vi ext/my_module/config.m4
3. $ ./buildconf
4. $ ./configure --[with|enable]-my_module
5. $ make
6. $ ./php -f ext/my_module/my_module.php
7. $ vi ext/my_module/my_module.c
8. $ make

Repeat steps 3-6 until you are satisfied with ext/my_module/config.m4 and step 6 confirms that your module is compiled into PHP. Then, start writing code and repeat the last two steps as often as necessary.

这些指令就会生成前面所说的那些文件。为了能够在自动配置文件和构建程序中包含新增加的模块,你还需要再运行一次 buildconf 命令。这个命令会通过搜索 Ext 目录和查找所有 config.m4 文件来重新生成 configure 脚本。默认情况下的的 config.m4 文件如例 3-1 所示,看起来可能会稍嫌复杂:

例3.1 默认的 config.m4 文件


dnl $Id: build.xml,v 1.1 2005/08/21 16:27:06 goba Exp $
dnl config.m4 for extension my_module
dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.

dnl If your extension references something external, use with:

dnl PHP_ARG_WITH(my_module, for my_module support, dnl Make sure that the comment is aligned:
dnl [ --with-my_module Include my_module support])

dnl Otherwise use enable:

dnl PHP_ARG_ENABLE(my_module, whether to enable my_module support,
dnl Make sure that the comment is aligned:
dnl [ --enable-my_module Enable my_module support])

if test $PHP_MY_MODULE != “no”; then
dnl Write more examples of tests here...

dnl # --with-my_module -> check with-path
dnl SEARCH_PATH = /usr/local /usr # you might want to change this
dnl SEARCH_FOR=/include/my_module.h you most likely want to change this
dnl if test -r $PHP_MY_MODULE/; then # path given as parameter
dnl MY_MODULE_DIR=$PHP_MY_MODULE
dnl else # search default path list
dnl AC_MSG_CHECKING([for my_module files in default path])
dnl for i in $SEARCH_PATH ; do
dnl if test -r $i/$SEARCH_FOR; then
dnl MY_MODULE_DIR=$i
AC_MSG_RESULT(found in $i)
dnl fi
dnl done
dnl fi
dnl
dnl if test -z "$MY_MODULE_DIR"; then
dnl AC_MSG_RESULT([not found])
dnl AC_MSG_ERROR([Please reinstall the my_module distribution])
dnl fi

dnl # --with-my_module -> add include path
dnl PHP_ADD_INCLUDE($MY_MODULE_DIR/include)

dnl # --with-my_module -> chech for lib and symbol presence
dnl LIBNAME=my_module # you may want to change this
dnl LIBSYMBOL=my_module # you most likely want to change this
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
dnl [ dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $MY_MODULE_DIR/lib, MY_MODULE_SHARED_LIBADD)
dnl AC_DEFINE(HAVE_MY_MODULELIB,1,[ ])
dnl ],[
dnl AC_MSG_ERROR([wrong my_module lib version or lib not found])
dnl ],[
dnl -L$MY_MODULE_DIR/lib -lm -ldl
dnl ])
dnl
dnl PHP_SUBST(MY_MODULE_SHARED_LIBADD)

PHP_NEW_EXTENSION(my_module, my_module.c, $ext_shared)
fi

如果你不太熟悉 M4 文件(现在毫无疑问是熟悉 M4 文件的大好时机),那么就可能会有点糊涂。但是别担心,其实非常简单。

注意:凡是带有 dnl 前缀的都是注释,注释是不被解析的。

config.m4 文件负责在配置时解析 configure 的命令行选项。这就是说它将检查所需的外部文件并且要做一些类似配置与安装的任务。

默认的配置文件将会在 configure 脚本中产生两个配置指令:–with-my_module 和 –enable-my_module。当需要引用外部文件时使用第一个选项(就像用 –with-apache 指令来引用 Apache 的目录一样)。第二个选项可以让用户简单的决定是否要启用该扩展。不管你使用哪一个指令,你都应该注释掉另外一个。也就是说,如果你使用了–enable-my_module,那就应该去掉–with-my_module。反之亦然。

默认情况下,通过 ext_skel 创建的 config.m4 都能接受指令,并且会自动启用该扩展。启用该扩展是通过 PHP_EXTENSION 这个宏进行的。如果你要改变一下默认的情况,想让用户明确的使用 –enable-my_module 或 –with-my_module 指令来把扩展包含在 PHP 二进制文件当中,那么将 “if test "$PHP_MY_MODULE" != “no””改为“if test "$PHP_MY_MODULE" == "yes"”即可。

if test "$PHP_MY_MODULE" == "yes"; then dnl
Action.. PHP_EXTENSION(my_module, $ext_shared)
fi

这样就会导致在每次重新配置和编译 PHP 时都要求用户使用 –enable-my_module 指令。

另外请注意在修改 config.m4 文件后需要重新运行 buildconf 命令。

在我们开始讨论具体编码这个话题前,你应该让自己熟悉一下 PHP 的源代码树以便可以迅速地对各个源文件进行定位。这也是编写和调试 PHP 扩展所必须具备的一种能力。

下表列出了一些主要目录的内容:

目录 内容
php-src 包含了PHP主源文件和主头文件;在这里你可以找到所有的 PHP API 定义、宏等内容。(重要). 其他的一些东西你也可以在这里找到。
php-src/ext 这里是存放动态和内建模块的仓库;默认情况下,这些就是被集成于主源码树中的“官方” PHP 模块。自 PHP 4.0开始,这些PHP标准扩展都可以编译为动态可载入的模块。(至少这些是可以的)。
php-src/main 这个目录包含主要的 PHP 宏和定义。 (重要)
php-src/pear 这个目录就是“PHP 扩展与应用仓库”的目录。包含了PEAR 的核心文件。
php-src/sapi 包含了不同服务器抽象层的代码。
TSRM Zend 和 PHP的 “线程安全资源管理器” (TSRM) 目录。
ZendEngine2 包含了Zend 引擎文件;在这里你可以找到所有的 Zend API 定义与宏等。(重要)

当然,讨论 PHP 包里面全部每一个文件无疑是超出了本章的范围,但你还是应该仔细看一下下面的几个文件

  • php-src/main/php.h, 位于PHP 主目录。这个文件包含了绝大部分 PHP 宏及 API 定义。
  • php-src/Zend/zend.h, 位于 Zend 主目录。这个文件包含了绝大部分 Zend 宏及 API 定义。
  • php-src/Zend/zend_API.h, 也位于 Zend 主目录,包含了Zend API 的定义。

除此之外,你也应该注意一下这些文件所包含的一些文件。举例来说,哪些文件与 Zend 执行器有关,哪些文件又为 PHP 初始化工作提供了支持等等。在阅读完这些文件之后,你还可以花点时间再围绕PHP包来看一些文件,了解一下这些文件和模块之间的依赖性――它们之间是如何依赖于别的文件又是如何为其他文件提供支持的。同时这也可以帮助你适应一下 PHP 创作者们代码的风格。要想扩展 PHP,你应该尽快适应这种风格。

扩展规范

Zend 是用一些特定的规范构建的。为了避免破坏这些规范,你应该遵循以下的几个规则:

几乎对于每一项重要的任务,Zend 都预先提供了极为方便的宏。在下面章节的图表里将会描述到大部分基本函数、结构和宏。这些宏定义大多可以在 Zend.h 和 Zend_API.h 中找到。我们建议您在学习完本节之后仔细看一下这些文件。(当然你也可以现在就阅读这些文件,但你可能不会留下太多的印象。)

内存管理

资源管理仍然是一个极为关键的问题,尤其是对服务器软件而言。资源里最具宝贵的则非内存莫属了,内存管理也必须极端小心。内存管理在 Zend 中已经被部分抽象,而且你也应该坚持使用这些抽象,原因显而易见:由于得以抽象,Zend 就可以完全控制内存的分配。Zend 可以确定一块内存是否在使用,也可以自动释放未使用和失去引用的内存块,因此就可以避免内存泄漏。下表列出了一些常用函数:

函数 描述
emalloc() 用于替代 malloc()
efree() 用于替代 free()
estrdup() 用于替代 strdup()
estrndup() 用于替代strndup()。速度要快于 estrdup() 而且是二进制安全的。如果你在复制之前预先知道这个字符串的长度那就推荐你使用这个函数。
ecalloc() 用于替代 calloc()
erealloc() 用于替代 realloc()

emalloc(), estrdup(), estrndup(), ecalloc(), 和 erealloc() 用于申请内部的内存,efree() 则用来释放这些前面这些函数申请的内存。e*() 函数所用到的内存仅对当前本地的处理请求有效,并且会在脚本执行完毕,处理请求终止时被释放。

Zend 还有一个线程安全资源管理器,这可以为多线程WEB 服务器提供更好的本地支持。不过这需要你为所有的全局变量申请一个局部结构来支持并发线程。但是因为在写本章内容时Zend 的线程安全模式仍未完成,因此我们无法过多地涉及这个话题。

目录与文件函数

下列目录与文件函数应该在 Zend 模块内使用。它们的表现和对应的 C 语言版本完全一致,只是在线程级提供了虚拟目录的支持。

Zend 函数 对应的 C 函数
V_GETCWD() getcwd()
V_FOPEN() fopen()
V_OPEN() open()
V_CHDIR() chdir()
V_GETWD() getwd()
V_CHDIR_FILE() 将当前的工作目录切换到一个以文件名为参数的该文件所在的目录。
V_STAT() stat()
V_LSTAT() lstat()

字符串处理

在 Zend 引擎中,与处理诸如整数、布尔值等这些无需为其保存的值而额外申请内存的简单类型不同,如果你想从一个函数返回一个字符串,或往符号表新建一个字符串变量,或做其他类似的事情,那你就必须确认是否已经使用上面的 e*() 等函数为这些字符串申请内存。(你可能对此没有多大的感觉。无所谓,现在你只需在脑子里有点印象即可,我们稍后就会再次回到这个话题)

复杂类型

像数组和对象等这些复杂类型需要另外不同的处理。它们被出存在哈希表中,Zend 提供了一些简单的 API 来操作这些类型。

正如上图(图3-1 PHP 内部结构图)所示,PHP 主要以三种方式来进行扩展:外部模块内建模块Zend 引擎。下面我们将分别讨论这些方式:

外部模块

外部模块可以在脚本运行时使用 dl() 函数载入。这个函数从磁盘载入一个共享对象并将它的功能与调用该函数的脚本进行绑定并使之生效。脚本终止后,这个外部模块将在内存中被丢弃。这种方式有利有弊,如下表所示:

优点 缺点
外部模块不需要重新对 PHP 进行编译。 共享对象在每次脚本调用时都需要对其进行加载,速度较慢。
PHP通过“外包”方式来让自身的体积保持很小。 附加的外部模块文件会让磁盘变得比较散乱。
  每个想使用该模块功能的脚本都必须使用 dl() 函数手动加载,或者在 php.ini 文件当中添加一些扩展标签(这并不总是一个恰当的解决方案)。

综上所述,外部模块非常适合开发第三方产品,较少使用的附加的小功能或者仅仅是调试等这些用途。为了迅速开发一些附加功能,外部模块是最佳方式。但对于一些经常使用的、实现较大的,代码较为复杂的应用,那就有些得不偿失了。

第三方可能会考虑在 php.ini 文件中使用扩展标签来创建一个新的外部模块。这些外部模块完全同主PHP 包分离,这一点非常适合应用于一些商业环境。商业性的发行商可以仅发送这些外部模块而不必再额外创建那些并不允许绑定这些商业模块的PHP 二进制代码。

内建模块

内建模块被直接编译进 PHP 并存在于每一个 PHP 处理请求当中。它们的功能在脚本开始运行时立即生效。和外部模块一样,内建模块也有一下利弊:

优点 缺点
无需专门手动载入,功能即时生效。 修改内建模块时需要重新编译PHP。
无需额外的磁盘文件,所有功能均内置在 PHP 二进制代码当中。 PHP 二进制文件会变大并且会消耗更多的内存。

Zend 引擎

当然,你也能直接在 Zend 引擎里面进行扩展。如果你需要在语言特性方面做些改动或者是需要在语言核心内置一些特别的功能,那么这就是一种很好的方式。但一般情况下应该尽力避免对 Zend 引擎的修改。这里面的改动会导致和其他代码的不兼容,而且几乎没有人会适应打过特殊补丁的 Zend 引擎。况且这些改动与主 PHP 源代码是不可分割的,因此就有可能在下一次的官方的源代码更新中被覆盖掉。因此,这种方式通常被认为是“不良的习惯”。由于使用极其稀少,本章将不再对此进行赘述。

“扩展 PHP”说起来容易做起来难。PHP 现在已经发展成了一个具有数兆字节源代码的非常成熟的系统。要想深入这样的一个系统,有很多东西需要学习和考虑。在写这一章节的时候,我们最终决定采用“边学边做”的方式。这也许并不是最科学和专业的方式,但却应该是最有趣和最有效的一种方式。在下面的小节里,你首先会非常快速的学习到如何写一个虽然很基础但却能立即运行的扩展,然后将会学习到有关 Zend API 的高级功能。另外一个选择就是将其作为一个整体,一次性的讲述所有的这些操作、设计、技巧和诀窍等,并且可以让我们在实际动手前就可以得到一副完整的愿景。这看起来似乎是一个更好的方法,也没有死角,但它却枯燥无味、费时费力,很容易让人感到气馁。这就是我们为什么要采用非常直接的讲法的原因。

注意,尽管这一章会尽可能多讲述一些关于 PHP 内部工作机制的知识,但要想真的给出一份在任何时间任何情况下的PHP 扩展指南,那简直是不可能的。PHP 是如此庞大和复杂,以致于只有你亲自动手实践一下才有可能真正理解它的内部工作机制,因此我们强烈推荐你随时参考它的源代码来进行工作。

Zend 是什么? PHP 又是什么?

Zend 指的是语言引擎,PHP 指的是我们从外面看到的一套完整的系统。这听起来有点糊涂,但其实并不复杂(见图3-1 PHP 内部结构图)。为了实现一个 WEB 脚本的解释器,你需要完成以下三个部分的工作:

  1. 解释器部分:负责对输入代码的分析、翻译和执行;
  2. 功能性部分:负责具体实现语言的各种功能(比如它的函数等等);
  3. 接口部分:负责同 WEB 服务器的会话等功能。

Zend 包括了第一部分的全部和第二部分的局部,PHP 包括了第二部分的局部和第三部分的全部。他们合起来称之为 PHP 包。Zend 构成了语言的核心,同时也包含了一些最基本的 PHP 预定义函数的实现。PHP 则包含了所有创造出语言本身各种显著特性的模块。

PHP 内部结构图

图3-1   PHP 内部结构图

下面将要讨论PHP 允许在哪里扩展以及如何扩展。

摘要

知者不言,言者不知。

――老子《道德经》五十六章

有时候,单纯依靠 PHP “本身”是不行的。尽管普通用户很少遇到这种情况,但一些专业性的应用则经常需要将 PHP 的性能发挥到极致(这里的性能是指速度或功能)。由于受到 PHP 语言本身的限制,同时还可能不得不把庞大的库文件包含到每个脚本当中。因此,某些新功能并不是总能被顺利实现,所以我们必须另外寻找一些方法来克服 PHP 的这些缺点。

了解到了这一点,我们就应该接触一下 PHP 的心脏并探究一下它的内核-可以编译成 PHP 并让之工作的 C 代码-的时候了。

译序:
网上关于 PHP 的资料多如牛毛,关于其核心 Zend Engine 的却少之又少。PHP 中文手册出现已 N 年,但 Zend API 的翻译却仍然不见动静,小弟自觉对 Zend Engine 略有小窥,并且翻译也有助于强迫自己对文章的进一步理解,于是尝试翻译此章,英文不好,恭请方家指点校核。转载请注明来自抚琴居(译者主页):http://www.yAnbiN.org/

PHP 中文手册《Zend API:深入 PHP 内核》一章当前翻译进度(已翻译完毕):

  1. 摘要
  2. 概述
  3. 可扩展性
  4. 源码布局
  5. 自动构建系统
  6. 开始创建扩展
  7. 使用扩展
  8. 故障处理
  9. 关于模块代码的讨论
  10. 接收参数
  11. 创建变量
  12. 使用拷贝构造函数复制变量内容
  13. 返回函数值
  14. 信息输出
  15. 启动函数与关闭函数
  16. 调用用户函数
  17. 支持初始化文件(php.ini)
  18. 何去何从
  19. 参考:关于配置文件的一些宏
  20. API 宏

作为目前最优秀 Delphi 反编译器(其实称之为针对 Delphi 的反汇编器会更为恰当些),DeDe 已经有三年没有更新了。随着越来越多的 Delphi 程序员开始转到 BDS2006,它也似乎有点力不从心了。除了不能正确识别高于 Delphi 7 编译的程序的编译器版本外,还有以下几个方面有待改进:

  1. 缺乏对应的 VCL 符号识别文件。这本应该在设计时考虑到的,但制作相应版本 VCL 符号文件却异常困难。似乎除了作者自己外,他人很难操刀;
  2. Dump DCU 引擎比较古老,现在已经有新版本出来了;
  3. 对 Delphi 窗体可视化编辑也不太好,对中文字符串也不能正确显示;
  4. 整个反汇编项目不能保存。下次只能重新“处理”。

所幸网上流传有 DeDe 较早版本(v3.10,DeDe 最新版本为 v3.50)的源代码,可供我们瞻仰学习一番。希望我能彻底读懂 DeDe 源代码,因为它的源代码实在太难读了~ 🙁

根据鄙人的使用经验,感觉相对于普通的 CHM 手册,Extended CHM 版则额外提供了以下几种主要特性:

  1. 附带非常实用用户注释,其价值不亚于用户手册,这是最大的优点!
  2. 可以使用自定义的 CSS 文件来切换外观,可自定义右键菜单;
  3. PHP 代码块以语法高亮显示;
  4. PHP 代码块中的函数为超链接形式,点击自动跳转至相应的函数;
  5. 可以很方便地集成于大多数 IDE 和编辑器。

尽管 Extended CHM 格式的 PHP 手册提供了这么多优秀特性,但 PHP 官方网站却只提供了英文版,始终没有提供简体中文版。因此我就趁空闲之机编译了这套手册。虽然网上也有其他热心 PHPER 编译提供的中文版,但大多只是简单的从 CVS 上 Checkout 编译了一下,并没有针对中文版的特殊情况做些处理,因此就出现了诸如“不能搜索中文”、“搜索到的条目中文标题显示为乱码”或“页面中某些中文显示为数字编码”等 BUG。而本版本几乎完美的解决了这些 BUG ,欢迎大家测试使用,反馈请到PHP 官方站点抚琴居,我只负责编译问题。:-)

压缩包内的文件说明:

  • php_manual_zh.chm 是手册的主文件,如果你只需要纯文本的手册,可以只保留这个文件。
  • php_manual_notes.chm 是手册对应章节的用户注释部分,不能单独打开,只能配合 php_manual_zh.chm 使用。这是一个很有用的东东,补充了一些手册没有的或不宜加入的部分,并且是随时更新的。
  • php_manual_prefs.js 负责为上面两个 chm 文件载入相应的 skin ,并且提供了对自定义右键菜单功能的支持。
  • context.ini 即是右键菜单的定义部分。你可以使用 php_manual_prefs.exe 这个 GUI 程序来配置,也可以直接手动编辑 context.ini 文件,但是编辑前注意和 php_manual_prefs.js 相关部分对应。
  • /skins/ 这个目录保存的即是一些另外的 skin(High 和 Low 两种Skin已经内置在两个 chm 文件中),可以根据自己的喜好来定。
  • mirrors.ini 里面保存所有 php.net 的镜像网址,在手册主文件当中,有个在线版本的连接,可以在 php_manual_prefs.exe 中定义采用哪个镜像来访问。

再附上一条使用技巧--在 EditPlus 中集成本手册的方法:

【工具(Tools)】–>【用户工具(User Tools)】–>【添加工具(Add Tool)】:
菜单文本(Menu Text):PHP 手册
命令(Command): HH X:\XXXXXX\php_manual_zh.chm (此处X:\XXXX替换为您的 chm 帮助文件的位置)
参数(Argument): ::/_function.html#$(CurWord)
初始目录(Initial): $(FileDir) (可不填)

使用方法:把点击某一关键词,然后按快捷键(如:Ctrl+1,这个快捷键可以在【工具(Tools)】菜单下看到)即可。

点此下载PHP 手册中文版(Extended CHM 格式)

'