您了解 PHP 吗?

  PHP 的初期

  我在大约 10 年前开始开发 PHP。那时在名词“开放源代码”出现且 GPL 和自由软件广为人知之前很久。正如许多已经变得流行的开放源代码项目一样,动机决不是哲学上的或者甚至是自我陶醉。它纯粹是因为需要一种工具来解决现实中与 Web 相关的问题。在 1994 年,当提到 Web 开发工具时,选择相当有限。我发现当我自己在用 C 或 Perl 为 Web 站点编写动态组件时,从一个问题到另一个问题的代码重叠现象十分显著。出于性能的考虑,我越来越远离 Perl,而倾向于 C,因为必须将 Perl 作为一个单独的 CGI 运行的 fork+exec 开销限制性太强了。

  PHP 最早的未发布版本主要是常用 C 函数的一个 C 资料库,我编写了这些 C 函数,以便能够容易地从一个开放源代码项目到另一个开放源代码项目进行重用。我有一个简单的状态机驱动的分析器,它从 HTML 文件中挑出标记,然后调用我编写的后端 C 函数。这些代码最初是作为一个称为个人主页工具 (Personal Home Page Tools) 的程序包公开发布的,这个程序包中的每个工具都是关于如何使用系统来解决关于个人主页的常见问题的一个例子。在之后的某段时间,我从中分出了一部分工具并把它称为 FI,代表表单解释程序 (Form Interpreter)。FI 幕后的想法是,当您接收到一个表单提交的结果时,您需要做一些常见的事情,而它能够处理所有这些事情。一些早期的例子:

<!–getenv HTTP_USER_AGENT–> <!–ifsubstr $exec_result Mozilla–> Hey, you are using Netscape!<p> <!–endif–> <!–sql database select * from table where user=’$username’–> <!–ifless $numentries 1–> Sorry, that record does not exist<p> <!–endif exit–> Welcome <!–$user–>!<p> You have <!–$index:0–> credits left in your account.<p> <!–include /text/footer.html–>

  我的 FI 的分析器非常糟糕,这促使我试着去编写一个更好的分析器。我抛弃了 <!– cmd –> 语法,而改用 <? cmd >,重新将 Personal Home Page Tools 的某些部分和这个新的 FI 工具结合起来,并在 1995 年底将它作为一个名称为 PHP/FI (这个名称有点半开玩笑性质地模仿了 TCP/IP)的程序包对外发布。PHP/FI 在接下来的几年里正好和 Web 一起得到了发展。在 1997 年,两个正使用 PHP/FI 的以色列人 Zeev Suraski 和 Andi Gutmans 问我是否有兴趣使用一个新的分析引擎,他们将为下一个版本的 PHP 编写这个引擎。我召集了其它一些一直在为 PHP/FI 提供补丁和代码的人,我们共同协作在 1998 年中期发布了 PHP 第 3 版。这可能是 PHP 发展期间最关键的时刻。如果这个项目一直是一个人在努力,那么当时它可能已经失败了,如果一群刚刚组合在一起的陌生人弄不清楚如何朝着一个共同的目标去努力,那么这个项目也可能很容易就失败了。无论怎样,我们设法解决了我们以自我为中心的问题和其它的个人问题,这个项目成长了起来。为这个项目做出贡献的人的数量在稳定地增长,现在我们打算在 2004 年上半年的某个时候推出 PHP 5.0 版本。

  编程语言中的“丑小鸭”

  关于 PHP 的流行观点可以分为两个极端。语言纯化论者往往不喜欢许多有点随意的特性实施和这些年来出现的一些前后不一致的地方。同时,实际问题的解决者往往喜欢 PHP 似乎已经读懂您的意图,并表现出它就是理想的 Web 问题解决工具。

  在使纯化论者抓狂的事情中有:函数名称不区分大小写但变量名称区分大小写;内置函数的名称前后不一致;没有强制 PHP 开发人员使用任何真正的结构,从而使得很容易写出凌乱的代码。实际上我不得不同意这些批评意见,但我至少可以试着解释一下我们如何以及为什么到了这种状态。

  首先,关于函数名区分大小写的问题:这可以回溯到最早的 PHP 版本。在 Web 的早期(XHTML 之前很久),所有的 HTML 标记标签都是大写的是很常见的。但因为这些标签是不区分大小写的,所以人们在这上面并不是非常一致。我希望人们对待具体的 PHP 标签基本就像和对待其它的标记标签一样,这意味着 PHP 的标签也将是不区分大小写的。当 PHP 变得更加高级并且拥有了如变量之类的特性时,使这些新的特性区分大小写并没有什么害处,因为它没有破坏对已有的 PHP 页面的向后兼容性。回过头去,突然处理最初的简单标签(这些标签实质上只是函数调用)时,区分大小写将破坏那些页面,并使它们在更新的 PHP 版本中不可用。无论如何,人们不应该拥有只是名称大小写不同的函数。尽管如此,回想起来,当相对少的人在使用 PHP 时,尽早地打破向后兼容性是个好主意;但在那时,没有人预料到 PHP 的惊人成长。

  至于函数命名本身,我往往是从我熟悉的其它语言和 API 借鉴一些想法。这意味着 PHP 拥有诸如 strlen() 和 substr() 之类的函数,它们如果写成 str_len() 或 sub_str(),那看起来将有点可笑。我增加了如 stripslashes() 之类的函数,这些函数由于长度的原因常常被写为 StripSlashes(),以使其更易于读懂。同时,我模仿了底层的数据库 API,诸如函数 msql_connect() — miniSQL 是第一个被 PHP 支持的数据库 — 它们可使用下划线命名。熟悉这些不同来源的人将非常熟悉 PHP 中的命名。当 PHP 作为 Web 服务器和您想要挂在 Web 服务器上的所有不同后端工具之间的一个接口时,它并不是这样一种独立的语言。因此,今天当人们看到 PHP 作为一种独立的语言,而没有考虑其前后关系的时候,它可能看起来有点前后不一致。

  关于缺少强制的结构,我能说的是,我绝对痛恨使我限制在解决问题的某种特定方法上的编程框架。这不意味着我不相信结构和框架,但我的确相信人们能够提出他们自己的、与他们的环境相称的方法。在本文稍后我论述各种 PHP 项目的可能体系结构时,将更多地涉及这个问题。

  所有这些归结为 PHP 从来不打算去赢得任何“选美比赛”。它的目的不是要引入任何新的革命性的编程范例。它的目的是解决单个问题:Web 问题。这种问题可能会相当难看,有时您需要一种难看的工具来解决难看的问题。实际上,虽然一种漂亮的工具也可能解决问题,但常常一种难看的 PHP 解决方案能够更快速地得到实施,并且使用更少的资源。这大概总结了这些年来 PHP 的固执的“功能优于形式”的方法。

  给设计师的建议

  PHP 的最流行的部署模式是将它直接和预先分支的多进程 Apache 1.3.x Web 服务器连接起来。和使用 Java 不同的是,不存在独立的过程(如 JVM)。PHP 类似于如 Perl 和 Python 之类的脚本语言,在这些脚本语言中,直接分析和执行脚本。

  没有中央控制过程是一个特色,同时也是给许多人带来极大挫折的原因。PHP 不共享任何体系结构 — 其中每条请求都完全不同并且可以同其它任何请求分开 — 使这种语言本身具有无限的平行可伸缩性。PHP 鼓励您把可伸缩性问题放到需要它的层次上去。如果您需要共享的数据存储,那么就使用一个支持复制和能够扩展到您需要的层次上的数据库。如果您需要负载均衡请求或将特定的请求分配到特定的服务器上,那么就使用一个支持这种功能的前端负载均衡器。通过消除中央控制过程,PHP 避免了成为系统中的瓶颈。这是使 PHP 区分于人们通常称之为应用服务器的工具的判定特性。

