Quick viewing(Text Mode)

Symfony、Cakephp 和zend Framework 简介

Symfony、Cakephp 和zend Framework 简介

第 章 1 、CakePHP 和 Zend Framework 简介

本章主要内容: ● 框架简介 ● 主流 PHP 框架简介 ● 设计模式

我们都知道,所有 Web 应用程序都有某些共同之处:用户都能够进行注册、登录和交 互。交互通常通过已经验证的安全形式实现,结果存储在各种数据库中。Web 应用程序先 搜索数据库,处理数据,再根据用户地址提供数据。如果将这些模式提取为某种类型的抽 象,然后将这些抽象传送到更多应用程序中,那么可以加快开发过程。 我们显然可以做到这一点,而且能够以多种不同方式使用几乎所有编程语言做到这一 点。正因为如此,许多解决方案都能够使 Web 开发更方便快捷。本书提供 3 种解决方案: Symfony、CakePHP 和 Zend Framework。这 3 种解决方案不仅能够大大加快开发过程,而 且能够提供 Web 2.0 应用程序必不可少的许多高级功能。

1.1 Web 应用程序框架的定义及其用法

Web 应用程序框架是归入某种特定的体系结构中的一批源代码,可用于快速开发 Web 应用程序。可以认为框架是一种半成品的应用程序,人们可以依据需求将它们扩展为成品。 有人认为这意味着任务已经完成了一半,但在某种程度上说这只是一种美好的愿望,因为

PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

这项工作是以特定方式完成的,缺乏监督机制。 所有框架要么都包含一种编码方法以及一些命名和结构约定,要么如果要摆脱这些限 制,就需要用户自己重新配置。这样做将会降低灵活性,或者让学习这些方法的过程变得 更加困难。如果确实希望避免这些问题而使用类似于库的方法,那么不得不牺牲开发速度。 因此所有框架都是一种折中。 因此最好先了解多种框架并比较它们之间的差异。不管怎样,也许其中某种框架能够 提供更合适的约定。也许可以使用某种初始配置实现快速灵活的开发。也许你只是想要一 个功能强大的组件库,由你自己将它们链接起来?选择权在于你,如果能够避开这些框架 的弱点,那么将充分享受它们的最大好处:真正实现快速开发。 框架的优点还包括代码简洁,能够尽量降低编程错误所带来的风险。框架遵循“不重 复自己”(DRY)的原则,也就是说在一个地方只对所有逻辑进行一次编码。该规则禁止复 制代码,特别是复制再粘贴。这有利于代码维护,能够防止出现严重错误。一般来说,框 架提高了代码的可重用性,提供了良好的编程实践,对于没有足够专业知识或纪律来关注 代码质量的编程人员而言,这非常重要。 另一个重要功能是干净有组织的链接外观(可以使用 URL 重写实现),大多数框架支持 这种功能。例如,不是输入/animals.?species=cats&breed=mainecoon,而是输入/animals/cats/ mainecoon。后者不仅看起来更简洁,而且有利于搜索引擎优化(Search Engine Optimization, SEO)。 1.1.1 框架与库

库与框架的主要区别在于: ● 从代码调用库 ● 框架调用代码 换句话说,应用程序的框架是可以填充功能的骨架,或者是一个可以在上面构建模块 的平台。而库则是在你自己构建的平台顶部提供附属模块。有些人认为框架比库更好更完 整,因此经常滥用“框架”这个术语。因此也有人将某些库称为框架,即使它们没有调用 开发人员的代码。将一块代码称为库没有错,因为它只是不同的实体而已。也有一些不好 的框架损害了好框架的名声——基本上是发布半成品的应用程序,然后将它称为框架。这 两种软件组大相径庭,不要将其混淆。 框架利用的应用程序体系结构称为控制倒置(inversion of control),因为相对于一般过 程化编程来说,它的数据流是颠倒的。这也称为好莱坞原则:“不要给我们打电话,我们会 打给你”,它与调用开发人员代码的第三方代码相符。这么做的主要原因是让高级组件减少 对其子系统的依赖。高级组件将控制传递给低级组件,低级组件自己决定如何运行与何时 响应。例如,命令行程序与包含用户界面窗口的程序就不相同。命令行程序停下来要求用 户输入;而在包含用户界面窗口的程序中,用户只须单击按钮,再由窗口管理器调用程序。

2 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

有些框架(例如 Zend Framework 或 CodeIgniter)遵循松散耦合的体系结构,也就是说, 它们的组件彼此很少相互依赖,甚至可以单独使用,这更像库的风格。虽然松散耦合的框 架提供的开发速度不及紧密耦合的体系结构和模型-视图-控制器(Model-View-Controller, MVC)模式所提供的速度,但这种方法更加灵活,代码的可控性也更强。 1.1.2 使用框架的时机

框架并不能解决所有编程问题。抛开今天开发的狂热状态,你应该还记得几年前是如 何创建框架的。当时大多数框架几乎可以说是无法有效利用的垃圾,人们创建这些框架只 是为了加速开发过程,根本没有关注文档、优雅性、易用性或代码的可读性。另一群人使 用了这些代码,并给它们填充了一些与原始代码几乎不相符的其他功能。为了便于使用, 必须清除代码,但这不是完全重写,也不是将代码包装到其他包装类里,因为这样只会进 一步增加代码的复杂性。 当然,今天框架的杂乱无序不像以前那样明显,因为代码的质量已经大大提高。但这 既是大多数框架存在性能问题的原因,也是不易掌握它们的原因,同时是新框架掩盖旧框 架弱点的原因。最后,这也是主流框架提供全新 2.0 版本的原因,这个版本解决了前面提 到的所有问题。

1. 优点

