1. 2005-06-27 Zend API 手册中文翻译计划

PHP手册中的一个章节便是介绍Zend API,苦于满篇英文,不易理解,因此启动中文翻译计划,为更广大的朋友玩起Zend API做些贡献啊~一起来参与!

张晓烨
刘彤
召唤其他人~~

1.1. Hacking the Core of PHP

Those who know don't talk.

Those who talk don't know.

Sometimes, PHP "as is" simply isn't enough. Although these cases are rare for the average user, professional applications will soon lead PHP to the edge of its capabilities, in terms of either speed or functionality. New functionality cannot always be implemented natively due to language restrictions and inconveniences that arise when having to carry around a huge library of default code appended to every single script, so another method needs to be found for overcoming these eventual lacks in PHP.

As soon as this point is reached, it's time to touch the heart of PHP and take a look at its core, the C code that makes PHP go.

1.2. What Is Zend? and What Is PHP

//手册上看着比较费解,谁来负责这段阿。。。

//有错儿请修改

Zend指的是语言的引擎,即PHP的核心。PHP指的是从外面所看到的整个系统。这起初听起来有点儿混乱,但是它并不难理解(见图24-1)。实现一个Web脚本解释器,你需要三个部分:

 1、解释器部分分析输入的代码,解释并执行它。
 2、功能性部分实现语言的功能(它的函数,等等)
 3、接口部分与Web服务器通讯,等等

Zend完成了部分1和一点儿部分2中的内容;PHP完成了部分2和3。它们组合在一起形成了完整的PHP包。Zend仅仅形成了语言的核心,用一些预定义的函数实现了PHP的一些非常基础的东西。PHP则包含了创造出这门语言突出能力的所有模块儿。

图24-1. PHP的内部结构

Upload new attachment "php-clip-24-1.jpg"

1.3. Extension Possibilities

php主要可以从以下三种方式扩展

1、外部模块 external modules
2、内建模块 built-in modules
3、Zend engine

1.3.1. External Modules

外部模块可以实时的通过dl()函数载入脚本。这个函数从磁盘载入一个共享的实例到脚本中,让他所绑定的脚本可以使用实例的功能。当这个脚本结束后,外部模块则从内存中注销。这个方法有利有弊,如下:

Advantages

Disadvantages

外部模块的载入不需要重新编译php

当脚本每次运行时都需要重新载入共享实例,比较慢

php通过外部代码实现某些功能来保证自身的大小

外部附加文件使得磁盘混乱

每个脚本如果想使用外部模块,不得不使用dl()函数,或者修改Php.ini的extension标签

综上所述, 外部模块对于第三方产品非常有益,php很少用到简单的附加,仅仅是测试的目的。为了快速开发附加功能,外部模块提供了最好的方式。但是若想开发一个频繁使用、大量应用、复杂代码,那么它的弊大于利。

第三方可能考虑到利用php.ini中的extension标签创建外部模块。这些外部模块完全与主库分离,而是用于商业目的,他们只需要简单的发布一个压缩包,里面包含着外部模块,而不需要修改php的2进制文件,也不允许其他模块找到它们。

1.3.2. Built-in Modules

内建模块直接编译到Php里,并且随着每个php进程;他们可以被正在运行的任何脚本随时调用。就像外部模块,内建模块同样有利有弊。如下:

Advantages

Disadvantages

不用载入指定模块,可以随时随地运行

如果内建模块发生更改,需要重新编译php

没有凌乱的外部文件,所有文件都保存在php的2进制文件中

php的2进制代码将消耗更多内存

当你拥有一个单独的函数库,并且保证相对不会变化,要求运行效率,并且在你的网站中大量用到这个库,那么内建模块是最好的选择。需要重新编译php的消耗将很快从其速度和易用性中获得补偿。内建函数对于开发很小的附加程序不是一个很好的主意。

1.3.3. The Zend Engine

当然,扩展模块也可以直接被Zend引擎执行。如果你需要改变一个语言习惯或者要求特定函数被直接编译到php的核心,那么利用zend引擎是个好的选择。一般来说,尽量避免修改Zend Engine.因为修改后的结果,会导致和其他人不兼容,而且很少人会适应经过特殊补丁的Zend引擎。所有的变更无法从主要的Php代码中脱离,并且限制了下一次官方升级。因此,这种方式被认为是最坏的方式,考虑到很少有人用到它,因此在手册中没有提及。

1.4. Source Layout

在我们讨论代码之前,必须自己去熟悉原代码树,这是执行和调试扩展模块所必须的。