[oarch1.png] 用于高端 PHP 部署的一个非常常见的体系结构

  在上图中,一个或更多的负载均衡器将进入的请求分配在任意数量的 Web 服务器上。考虑到数据存储,您可能在每个 Web 服务器上部署一个只读的数据库拷贝(如果数据集足够小,允许您那么做的话),或者您可能创建一个单独的数据库服务器树来处理各种类型的请求。

  增加结构

  PHP 超过其它许多针对解决 Web 问题的工具的巨大优势之一是,其它的工具往往把这种非常具体的目标问题的解决和从结构上控制用户解决问题的方式的要求联系在一起。PHP 不强制使用任何这种结构,相反选择专注于使问题的各个单独的功能方面尽可能地易于使用。例如,PHP 提供目的性非常强的函数来与后端数据库通信。这些函数是各个数据库专用的,并且不牺牲任何性能来获取和其它后端数据库的统一性或一致性。在文件布局方面,也没有规定的方法来构建 PHP 应用程序的结构。

  PHP 不强制使用结构的事实并不意味着您不应该以一种井然有序和结构化的方式来构建 PHP 应用程序。当人们问我将如何着手构建一个大型 PHP 应用程序的结构时,我喜欢向他们展示下面这种方法。

   +——————————–+
   |     HTML TEMPLATES             |
   |     $DOC_ROOT/*.php            |
   +——————————–+
   |     TEMPLATE HELPERS           |
   |     $DOC_ROOT/*.inc            |
   +——————————–+
   |     BUSINESS LOGIC             |
   |     /usr/local/php/*.inc       |
   +——————————–+
   |     C/C++ CORE CODE            |
   |     /usr/local/lib/php/*.so    |
   +——————————–+

  这个分四个层次的方法解决了一些问题。首先,它将一个典型项目中的内容沿着职责的线索分开。Web 前端开发人员从顶层进行工作,后端工程师从底层进行工作。他们在模板助手层有一点交叠。它还分开了包含 HTML 的任何文件,将它们放到 document_root 中,而将不包含 HTML 的任何文件放到 document_root 外。

  顶层的模板层一般包含非常少的 PHP — 只是简单的函数调用和偶然的包含。可能一个循环。这些文件通常用一个 HTML 编辑工具来进行编辑。第二层,即模板助手,是定义商务逻辑和布局之间的接口的地方。这一层可能有一些方便的函数(如 start_table()、 show_user_record())和其它任何可重用的组件,这些可重用组件使得模板制作者的工作更加容易。

  商务逻辑层完全不包含任何 HTML。这是实施诸如 SQL 查询和任何其它的 PHP 用户空间商务逻辑之类的东西的地方。您可以期望看到一个诸如 get_user_record() 之类的函数在这一层得到实施。这个函数将获取一个 ID,执行相应的 SQL 查询,然后返回包含结果的一个联合数组。然后,该层中的一个函数获取该数组,并为它包装一些 HTML,使它看起来好一点。

  最后的 C/C++ 层是您安放一个项目所需的任何定制的后端代码的地方。许多人在这一层上没有任何东西,但如果您有一个专用的 C 或 C++ 资料库,您就可以在这里编写一个 PHP 扩展来和资料库接口。有时当一个用用户空间 PHP 编写的商务逻辑函数太慢时,也将使用这一层。

  招聘和培训 PHP 开发人员

  PHP 不是一种新的语言。它没有引进任何新的概念。这意味着培训已经了解 C、C++、Perl 或者甚至 Java 中的任意一种语言的编程人员来编写 PHP 代码相当容易。当我找 PHP 开发人员来做一个项目的时候,我倾向于找拥有 C 或 C++ 技能的人,我的想法是招聘经验丰富的编程人员要比招聘必须对 PHP 非常了解的人员容易得多。如果他们能够掌握这些语言,PHP 对他们来说将是小菜一碟。当然,如果他们有两方面的经验,那就更好了。

  随意部署 PHP

  使用合适的工具进行工作。我遇到过一些完全采用了 PHP 的公司,它们绝对是将 PHP 部署到了每个地方,但 PHP 从未打算成为一种适合于所有问题的通用语言。它主要适合于作为 Web 的前端脚本语言。取决于 Web 站点的通信量,它还可以用来执行大批量的后端工作。但在某些地方,您将需要用一种强类型的编译语言(如 C 或 C++)来编写您的部分代码,以获得最优的性能。

  PHP 将去向何方?

  人们常常问我 PHP 将去向何方。这是一个很难回答的问题,因为 PHP 主要是一个应运而生的开放源代码项目,它的发展是为了满足其社区的需要。在 PHP5 中,OO 功能和与 XML 的集成得到了大大的改进。我们集成了一个称为 SQL-Lite 的有趣工具,这个工具直接为文件提供了一个 SQL 接口,而无需服务器。很明显,它替代不了实际的数据库,但使用它无疑是比试图编写您自己的纯文本文件处理例程好得多的一种方法。它为您提供了一个 SQL 接口的事实意味着到一个实际数据库的移植(如果在任何时候需要这么做的话)变得更容易。

  在 PHP5 中的这些变化虽然很重大,但却是渐进的。我们并不打算用这个版本把 PHP 的世界搞个天翻地覆。为 PHP4 编写的脚本中,基本不作修改就可在 PHP5 下工作。最大的变化是在 PHP5 中对对象的处理不同了。当您在 PHP5 中新建一个对象时,现在将默认地获得到该对象的一个引用,您可以到处传递它,而无需像您在 PHP4 中必须做的那样显式地声明您想通过引用来传递对象。在 PHP5 中,如果您想实际获得对象的一个拷贝,那么您需要“克隆”它。

  更长远地看,有人在研究 Parrot 引擎的使用。Parrot 是作为 Perl6 后端的引擎编写的,但它实际上是一个与语言无关的通用脚本引擎。如果各种脚本语言可以全部在单个后端引擎上达成一致,然后这个引擎可以用作公共扩展和更好的语言交互的基础,那么这将非常有趣。

  此外还有其它一些人在研究通过 JSR 223 的 Java 连通性,认为 Java 是可以成为脚本语言的单个通用后端。

  不管 PHP 可能拥有怎样的一个未来,有一点是不变的。我们将继续和许多人似乎沉迷于其中的复杂性作斗争。最复杂的解决方案很少是正确的解决方案。我们为解决 Web 问题而提供的专一、直接的方法正是使 PHP 从一开始就与众不同的地方,当我们周围的其它解决方案似乎变得越来越庞大、越来越复杂时,我们正努力地简化和优化 PHP 和它解决 Web 问题的方法。