在下列情况下适合使用 Web 应用程序框架: ● 几乎包含动态内容的所有标准项目,例如社交网、联机商店、新闻门户网站等。 ● 易于扩展的应用程序,这些应用程序可以从头发展为全球普及服务,而不需要对代 码做大的变更。 ● 生成连续的应用程序,其中代码(例如控制器和视图)的模块性和可重用性都非常 有用。 ● 包含最后期限、员工流动和不固定客户的现实开发。 ● 如果你是专业 Web 开发人员或者希望成为专业 Web 开发人员,就必须学会如何使 用框架。 这些情况适用于大多数商业 Web 应用程序,这些应用程序连接到数据库,并允许用户 创建和修改数据库中的内容。因此,在 Web 开发领域中,使用 Web 应用程序框架编程已 成为标准实践。

2. 缺点

在下列情况下应该考虑不使用框架进行开发: ● 不包含用户创建内容的纯粹提供信息的网页,例如包含奇妙图形的艺术作品。 ● 包含有限数据库连接的小项目,它们不能从框架的代码生成中受益。

3 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

● 需要提供高性能的大项目,例如 Google 套件(可以使用经过编译的编程语言,而不 是 PHP)。 ● 需要提供最好的性能却只包含有限硬件资源的项目(因为现在编程成本总是高于硬 件成本)。 ● 专业或实验性应用程序,它们可能涉及完全未知的方向或使用某些自定义解决方 案,例如用于科学试验的界面(包含面向对象数据库)。 ● 当真正需要(并能提供)完全控制代码和应用程序的演变时。 ● 当你想创建一个 Web 应用程序,而你的合伙人不想或者甚至不会使用框架时。 这些条件通常体现在以下 3 种类型的项目中:小型静态网站、非常专业的网站和失败 的网站。创建框架是为了使用标准体系结构开发常见的 Web 应用程序。当然,可以使用插 件或模块来扩展它们,但是完全更改其结构则要付出艰辛努力,而且需要不断检查它们的 功能与项目的设计需求是否一致。 1.1.3 PHP 与其他编程语言

多年以来,PHP 一直是一种非常普及的编程语言,但人们普遍认为它不够专业。墨守 陈规的 PHP 开发人员都是生产廉价、低质量代码而又未受过良好教育的自由人。专家们推 荐使用 、ASP 或者各种 技术。2005 年,Ruby 得到了迅猛发展。每个人对这种编 程语言的优雅性都惊叹不已;用 Ruby 语言编写的软件 被认为是最完美的 Web 应用程序框架。不久,Ruby on Rails 的克隆版开始涌现,同时也出现了 Python 的 和 Turbogears 以及各种 PHP 框架。 PHP5 于 2004 年发布,这是一种面向对象的简洁编程语言。如果有人写的老式 HTML 仍然混合了一些 PHP 脚本片段,那么这只是他自己的选择,并不是编程语言的问题。一段 时间之后,人们才逐渐将 PHP 作为一种规范的专业工具使用。PHP 和现代 MVC 范例以及 其他框架功能一起工作,成为了开发 Web 应用程序的首选语言。 多年之后,Ruby on Rails 出现了许多限制。其中一个重要限制就是低可用性和 Ruby 托管的高成本,而 PHP 有许多廉价的托管。有许多团体希望开发早期的 PHP 框架。这导 致了一场 IT 革命,这场革命颠覆了 Ruby on Rails 作为最受欢迎框架的地位,而用 PHP 框 架取而代之。 图 1-1 显示的是各种框架在 Google 搜索引擎的 Computers & Electronics 类别中搜索量 随时间的变化情况。该图是使用 Google Insights for Search 创建的,它是有名的 Google Trends 工具的高级形式。可以在 www.google.com/insights/search/网站上自己搜索这些条目, 查看 2011 年的搜索结果。

4 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

图 1-1 不同编程语言中框架的搜索量

1.2 开源 PHP Web 框架

另一个需要回答的问题是为什么选择这 3 种特定框架。它们是否真的更好,还是我们 有偏见,又或者是推广它们有某种商业利益?先回答最后一个问题:我们是完全独立的开 源拥护者,我们只比较免费(就像免费演讲一样)软件,因此我们背后没有利益集团,也没 有人告诉我们应该选择哪种框架。下面几节将回答它们是否比其他框架更好这个问题。

之前曾经也有过闭源 PHP 框架,但由于免费框架取得了广泛成功,现在闭 源框架已经成为了过去。

1.2.1 公众关注的框架对比

我们之所以选择 Symfony、CakePHP 和 Zend Framework,是因为它们在 Web 开发团 队中非常普及,并且我们有使用 PHP 的经验。我们认为,开源编程工具在普及度和质量之 间至少有某种相关性,只有它们有用,人们才会使用。就这一点而言,它们不同于专有软 件或流行音乐,专有软件或音乐可能通过迅猛的市场营销来实现普及性,从而轻易地取代 人们对质量的关注。

5 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

事实证明可以更为客观地反映公众对 Web 框架的兴趣。图 1-2 显示的是 Google Insights for Search 中不同 PHP 框架的搜索量。从中我们可以看出主要有 4 个竞争者。其他 Web 框 架加起来还不及这 4 个框架中的一个。Lithium 和 Prado 框架直接被省略了,因为它们的名 称不是唯一的,容易产生混淆。我们在特定类别中搜索过这两个名称,发现它们并不是重 要的搜索项。

图 1-2 不同 PHP 框架的搜索量对比

