1313
1414
1515### 存在可能性的扩展
16+ ===
1617
1718 PHP主要在三个地方可被扩展:外部模块,内置模块,Zend引擎。
1819
1920外部模块:
2021 外部模块可以在脚本运行时用 dl() 函数加载。这个函数通过在磁盘上加载一个共享对象使模块功能对绑定的脚本可用。
2122这种方法有好处也有坏处:
2223
23- ```
24- --------------------------------------------------------------------------
25- | 好处 | 坏处 |
26- |--------------------------------------------------------------------------|
27- | 外部模块不需要重新编译PHP | 共享对象在脚本执行时每次都要加载,非常慢 |
28- |--------------------------------------------------------------------------|
29- | 通过外包某些功能保持PHP的大小 | 外部的额外文件使磁盘杂乱 |
30- |--------------------------------------------------------------------------|
31- ```
24+ | 好处 | 坏处
25+ |--- |---
26+ | 外部模块不需要重新编译PHP | 共享对象在脚本执行时每次都要加载,非常慢
27+ | 通过外包某些功能保持PHP的大小 | 外部的额外文件使磁盘杂乱
3228
3329 每个使用外部模块功能的脚本需要特别包含一个 dl() 的调用,否则需要修改 php.ini 的扩展标签(不总是合适的解决办法)。
3430
4137 内置模块直接编译进PHP并被每一个PHP进程携带;它们的功能对每一个正在运行的脚本来说都是即刻可用的。
4238相比外部模块,内置模块有好处和坏处:
4339
44- ```
45- -------------------------------------------------------------------------------------------
46- | 好处 | 坏处 |
47- |-------------------------------------------------------------------------------------------|
48- | 不需要明确的加载模块;功能立即可用 | 内置模块的变化需要重新编译PHP |
49- |-------------------------------------------------------------------------------------------|
50- | 没有使磁盘杂乱的外部文件;什么都驻留在PHP二进制文件中 | PHP二进制文件增多并且消耗更多内存 |
51- |-------------------------------------------------------------------------------------------|
52- ```
40+ | 好处 | 坏处
41+ |--- |---
42+ | 不需要明确的加载模块;功能立即可用 | 内置模块的变化需要重新编译PHP
43+ | 没有使磁盘杂乱的外部文件;什么都驻留在PHP二进制文件中 | PHP二进制文件增多并且消耗更多内存
5344
5445 当你有一个保持相对不变的可靠的二进制的功能库,要求比平均性能好,或者在你的站点上被许多脚本经常的使用,内置模块是最好的。
5546必须重新编译PHP的操作很快被速度和易用的好处所补偿。然而,当需要快速的开发小的补充时,内置模块并不是最适合的。
@@ -60,32 +51,24 @@ Zend引擎:
6051修改部分不能与主PHP源文件分开并且下次使用官方源文件库升级会覆盖。因此,这种方法通常被认为是糟糕的实践,由于它的罕见,没有包括在这里。
6152
6253
63- ### 源码排版
54+ ### 源码布局
55+ ===
6456
6557 在我们讨论代码问题前,你应该熟悉源代码树能快速浏览PHP文件。这是实现和调试扩展必须具备的能力。
6658接下来的表格描述了主要目录的内容:
6759
68- ```
69- ---------------------------------------------------------------------------------------------------
70- | 目录 内容 |
71- |---------------------------------------------------------------------------------------------------|
72- | php-src 主要的PHP源文件和主要的头文件;这里有所有PHP的API定义,宏,等等(重要)。 |
73- | 其它任何东西都在这个目录下面。 |
74- |---------------------------------------------------------------------------------------------------|
75- | php-src/ext 动态的和内置模块的仓库;默认情况,这些是与主要源文件树融为一体的官方PHP模块。 |
76- | 从PHP4.0开始,编译这些标准扩展作为动态加载的模块是可能的(至少,是支持的)。 |
77- |---------------------------------------------------------------------------------------------------|
78- | php-src/main 这个目录包含主要的php宏和定义。(重要) |
79- |---------------------------------------------------------------------------------------------------|
80- | php-src/pear PHP扩展和应用仓库(PEAR)。这个目录包含核心PEAR文件。 |
81- |---------------------------------------------------------------------------------------------------|
82- | php-src/sapi 包含针对不同服务器抽象层的代码。 |
83- |---------------------------------------------------------------------------------------------------|
84- | TSRM Zend和PHP的"线程安全资源管理器"(Thread Safe Resource Manager, TSRM)。 |
85- |---------------------------------------------------------------------------------------------------|
86- | ZendEngine2 Zend引擎文件;这里可以找到所有Zend的API定义,宏,等等(重要)。 |
87- ---------------------------------------------------------------------------------------------------
88- ```
60+ | 目录 | 内容
61+ |--- |---
62+ | php-src | 主要的PHP源文件和主要的头文件;这里有所有PHP的API定义,宏,等等(重要)。<br />
63+ | | 其它任何东西都在这个目录下面。
64+ | php-src/ext | 动态的和内置模块的仓库;默认情况,这些是与主要源文件树融为一体的官方PHP模块。<br />
65+ | | 从PHP4.0开始,编译这些标准扩展作为动态加载的模块是可能的(至少,是支持的)。
66+ | php-src/main | 这个目录包含主要的php宏和定义。(重要)
67+ | php-src/pear | PHP扩展和应用仓库(PEAR)。这个目录包含核心PEAR文件。
68+ | php-src/sapi | 包含针对不同服务器抽象层的代码。
69+ | TSRM | Zend和PHP的"线程安全资源管理器"(Thread Safe Resource Manager, TSRM)。
70+ | ZendEngine2 | Zend引擎文件;这里可以找到所有Zend的API定义,宏,等等(重要)。
71+
8972
9073 讨论包含在PHP包中的所有文件超出了本章范围。然而,你应该仔细看一下下面的文件:
9174
@@ -109,24 +92,16 @@ Zend引擎:
10992你应该遵守这种抽象的明确原因是:由于这种抽象,Zend获得所有内存分配的完全控制权。
11093Zend能够确定一个块是否在使用,自动释放未使用的块和丢失引用的块,这样来阻止内存泄漏。使用的函数在下表中:
11194
112- ```
113- -------------------------------------------------------------------------
114- | 函数 描述 |
115- |-------------------------------------------------------------------------|
116- | emalloc() 作为 malloc() 的替代 |
117- |-------------------------------------------------------------------------|
118- | efree() 作为 free() 的替代 |
119- |-------------------------------------------------------------------------|
120- | estrdup() 作为 strdup() 的替代 |
121- |-------------------------------------------------------------------------|
122- | estrndup() 作为 strndup() 的替代。比 estrdup() 更快并且二进制安全。 |
123- | 这是推荐使用的函数,如果你在复制字符串之前知道它的长度。 |
124- |-------------------------------------------------------------------------|
125- | ecalloc() 作为 calloc() 的替代 |
126- |-------------------------------------------------------------------------|
127- | erealloc() 作为 realloc() 的替代 |
128- -------------------------------------------------------------------------
129- ```
95+ | 函数 | 描述
96+ |--- |---
97+ | emalloc() | 作为 malloc() 的替代
98+ | efree() | 作为 free() 的替代
99+ | estrdup() | 作为 strdup() 的替代
100+ | estrndup() | 作为 strndup() 的替代。比 estrdup() 更快并且二进制安全。
101+ | | 这是推荐使用的函数,如果你在复制字符串之前知道它的长度。
102+ | ecalloc() | 作为 calloc() 的替代
103+ | erealloc() | 作为 realloc() 的替代
104+
130105
131106 译者注:
132107 malloc() : 手动分配内存空间,malloc() 不能初始化所分配的内存空间。
@@ -147,27 +122,18 @@ Zend能够确定一个块是否在使用,自动释放未使用的块和丢失
147122目录和文件函数:
148123 下面的目录和文件函数应该在Zend模块中使用。它们的行为完全像C的副本,但它们在线程级别上提供虚拟工作目录支持。
149124
150- ```
151- ---------------------------------------------------------------------------
152- | Zend函数 规则的C函数 |
153- |---------------------------------------------------------------------------|
154- | V_GETCWD() getcwd() |
155- |---------------------------------------------------------------------------|
156- | V_FOPEN() fopen() |
157- |---------------------------------------------------------------------------|
158- | V_OPEN() open() |
159- |---------------------------------------------------------------------------|
160- | V_CHDIR() chdir() |
161- |---------------------------------------------------------------------------|
162- | V_GETWD() getwd() |
163- |---------------------------------------------------------------------------|
164- | V_CHDIR_FILE() 把文件路径作为参数,并把当前工作目录改为那个文件的目录 |
165- |---------------------------------------------------------------------------|
166- | V_STAT() stat() |
167- |---------------------------------------------------------------------------|
168- | V_LSTAT() lstat() |
169- ---------------------------------------------------------------------------
170- ```
125+
126+ | Zend函数 | 规则的C函数
127+ |--- |---
128+ | V_GETCWD() | getcwd()
129+ | V_FOPEN() | fopen()
130+ | V_OPEN() | open()
131+ | V_CHDIR() | chdir()
132+ | V_GETWD() | getwd()
133+ | V_CHDIR_FILE()| 把文件路径作为参数,并把当前工作目录改为那个文件的目录
134+ | V_STAT() | stat()
135+ | V_LSTAT() | lstat()
136+
171137
172138字符串处理:
173139 由Zend引擎处理的字符串和其它如整型,布尔型等有一点不同,字符串不需要额外的内存分配来存储它们的值。
@@ -181,6 +147,7 @@ Zend能够确定一个块是否在使用,自动释放未使用的块和丢失
181147
182148
183149### PHP的自动生成系统
150+ ===
184151
185152 PHP4有非常灵活地自动生成系统。所有的模块在ext目录的一个子目录中。除了它自己的源代码外,
186153每个模块包含一个 config.m4 文件,作为模块配置。
@@ -312,6 +279,7 @@ Zend能够确定一个块是否在使用,自动释放未使用的块和丢失
312279 组成扩展的文件:http://php.net/manual/zh/internals2.structure.files.php )
313280
314281### 创建扩展
282+ ===
315283
316284 我们起初将以创建一个非常简单的扩展作为开始,它基本上不过实现了一个返回接收到的整型参数的函数。下面的扩展展示了源代码。
317285
@@ -403,15 +371,12 @@ Zend能够确定一个块是否在使用,自动释放未使用的块和丢失
403371手工编译:
404372 手工编译模块使用下面的命令:
405373
406- ```
407- -------------------------------------------------------------------------------------------------
408- | 动作 命令 |
409- |-------------------------------------------------------------------------------------------------|
410- | 编译 cc -fpic -DCOMPILE_DL_FIRST_MODULE-1 -l/usr/local/include -l. -l.. -l../Zend -c -o |
411- |-------------------------------------------------------------------------------------------------|
412- | 链接 cc -shared -L/usr/local/lib -rdynamic -o <your_module_file> <your_object_file(s)> |
413- -------------------------------------------------------------------------------------------------
414- ```
374+
375+ | 动作 | 命令
376+ |--- |---
377+ | 编译 | cc -fpic -DCOMPILE_DL_FIRST_MODULE-1 -l/usr/local/include -l. -l.. -l../Zend -c -o
378+ | 链接 | cc -shared -L/usr/local/lib -rdynamic -o <your_module_file> <your_object_file(s)>
379+
415380
416381 编译模块的命令指示编译器生成一个位置无关的代码( -fpic 不要漏掉)并额外定义了COMPILE_DL_FIRST_MODULE常量\
417382来告诉模块代码已经编译成一个动态加载的模块(以上的测试模块;我们将马上讨论)。
@@ -428,6 +393,8 @@ Zend能够确定一个块是否在使用,自动释放未使用的块和丢失
428393
429394
430395### 使用扩展
396+ ===
397+
431398 根据你所选择的构建过程,你应该选择结束一个链接到你的Web服务器的的PHP二进制文件(或作为CGI运行),或是一个.so(共享对象)文件。
432399如果你编译例子文件 first_module.c 作为一个共享对象,你的目标文件应该是 first_module.so。
433400要使用它,首先你要复制一份到PHP能访问的地方。对于一个简单的测试程序,你可以复制它到 htdocs 目录并试一下Example #3 的源代码。
@@ -460,10 +427,11 @@ Zend能够确定一个块是否在使用,自动释放未使用的块和丢失
460427
461428 如果你已经得到了这个,恭喜!你刚才构建了你的第一个PHP扩展。
462429
430+ ### 分析解决问题
431+ ===
463432
464- ----
433+ ...
465434
466- Part2 开始
467435
468436### 调用用户使用的函数 [ Calling User Functions ]
469437===
@@ -552,7 +520,7 @@ Return value: 'hello'
552520
553521
554522### 初始化文件支持
555- ---
523+ ===
556524PHP4以重新设计的文件支持为特色。现在直接在代码中指定默认的初始化方式已成为可能,在运行时读取和改变这些值,
557525并且为更改通知创建消息句柄。
558526
@@ -596,3 +564,56 @@ PHP_INI_MH(OnChangeSecond)
596564 uint new_value_length, void *mh_arg1,
597565 void *mh_arg2, void *mh_args)
598566```
567+
568+ 所有这些定义可以在 * php_ini.h* 中找到。你的消息句柄可能需要访问包含所有记录的结构,新值,长度,其它三个选项参数。
569+ 这些选项参数可以用其它的宏来指定 * PHP_INI_ENTRY1()* (允许一个额外的参数),* PHP_INI_ENTRY2* (允许两个额外的参数),
570+ * PHP_INI_ENTRY3* (允许三个额外的参数)。
571+ 更新通知句柄(回调函数)应该在初始化记录时被缓存,为了更快的访问或执行值改变后确定的任务。
572+ 例如,如果一个模块需要一个确定主机的长连接并且有人更改了这个主机名,会自动终止旧的连接并尝试新的连接。
573+
574+ 访问初始的记录可以用下面的宏处理:
575+ 在PHP中访问初始记录的宏
576+
577+ | 宏 | 描述
578+ |--- |---
579+ | * INI_INT(name)* | 把当前记录name的值作为整型(long)返回。
580+ | * INI_FLT(name)* | 把当前记录name的值作为浮点数(double)返回。
581+ | * INI_STR(name)* | 把当前记录name的值作为字符串返回。注意:<br />字符串不是副本,而是指向数据的指针。进一步的访问需要复制到本地内存。
582+ | * INI_BOOL(name)* | 把当前记录name的值作为布尔值返回(由zend_bool定义,意思是unsigned char)。
583+ | ** INI_ORIG_INT(name)** | 把name的初始值作为整型值(long)返回。
584+ | ** INI_ORIG_FLT(name)** | 把name的初始值作为布尔值(double)返回。
585+ | ** INI_ORIG_STR(name)** | 把name的初始值作为字符串返回。非副本,而是指向数据的指针。进一步的访问需要复制到本地内存。
586+ | ** INI_ORIG_BOOL(name)** | 把name的初始值作为布尔值返回(由zend bool定义,意思是unsigned char)。
587+
588+ 最后,你需要把初始值放入PHP。这个可以在模块启动和关闭函数中做,使用宏 * REGISTER_INI_ENTRIES()* 和 * UNREGISTER_INI_ENTRIES()*
589+
590+ ```
591+ ZEND_MINIT_FUNCTION(mymodule)
592+ {
593+
594+ REGISTER_INI_ENTRIES();
595+
596+ }
597+
598+ ZEND_MSHUTDOWN_FUNCTION(mymodule)
599+ {
600+
601+ UNREGISTER_INI_ENTRIES();
602+
603+ }
604+ ```
605+
606+ ### 从这儿去哪儿
607+ ===
608+ 你已经学了很多关于PHP的。你知道如何创建动态加载的模块并静态链接的扩展。你已经学了PHP和Zend变量的内部存储并能创建和访问这些变量。
609+ 你知道相当一套做许多常规任务的工具函数如打印信息字符,自动绑定变量到符号表,等等。
610+
611+ 尽管这章经常有主要的“参考”字符,我们希望它让你洞悉如何开始写自己的扩展。空间有限,我们不得不放弃了很多;
612+ 我们建议你花时间学习头文件和一些模块(特别是在 ` ext/standard ` 目录中的和 MySQL 模块,因为这些实现通常是实用的)。
613+ 这将带给你其他人使用API函数的idea - 那些没有在这章中的。
614+
615+ ### 参考:一些配置宏
616+ ===
617+ ** config.m4**
618+
619+
0 commit comments