下面的表格描述了主要目录的内容:

Directory

Contents

php4

主要的php源文件和头文件,可以在这里找到php的API定义、宏。其他内容在下面的目录中。

php4/ext

动态和内建模块库,默认的,这里是官方的php模块,已经整合到main source tree。自从php 4.0以来,可以用动态载入模块方式编译这些标准模块

php4/main

这个目录包括主要的php定义和宏

php4/pear

核心的pear文件

php4/sapi

Contains the code for the different server abstraction layers.

php4/TSRM

Location of the "Thread Safe Resource Manager" (TSRM) for Zend and PHP.

php4/Zend

Zend引擎文件,在这里可以找到Zend's API定义、宏。

1.4.1. Macros

为了几乎所有重要任务,Zend预定义的宏是非常便捷的。在下面的章节中,讲解大部分基础函数、结构和宏定义。宏定义可以在zend.h和zend_API.h中找到。我们建议,在学习以下章节之前,仔细阅读这些文件。(虽然你可以向后阅读,但是不是所有东西对你都有意义。)

1.4.2. Memory Management

资源管理是一个关键性事务,特别是在服务端软件。内存是最有价值的资源之一,并且内存管理应该得到高度重视。内关管理已经被Zend部分抽象,你可以遵守其抽象,原因如下:归结于抽象,Zend控制了所有的内存分配。Zend可以得知一块内存是否正在被应用,自动的回收未使用的内存块,并且防止引用丢失,因此可以组织内存泄露。被用到的函数在下面表格中描述:

Function

Description

emalloc()

malloc()的替代

efree()

free()的替代

estrdup()

strdup()的替代

estrndup()

strndup()的替代。比estrdup()快而且安全。如果你事先知道字符串的长度,则推荐用这个函数

ecalloc()

calloc()的替代

erealloc()

realloc()的替代

emalloc(),estrdup(),estrndup(),ecalloc(),and erealloc()分配内部内存;efree()释放之前分配的内存块。内存被e*()处理,是考虑到以来当前进程,并且一旦运行的脚本结束,能够马上注销。

警告:
如果为当前的进程分配内存,可以用malloc()和free()。这需要提高警惕除非你捷克Zend API使用。否则,有内存泄露的危险!

Zend还有一个特点就是线程安资源安全管理,它对多线程web server提供了更好的支持。它要求给所有全局变量分配本地资源,使当前线程运行。因为线程安全模式尚未完成,在这里就不过多描述了。

1.4.3. Directory and File Functions

下面的目录和文件函数在Zend模块中会用到。它们的行为很像它们的C语言副本,但是它们提供线程级的虚拟工作目录。

Zend Function

Regular C Function

V_GETCWD()

getcwd()

V_FOPEN()

fopen()

V_OPEN()

open()

V_CHDIR()

chdir()

V_GETWD()

getwd()

V_CHDIR_FILE()

将一个文件路径作为参数传入,改变当前路径到该文件路径

V_STAT()

stat()

V_LSTAT()

lstat()

1.4.4. String Handling

在Zend引擎中,字符串的处理比起其他变量如整形、布尔有一些不同,整形和布尔不需要额外的内存分配来存放。如果你想从一个函数中返回字符串,给符号表传入一个新的变量,或者类似的,你必须确定字符串将占用的内存在之前已经用e*()函数分配了。(以前,这可能对你来说不重要;但是从现在开始重视。我们很快就会讲到它。)

1.4.5. Complex Types

复杂类型,如数组、对象,要求不同对待。Zend用单一的API对待这些类型,把它们存放在哈希表中。

注:
为了减少下面例子的复杂度,我们刚开始只用到整数这样的简单类型。在这章的稍后会介绍如何创建更复杂类型。

1.5. PHP's Automatic Build System

PHP4的自动build系统是非常有弹性的。所有的模块都放在ext子目录中。除了它所用的资源外,每个模块由一个config.m4文件组成,为了扩展模块的设置。(例子:http://www.gnu.org/manual/m4/html_mono/m4.html)

所有这些文件都是自动生成的,连同.cvsignore一起,由一个叫做ext_skel脚本生成,它存在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.

这个介绍创建了上述文件。为了在自动配置和build过程中包含新模块,你必须运行buildconf,它通过查询ext目录,包含了所有可以找到的.m4文件,然后重新生成配置脚本。

例子 27-1. The default config.m4.

dnl $Id: Extending_Zend_Build.xml,v 1.8 2002/10/10 18:13:11 imajes 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
  dnl       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