用户搜索与框架相关的信息时,结果通常是各种博客和论坛上的讨论情况,以及关于 如何学习这种技术的有关条目和关于如何使用这些框架开发应用程序的相关条目。由此我 们可以看出公众关注的是如何真正长期使用某种 Web 框架。 对于我们而言,真正有争议的是 CodeIgniter 框架。关于是否将它作为主要框架之一, 我们争论了很长一段时间。也许现在它的搜索频率与 Symfony 和 CakePHP 框架相当,但 更重要的是图 1-2 中下面的区域,它反映有多少人找到了答案并可能在他们的项目中使用 这种方法。 当然,图 1-2 中的曲线图只反映搜索量,就这种增长曲线来看,很难区分长期趋势和 临时现象。我们知道 CodeIgniter 框架非常好,因此它绝对不只是一种潮流,也许一两年之 后,它将在主要 Web 工具中占有一席之地。 最后我们决定讨论 3 种框架,但我们没有完全放弃对 CodeIgniter 框架的讨论,在附录 B 中我们将对它的功能进行描述,其中也将介绍 Lithium 和 框架,在该附录中还将 使用每种框架开发一个简单的应用程序。

6 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

1.2.2 3 种框架概述

框架概述几乎没有给我们提供关于框架功能的任何信息,它们的网站上也只有营销性 描述,在它们的网站上只是列举了这 3 种框架的某些相似功能: ● Symfony 框架是一种全栈框架,是一种用 PHP 语言编写的关联类库。它为开发人 员提供了一种体系结构以及一些组件和工具,以方便他们更快地构建复杂的 Web 应用程序。选择 symfony 框架能让你更早发布应用程序,托管、升级以及维护它们 都没有问题。Symfony 框架并不是完全重新创造的,而是基于经验的:它使用大多 数 Web 开发的最佳实践,同时集成某些第三方库。 ● CakePHP 框架是一种快速开发框架,它为开发、维护和部署应用程序提供了一种可 扩展的体系结构。通过在配置范例约定内使用常见的设计模式(例如 MVC 和 ORM), CakePHP 框架可以降低开发成本,让开发人员写更少的代码。 ● Zend Framework 是 PHP 的扩展,它基于简单性、面向对象的最佳实践、公司友好 的许可和经过严格测试的敏捷代码库。Zend Framework 致力于构建更安全、更可靠、 更现代的 Web 2.0 应用程序和 Web 服务。 你是否能够说出这三者之间的不同?网站并没有提供这些框架功能的信息。在各种博 客和论坛上虽然可以找到一些信息,但缺少经过验证的数据,而且一般讨论倾向于交流个 人观点。 这就是我们写这本书的原因。实际上,框架之间的不同并不明显,需要用一段时间对 它们进行验证,也需要用一些实际示例来进行说明,然后才能推广为商业解决方案。下面 从最基础的内容开始。

1. Symfony

开始时间:2005 年 许可证:MIT PHP 版本: ● Symfony 1.4: PHP 5.2.4+ ● Symfony 2.0: PHP 5.3+ 其徽标如图 1-3 所示,网址是 www.symfony- project.org。 图 1-3 Symfony 徽标 Symfony 框架由一家名为 Sensio Labs 的法国 Web 开发公司创建,开发人员是 Fabien

Potencier。它最初用于开发该公司自己的应用程序,2005 年发布为一个开源项目。其名称 是“symfony”,但有时为了醒目起见而将首字母大写(本书就是这么做的)。 Symfony 框架基于古老的 Mojavi MVC 框架,必然也受到 Ruby on Rails 的一些影响。 它集成了 Propel 对象-关系映射器,并且将 YAML Ain’t 标记语言(YAML Ain’t Markup Language,YAML)系列标准用于配置和数据建模。默认的对象-关系映射(ORM)解决方案后 来演变成了 Doctrine。

7 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

今天的 Symfony 框架是主要 Web 框架之一。它有一个非常活跃的大型社区,还有许多 文档——主要是免费的电子书。2010 年末发布的 Symfony 2.0 提供了更多新功能,性能也 大大增强。

2. CakePHP

开始时间:2005 年 许可证:MIT PHP 版本:4.3.2+ 其徽标如图 1-4 所示,网址是 http://cakephp.org。 2005 年,波兰 Web 开发人员 Micha Tatarynowicz 提出了 CakePHP 框架。受 Ruby on Rails 的影响,CakePHP 框架是一个 完全由社区驱动的开源项目,其主要开发人员是 Larry Masters(也

称为 PhpNut)。下一个版本的 CakePHP 框架已经宣布,但发布日 期尚未确定。 图 1-4 CakePHP 徽标 CakePHP 框架最重要的特性是其友好性、开发速度和易用性,在这些方面它的确胜人

一筹。该框架开箱即用,无须配置。其文档非常完美,并且包含很多用于说明大部分功能 的运行示例。它的确有许多好的功能,支持使用更少数量的代码来实现更快的开发速度。 CakePHP 框架最有争议的功能之一是它与 PHP4 兼容。它曾经允许部署在不支持 PHP5 的主机上,现在这变成了阻碍 CakePHP 框架发展的障碍。幸运的是,版本 2.0 将使用 PHP 5.3+。还有一些报告提到了 CakePHP 框架的不良性能,但这些不良性能主要是由默认的无 效缓存造成的。

3. Zend Framework

开始时间:2005 年 许可证:新 BSD PHP 版本:从 ZF 1.7.0 后使用的是 PHP 5.2.4 其徽标如图 1-5 所示,网址是 http://framework. zend.com。 Zend Framework 由一家美国-以色列合资公司 Zend Technologies Ltd 创建。这家合资公司由 联合创建,这两个人是 PHP 的核心开发 人员。Zend Technologies Ltd 的战略伙伴包括 Adobe、

IBM、Google 和 Microsoft。该公司还提供各种商业产品; 然而,Zend Framework 是在“公司友好”的新 BSD 许 图 1-5 Zend Framework 徽标 可下发布的开源项目。

ZF 是简单的、基于组件的和松散耦合的,这意味着它是一个可以随意使用的组件库, 可以选择使用 MVC 体系结构。这样能够降低学习难度,增加灵活性。因为这种框架全面

8 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

面向对象,而且经过单元测试,所以文档非常优秀,源码质量非常高。Zend 也宣布即将发 布 2.0 版本,但发布日期尚未确定。 1.2.3 其他框架

PHP 框架有几百种。如果你仔细数一下,就会发现这个数字并不夸张,这些包括原来 的和已经取消的项目,以及新启动的项目和一些无用的短期项目。Web 应用程序市场是一 个庞大的市场,而 PHP 工具的数量也很多,在某种程度上说甚至是过多。下面简单介绍一 些比较有名的框架,有人已经使用这些框架成功开发出一些 Web 应用程序。

1. CodeIgniter

开始时间:2006 年 许可证:修订后的 BSD PHP 版本:4.3.2+ 其徽标如图 1-6 所示,网址是 http://codeigniter.com。 CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责 开发和维护。它虽从小处着眼,但对性能提升很大。它部分采 用 MVC 模式,因此模型是可选的。这种框架是松散耦合的, 用 的话说就是“它最不像框架”。其轻量级方法 在开发人员社区中赢得了广泛认同,但有时也有人批评它与 PHP4 兼容。 对于需要使用某种框架而又不太复杂的 Web 应用程序而 图 1-6 CodeIgniter 徽标 言,CodeIgniter 框架是一个不错的选择;而对于更复杂的 Web 应用程序 ,因为功能过多, 或者因为需要花费过多时间来进行配置,所以使用该框架将影响应用程序的性能。 CodeIgniter 框架结构简单,因此许多初学者都把它作为学习完整 MVC 框架之前的学习平台。

2. Lithium

开始时间:2009 年 许可证:BSD PHP 版本:5.3+ 其徽标如图 1-7 所示,网址是 http://lithify.me。

图 1-7 Lithium 徽标

Lithium 框架借鉴了 CakePHP 的所有优点,并将它移植到 PHP 5.3 上。最初它是 CakePHP 的一个分支,名为 Cake3,现在它是一个由 CakePHP 以前的开发人员运行的单独

9 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

项目。它是轻量级的,非常快速和灵活,并且广泛支持插件。同时,它还具有许多实验性 和创新性的功能,例如过滤器系统和集成测试套件。 在 Google 中搜索“Lithium 框架”,显示的第二个搜索结果就是标题为“CakePHP is dead…Lithium was born”的页面。然而这种说法并不准确,因为 Lithium 框架支持 PHP 5.3, 凭借这一优势,Lithium 框架将来也许真的会超过 CakePHP,除非 CakePHP 立即采取行动。

3. Agavi

开始时间:2005 年 许可证:LGPL PHP 版本:5.2.0+(推荐使用 5.2.8+) 其徽标如图 1-8 所示,网址为 www.agavi.org。 Agavi 框架和 Symfony 框架一样,都是基于 Mojavi 框架的。该 框架于 2005 年启动,直到 2009 年早期才发布 1.0.0 版。它的源代 码非常完美,因此有时也称为写得最好的 MVC OOP 框架。然而, 由于缺乏文档,这种框架并没有得到普及。 这种框架原本就没有打算普及。它的作者们强调说 Agavi 框架

不是构建网站的工具箱,而是一种具有强大性能和可扩展性的重要 框架,其目标应用程序是需要完全控制开发人员的长期专业项目。 图 1-8 Agavi 徽标

4.

开始时间:2007 年 许可证:BSD PHP 版本:5.2.3+ 其徽标如图 1-9 所示,网址为 http://kohanaphp.com。 Kohana 框架是 CodeIgniter 支持社区的一个分支。与

CodeIgniter 相比,Kohana 框架是为 PHP5 而设计的,并且完全 图 1-9 Kohana 徽标 面向对象。虽然其代码以更简洁著称,但它还是拥有 CodeIgniter 的全部特性。它是轻量级的,非常灵活,容易掌握。Kohana 框架背后的社区非常庞大,也 非常活跃,因此虽然这种框架还很年轻,但它是一种稳定可信赖的框架。

5. Prado

开始时间:2004 年 许可证:经过修订的 BSD PHP 版本:5.1.0+ 其徽标如图 1-10 所示,网址是 www.pradosoft.com。 Prado 是面向对象的 PHP 快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称。一段时间以前,它还比较普及, 图 1-10 Prado 徽标

10 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

但现在发展有些迟缓。然而,它仍然是一种适用于大多数商业应用程序的成熟框架。它最 有意义的功能之一是非常支持事件驱动编程,并且与 ASP.NET 也有一些相似之处。

6.

开始时间:2008 年 许可证:BSD PHP 版本:5.1.0+ 其徽标如图 1-11 所示,网址是 www.yiiframework.com。

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建,并且延续了它的许多约定。Yii 框架是一种非常快 速(超过大多数基准)、可扩展、模块化且严格面向对象的框架。它的功能众多,文档完美。 它没有使用特殊配置或者模板语言,因此如果要使用它,那么除了面向对象 PHP 之外,不 需要学习其他任何语言。和其他许多框架不同,它遵循纯 MVC 体系结构,将数据直接从 模型(Model)发送到视图(View)。

7. Akelos

开始时间:2006 年 许可证:LGPL PHP 版本:4 或 5 其徽标如图 1-12 所示,网址是 http:// www.akelos.org 或 http://github.com/bermi/akelos。

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails 的影响,而 Akelos 框架首当其冲。它关注国际化(提 供多语言模型和视图,以及没有扩展名的 Unicode 支持),可以在廉价的共享托管主机上运 行(这也是它支持 PHP4 的原因)。 Akelos 框架的作者们已经宣布完全重写的 Akelos 2。它不再支持 PHP4,并使用自动 加载和更惰性的策略来加载功能。它的特点是高级路由方法和强大的 REST 定向(第 12 章 将详细介绍 REST)。这种框架将在 2010 年后期发布,值得期待。

11 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

8. Seagull

开始时间:2001 年 许可证:BSD PHP 版本:4.3.11+ 其徽标如图 1-13 所示,网址是 http://seagullproject.org。 在所有 PHP 框架中,Seagull 资格最老—— 它创建于 图 1-13 Seagull 徽标 2001 年。多年的开发使得它更稳定、更可靠和更经得起测试。开发人员不会再积极地开发

它,因此也许在开发新项目时,它不是最佳选择,但还是有很多成功的应用程序使用它。 它对其他所有 PHP 框架的发展都做出了很大贡献。

9.

开始时间:2005 年 许可证:MIT PHP 版本:5.x 其徽标如图 1-14 所示,网址是 www.qcodo.com。 Qcodo 框架是一种 MVC 框架,在代码生成方面胜过数据

库设计。它有一个功能强大的代码生成器,能够分析数据模型 的结构,并为数据库操作创建 PHP 对象代码和 HTML 页面。 图 1-14 Qcodo 徽标 也许这种框架不是最普及的框架之一,但有些顶级机构(包括 NASA)都在项目中使用它。

Qcodo 框架最初由 QuasIdea Development 的 Mike Ho 开发,现在由一个活跃的社区开发。 它还有一个完全由社区驱动的分支 Qcube。

10. Solar

开始时间:2005 年 许可证:新的 BSD PHP 版本:5.2+ 其徽标如图 1-15 所示,网址是 http://solarphp.com。

图 1-15 Solar 框架徽标

SOLAR 是简单对象库和应用程序仓库(Simple Object Library and Application Repository) 的简称。它的结构和命名约定与 Zend Framework 类似。最大的不同点之一是构造对象的方 法—— 都使用统一的构造函数创建,使用配置文件中的数组进行配置。它还有许多有用的

12 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

内置示例应用程序。

11. PHP on Trax

开始时间:2007 年 许可证:GPL PHP 版本:5.x 其徽标如图 1-16 所示,网址为 www.phpontrax.com。

图 1-16 PHP on Trax 徽标

顾名思义,这种框架完全是 Ruby on Rails 的 PHP 副本。至少原本打算这样,但因此 它仍然缺少许多功能,所以最终很可能会无法实现这个目标。它是看起来很好但最终还是 失败的框架之一。

1.3 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送,从而能够加快开发过程。本节将详细介 绍这些抽象化概念以及它们创造 Web 应用程序框架的方法。 使用框架之前不一定要理解设计模式,因此如果你觉得本章没有意义,那么可以跳到 下一章。然而,对于框架和应用程序开发整体而言,设计模式还是非常重要的,因此我们 认为,如果现在跳过本节,以后还会回到这里。 1.3.1 设计模式的定义

设计模式的定义是:它是软件设计中常见问题的通用解决方案。其实没有规范的基础, 因为设计模式通常是一种实践方式,用以弥补规范机制的缺失。当编程语言不能提供抽象 机制,而这些机制在现实应用程序开发过程中又非常有用时,通常会创造设计模式。 设计模式与国际象棋游戏类似,初学者只要知道规则就行了,类似于学习编程语言的 基本语法。当然,知道“象”如何移动并不能使你成为优秀的象棋选手,就像知道如何用 括号并不能使你成为 PHP 程序员一样。熟练的选手能够预测下面几步,再运用相应的取胜 方案,就像经验丰富的编程人员能够创造工作软件一样。 随着对象棋游戏的进一步熟悉,你就会发现有一些模式。你只用看一眼棋盘就知道这 种情形属于哪种模式,然后依据现在的情况和未来的风险做出反应。你可以直观地理解这

13 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

些模式,也可以命名它们。这与软件设计模式很类似:如果你非常熟悉这些模式,那么可 能会一直使用它们,有时也会在毫无察觉的情况下使用它们。 虽然不一定要命名设计模式,但命名有两个好处。首先,它有助于认识模式,因为在 命名抽象事物之后,在实践中才更容易实现它。然后可以进一步分析这种模式,画出它的 示意图,并充分利用这种模式。另一个好处是这么做能够共享经验。象棋选手喜欢讨论各 种开局和第一着棋,编程人员也可以通过交流设计模式来相互学习。 更重要的是,如果要其他编程人员为固定类添加一些功能,并告诉他使用 Decorator 模式,那么可以用你确定的方式(而不是使用随机解决方案)来实现。设计模式具有防止未 来出现问题的巨大潜力。 1.3.2 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式,但 MVC 绝对是所有框架的结构主 干,其主要思想是将应用程序划分为 3 层: ● 模型层:表示应用程序的业务逻辑。它不仅仅是原始数据;模型层必须表示数据结 构的关系和依赖性。它可能包含一个或多个类,这些类对应应用程序的逻辑对象, 并提供一个用于操作它们的接口。模型层是唯一使用永久存储的层。它完全封装所 有数据库的连接。当其内部状态发生改变时,模型层应该通知视图层,以便刷新视 图层。 ● 视图层:显示给用户的输出。最重要的是,视图层从不修改应用程序的数据,它只 显示数据。对于相同的数据,可能有多个视图层,例如传统的 HTML、PDF、Flash 或移动设备的 WML。这些视图层应该可以互换,而不用修改其他层。 ● 控制器层:应用程序负责处理用户交互和采取其他动作的部分。控制器层应该很简 单—— 它是控制部分,使用模型层和视图层提供的方法;它自己不做任何事情。 图 1-17 说明了这 3 层之间的关系。

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP MVC 是一种比较老的设计模式,时间可以追溯到 1979 年 Trygve Reenskaug 的著作

14 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

Applications Programming in -80:How to use Model–View–Controller。从那时起它 就经常在非 Web 应用程序中使用,这些应用程序大多数是用像 C++或 Java 这样的编译语 言写的图形用户界面。实现具体的 MVC 模式很容易也很自然,但对于 Web 应用程序,需 要对它稍做修改。 如图 1-18 所示,模型-视图-表示器(Model-View-Presenter,MVP)是 MVC 的派生物。 它是一个 3 层的应用程序结构,其中表示器层是视图层和模型层之间的中间层。表示器与 控制器不同,它从模型层加载数据,然后将数据传递给视图层。 大多数所谓的 MVC 框架都遵循 MVP 模式。这本身没有错,因为 MVP 似乎更适合, 但是这种命名约定可能会产生混淆。只要 MVP 直接来源于 MVC,这就不是什么大问题, 因此本书沿用框架作者取的这些名称。我们可以将所有框架都称为模型-视图-控制器,即 使由控制器层完成主要的数据传递工作。

动作 数据操作与传递

表示器层

更新 模型层

视图层

结果

图 1-18 模型-视图-表示器模式

1.3.3 其他设计模式概述

设计模式可以划分为创造模式、行为模式和结构模式 3 种。完整描述所有这些设计模 式超出了本书的范围,在下面这本非常有影响力的书中可以找到相应描述:Design Patterns: Elements of Reusable Object-Oriented (作者:Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides)。下面简要介绍 Web 框架中常用的一些设计模式。

1. Singleton

这种设计模式通常称为反模式,它很普通但非常有用。Singleton 模式的目的是确保类 只有一个实例,并且让这个实例在全局范围内可访问。当另一个对象需要访问 Singleton 模 式时,它将调用一个全局可访问的静态函数来返回对单个实例的引用。图 1-19 显示的是 Singleton 模式的结构。

图 1-19 Singleton 模式的结构

15 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

Singleton 模式的诀窍是让该实例及其所有构造函数私有化,从而无法创建 Singleton 类的实例。那么如何创建第一个也是唯一一个实例呢?首先,instance()方法检查对象是否 存在;如果不存在,那么在返回它之前创建一个实例。下面看看使用 PHP 代码如何实现:

make;} function getModel() {return $this->model;} function getMakeAndModel() {return $this->getMake().' '.$this->getModel();} } ?>

codesnippet/singleton/CarSingleton.class.php

上面代码中的类是一个 Singleton 模式,表示汽车租赁公司 Dodge Magnum 的一个具体 的汽车样本。__construct()函数是这个类的构造函数。请注意,将它设置为 private 以防止 从类外部使用它。双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数) 之一,在类中声明构造函数将重写默认的构造函数。 CarSingleton 提供一个界面,表示租车和还车以及租车者。rentCar()函数首先检查汽车 是否已经租出。这不是 Singleton 模式的一部分,但对于本示例的逻辑非常重要。如果汽车 没有租出,那么该函数在返回之前先检查$car 变量是否为 NULL。如果该变量等于 NULL, 那么在首次使用之前先构建它。因此,rentCar()函数对应设计模式的 instance()方法。 下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人。他可以租车(这里只 有一辆车)、还车、了解正在驾驶的汽车的制造商和型号。

16 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

rentedCar = CarSingleton::rentCar(); if ($this->rentedCar == NULL) { $this->drivesCar = FALSE; } else { $this->drivesCar = TRUE; } } function returnCar() { $this->rentedCar->returnCar($this->rentedCar); } function getMakeAndModel() { if (TRUE == $this->drivesCar ) { return 'I drive '.$this->rentedCar->getMakeAndModel().' really fast!'; } else { return "I can't rent this car."; } } } ?>

codesnippet/singleton/Customer.class.php

可以用下面的代码来测试这些类。该代码创建两个客户,他们同时要租车。但第二个 客户只有等到第一个客户还车后才能租到车。

echo 'Customer_1 wants to rent the car.
'; $Customer_1->rentCar(); echo 'Customer_1 says: ' . $Customer_1->getMakeAndModel() . '
'; echo '
'; echo 'Customer_2 wants to rent the car.
'; $Customer_2->rentCar(); echo 'Customer_2 says: ' . $Customer_2->getMakeAndModel() . '
'; echo '
'; $Customer_1->returnCar(); echo 'Customer_1 returned the car.
'; echo '
'; echo 'Customer_2 wants to rent the car. Again.' . '
';

17 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

$Customer_2->rentCar(); echo 'Customer_2 says: ' . $Customer_2->getMakeAndModel() . '
'; echo '
'; ?>

codesnippet/singleton/Test.php

输出如下所示:

Customer_1 wants to rent the car. Customer_1 says: I drive Dodge Magnum really fast!

Customer_2 wants to rent the car. Customer_2 says: I can't rent this car.

Customer_1 returned the car.

Customer_2 wants to rent the car. Again. Customer_2 says: I drive Dodge Magnum really fast!

Singleton模式通常用于其他设计模式中,例如Prototype、State、Abstract Factory或Facade。 除此之外,还可以在所有需要全局访问的单个实例的类中使用它,但无法将它指派给其他 对象,也许还可以从首次使用时的初始化中受益。请注意,很容易过度使用 Singletons 模 式,这样很危险,就像过度使用全局变量一样。使用 Singletons 模式的另一个问题是在程 序的整个执行过程中它们都保持状态不变,这样会严重影响单元测试。有些专家甚至说 Singleton 模式不是一个好模式,应该避免使用它。 但框架使用 Singleton 模式有多种原因。原因之一是出于安全的目的存储用户数据。需 要一个实例来保存用户验证数据,并且确保不能创建其他实例。例如,Symfony 的 sfGuard 类就使用这种方法。

2. Prototype

当需要灵活创建参数化对象但又不想使用 new 操作符时,就可以使用 Prototype 模式。 使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类,这样就可以创建对象。每 个子类都包含一个实例化的 Prototype 对象,调用新实例时,它复制本身。这样就很容易灵 活地创建对象—— 不需要在代码中对具体子类名称进行硬连接,而且可以作为字符串或者 对相应 Prototype 的引用来传递类的名称。 这种模式还非常支持对象的深度复制。这种复制不是复制 Prototype,而是复制现有对 象,然后接收该对象的副本作为结果。甚至还可以从包含各种子类的混合对象的容器中复 制对象。唯一要求是它们要实现 clone()接口。用这种方法复制对象比使用 new 操作符并指 定值来创建对象更快。这种模式的通用示意图如图 1-20 所示。

18 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

图 1-20 Prototype 模式的结构

PHP 还有另一个魔法函数:__clone()能够完成大多数任务。在下面的示例中,唯一要做 的就是为不同生产者创建一个抽象的 CarPrototype 类和子类。__clone()函数声明为 abstract 类型,因此当调用这种方法时,默认使用子类方法。

model; } function getColor() { return $this->color; } function setColor($colorIn) { $this->color= $colorIn; } } class DodgeCarPrototype extends CarPrototype { function __construct() { $this->model = 'Dodge Magnum'; } function __clone() { } } class SubaruCarPrototype extends CarPrototype { function __construct() { $this->model = 'Subaru Outback'; } function __clone() { } } ?>

codesnippet/prototype/CarPrototype.class.php

19 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

汽车就是非常好的示例,因为在现实中制造商创建原型,然后基于这种原型创建不同 的模型,再填充特有功能。下面的代码用于测试前面的类。首先,它创建两个 Prototype 对 象作为展示的汽车,然后复制其中一个对象来满足客户,并可通过统一的界面来挑选颜色。

'; $customerDecision = 'Subaru'; if( $customerDecision == 'Subaru' ){ $customersCar = clone $subaruProto; } else { $customersCar = clone $dodgeProto; } echo $customersCar->getModel().'
'; echo 'What color do you want?
'; $customersCar->setColor('red'); echo 'Fine, we will paint your '.$customersCar->getModel(). ' '.$customersCar->getColor().'.
'; ?>

codesnippet/prototype/Test.php

前面的代码将得到下面的消息:

Which car do you want? Subaru Outback. What color do you want? Fine, we will paint your Subaru Outback red.

通常在框架的不同模块中使用 Prototype 模式。例如,可以在 Symfony 或者 CakePHP 的 AppController 类的表单中嵌套表单。

3. Decorator

子类化是一个很好的机制,但它也有一些严重的局限性。假设要生产一辆汽车,你努 力设计一种别人能买得起的汽车标准模型。这种完整的设计定义模型的观感,并且是以后 所有修改的参考。然后你可以提供一些可选配置,这些配置会给汽车增加一些新功能,从 而提高汽车的性能。例如,它可能是四轮驱动而不是前轮驱动,可能是自动档而不是手动 档。该车可能有不同的装配级别,可能包含电动皮革座椅、天窗、更好的音频系统或者 GPS 卫星导航系统。但基本界面相同—— 你可以开这辆车,并且感觉良好。 如果面临这些选择,就会大大增加可能的组合形式。图 1-21 显示 3 种改进的一些组合, 如继承层次结构所示。

20 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

图 1-21 继承层次结构

解决这个问题的答案是 Decorator 模式。Decorator 这个类与装饰类共享接口(在本示例 中,它是包含基本配置的汽车)。它封装装饰对象的实例,并动态扩展其性能。这就像把礼 物放在一个盒子里,然后用彩纸将它包装起来一样—— 虽然它仍然是一份礼物,但更耐磨、 更漂亮。Decorator 模式的继承结构如图 1-22 所示。

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中。用这种方法可以随意添加多 个可选模块。Decorator 模式也有自身的继承层次结构,在层次结构中它们能够递归封装核 心对象。 下面的代码创建一个没有可选设备的标准 Car 类。

gearMessage . ' Driving comfort is ' . $this->comfortMessage; }

21 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

} ?>

codesnippet/decorator/Car.class.php

下面的类负责扩展汽车的性能。第一个类 CarDecorator 是包装的第一层。它存储$car 变 量和$comfortMessage 变量的副本。Decorator 模式会改变这个变量,因此创建一个副本以免改 变原始的$car 对象。另一方面, $gearMessage 也在内部改变。子类化 drive()函数以使用合适 的变量$car->model 和$this->gearMessage,应该访问核心对象而不是$this->comfortMessage, 因为要用到修改后的值。 包装 CarDecorator 的第二层装饰类用来安装可选组件, 如下所示。Automatic TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中,而 GPSDecorator 安装到 CarDecorator 中。请注意,所有装饰类都共享相同接口,另外还提供具体的安装程序。

car = $car_in; $this->comfortMessage = $car_in->comfortMessage; } function drive() { return 'Accelerating. ' . $this->car->gearMessage . ' Driving comfort is ' . $this->comfortMessage; } }

class AutomaticTransmissionDecorator extends CarDecorator { protected $decorator; public function __construct(CarDecorator $decorator_in) { $this->decorator= $decorator_in; } public function installAutomaticTransmission(){ $this->decorator->car->gearMessage = 'Auto transmission shifts up.'; } } class GPSDecorator extends CarDecorator { protected $decorator; public function __construct(CarDecorator $decorator_in) { $this->decorator= $decorator_in; } public function installGPS(){ $this->decorator->comfortMessage= 'very high.'; } }

22 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

?>

codesnippet/decorator/CarDecorator.class.php

使用下面的代码来测试这些类。

'; echo $car->drive().'
'; $transmission->installAutomaticTransmission(); $gps->installGPS(); echo 'Driving fully decorated car:
'; echo $decorator->drive() . '
'; echo 'Driving the car without decoration:
'; echo $car->drive() . '
'; ?>

codesnippet/decorator/Test.php

结果如下所示:

Driving standard car: Accelerating. Remember to shift up. Driving comfort is standard. Driving fully decorated car: Accelerating. Auto transmission shifts up. Driving comfort is very high. Driving the car without decoration: Accelerating. Auto transmission shifts up. Driving comfort is standard.

首先调用基本的 car 模型。接着安装可选配置,调用 CarDecorator 的 drive()函数。最 后,选择开车,而不使用 Decorator 包装。请注意,调用$car 之后,选择的是自动档,因为 Decorator 模式永久性地改变了它。 接着讨论框架,Decorator 模式也用于其他布局和模板中。当需要添加可选视觉组件或 者需要新微件来扩展用户界面时,这种模式非常有用。例如,如果用户输入超过字段区域, 就会添加滚动条。

4. Chain of Responsibility

前面的 3 种设计模式关注的是对象创建和继承结构。Chain of Responsibility 是另一种 类型的模式,因为它应用于对象的行为。它的主要目的是分离请求的发送方与接收方。下 面用汽车示例来说明其用法。

23 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

假设道路出现紧急情况,需要马上停车。 请求 换句话说,发出了 stop 请求。在大多数情况 客户 下,踩下刹车踏板就可以了,但有时制动器 会失灵;这时 Chain of Responsibility 就派上 处理元素 用场了。如果制动器不能处理该请求,就会 处理元素 将它传递给手动制动器。如果由于某种原因 手动制动器也失灵,那么撞上障碍物之后, 处理元素 至少安全气囊应该弹出以保住乘客的性命。 对于大多数道路紧急事件而言,安全气囊是 处理元素 最常见的解决方案。虽然与专业解决方案(刹

车、避让)相比不是最好的,但如果这些解决 图 1-23 Chain of Responsibility 作为请求的响应 方案都无效,安全气囊解决方案就显得尤为 重要。这与应用程序是一样的—— 最好为请求提供一个潜在的处理程序链 (如图 1-23 所示), 而不是在它无效的时候连一条错误消息都没有。 那么该如何创建这种 Chain of Responsibility 呢?这种模式的主要思想就是通过一系列 连续的处理程序来处理请求,以避免硬件连接的映射。在处理程序链中,最初客户只保持 对第一个处理程序的引用,每个处理程序保持对下一个处理程序的引用。最后一个处理程 序必须总是接受请求,以避免将它传递给 NULL 值。 支持这种行为模式的类结构如图 1-24 所示。它由一个父 Handler 类组成,该父类通过 调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler。具体的处理程序子类 化 Handler 类,这些具体的处理程序会处理请求;如果它们处理失败,则调用它们超类的 handle()方法。

图 1-24 Chain of Responsibility 模式结构

Chain of Responsibility 通常用于过滤器,处理用户请求就是其中一个过滤的示例。首 先检查给定的控制器是否存在。如果不存在,则显示 404 error。如果存在,则将请求传递 给控制器,它进一步处理请求。它检查用户是否试图访问不安全的页面;如果是,那么它 将请求重定向到一个安全的 SSL 页面,然后检查身份验证等。

24 第 1 章 Symfony、CakePHP 和 Zend Framework 简介

5. State

有时我们希望组件根据应用程序的各种可能状态做出不同响应。首先定义一个抽象的 State 类,它是各种 ConcreteStates 的通用接口。所有状态都提供一种 handle()方法,这种方 法提供组件的各种行为。Context 类是包装 ConcreteState state 对象的核心类。如果 Context 类是提供状态独立功能的完整类,这种设计模式就非常有用。否则,子类化 Context 类可 能更有效。 在处理它自己的请求时,Context 类调用 state->handle()方法。Context 类还有一些方法 能够切换状态。依据 state 变量存放的 ConcreteState 不同,state->handle()方法提供不同的行 为。可以将它用于模拟运行时部分类型的改变。其示意图如图 1-25 所示。

图 1-25 state 模式结构

State 模式虽然很简单,但对于应用程序开发来说却非常有用,例如数据库连接—— 数 据库抽象层可能依据当前的连接状态改变行为。再例如联机商店的交易状态,应用程序可 以根据需要哪些步骤完成交易来显示不同的页面。

6. Iterator

有许多类型的集合对象,也有许多方法能够遍历它们。例如,由连续整数遍历的数组 可应用于数组运算符。如果要打印出包含 5 个元素的 myArray,那么可以使用下面的代码:

for ($i=0;$i<=4;$i++) { echo $myArray[$i]; }

但是这种解决方案并不完美。首先必须确定变量 i 的值。PHP 不是 C/C++,因此调用 myArray[100]并不是灾难性的—— 它不会从内存返回随机垃圾数据。然而它仍然很容易跳 过某些硬连接范围内的值。另一个问题是,这种方法显示了聚集的基本表示,它使得这种 遍历过程依赖具体的表示,因此不可重用。面向对象编程的目的就是封装聚集对象的内部 结构,并且只提供一个统一、安全的有用接口,就像 PHP 提供的接口一样:

interface Iterator{ function current(); // Returns the value of element under current key function key(); // Returns the current key function next(); // Moves the internal pointer to the next element function rewind(); // Moves the internal pointer to the first element

25 PHP 框架高级编程——应用 Symfony、CakePHP 和 Zend

function valid(); // Returns true if the element under current key is valid }

现在每个实现这种接口的类都可以使用 foreach 结构。下面的代码片段产生的输出与 前面的 for 循环一样:

foreach ($myArray as $value) { echo $value; }

这种机制背后的抽象化内容是 Iterator 设计模式,如图 1-26 所示。Client 应用程序能够 访问两个抽象类:Collection 和 TraversalAbstraction,前者是聚集对象接口,后者是由相应 的 Collection 创建的。对于 List 和 Map 而言,具体聚集可能不同,但可以为它们生成相应 的遍历方法。Client 调用 next()方法,为 List 和 Map 执行不同的排序算法,但在这两种情 况下,将会出现并发元素。

图 1-26 Iterator 模式结构

在 Web 框架中,Iterator 模式主要用于分页。需要一个统一界面将 Web 内容划分到足 够多的页面中,再将它们转换到单独网页中,然后通过这些页面遍历内容。

26