Hello Zend Framework!

来源:互联网 发布:淘宝上好的漫画书店 编辑:程序博客网 时间:2024/05/22 16:48
 

第二章 Hello Zend Framework!

在第二章,我们将看到一个输出“Hello World!”的简单的Zend Framework应用程序,对于普通的PHP应用程序来讲,源码应该像这样,它只由一行代码构成:
echo ‘Hello World’;
Zend Framework需要更多的文件来创建一个基础架构,因为一个完整的网站是在此架构上建立的。结果,我们的“HelloWorld!”程序的代码也许看上去徒然地絮絮叨叨。我们也将考虑如何组织在硬盘上的站点文件来确保我们能找到我们所需的并来看看ZendFramework是如何为应用程序设计一个MVC样式的。下面让我们先来看看所谓的Model-View-Controller是什么吧。

2.1 MVC设计样式

为了理解ZendFramework应用程序的工作流程,我有必要先讲一点基础理论。我们所创建的这些文件将关联到许多框架类,因此我们得首先来学习Controller基础。ZendFramework的控制器系统是MVC软件设计模式的一个具体实现,如图2-1。软件设计模式是一个通用问题的标准解决方法,这表明具体的实现会有差异,但是解决同类问题所采用的设计概念基本是一样的。MVC模式描述了一种将应用程序分离成三个部分的方式。

MVC设计模式图展示了一个web应用程序的三个主要部分以及一个分发器(dispatcher),它能找到处理相应请求的控制器(controller)

2.1.1 模型(Model)

MVC模式的Model部分在幕后工作,跟专门的应用有关,被称为商业逻辑,这部分代码决定了如何向电子订单添加运费数据或者如何取得某个客户的名字和姓氏,因此检索和存储数据的数据库是在模型层。ZendFramework利用Zend_Db_Table类来提供表级别的数据库接入,并允许轻松操纵应用程序所使用的数据。

2.1.2 视图(View)

视图是应用程序的显示逻辑部分,对于一个web应用程序来说,它通常指的就是构成页面HTML代码,当然也可能包括其它的,比如XML,它被用来构造RSS提要功能。另外,如果网站允许以CSV格式导出,导出的CSV将是视图的一部分。视图文件本身被称为模板,因为它们通常用来显示由model创建的数据。通常把更复杂的模板代码做成函数,称之为视图助手(View Helper),视图助手改进了视图代码的复用性。默认情况下,Zend框架的视图类(Zend_View)使用PHP的模板文件,但其他模板引擎,如Smarty或PHPTAL也可取代之。

2.1.3 控制器(Controller)

上面两种之外,应用程序剩下的部分是控制器。对于web应用程序来说,控制器决定如何处理web请求。ZendFramework中的控制器系统是基于前端控制器设计模式的,它利用句柄(Zend_Controller_Front)和动作命令(Zend_Controller_Action)进行协同工作。

2.2 剖析Zend Framework应用程序

一个典型的Zend Framework应用程序包含很多目录,这是为了分离程序不同的组成部分,顶层文件系统结构如图2-2:

图2-2 标准Zend Framework应用程序的文件系统
一共有四个顶层目录:
1. application
2. library
3. tests
4. web_root

2.2.1 application目录

应用程式目录中包含所有该应用程序运行所需要的代码,web服务器不能直接访问它。为了进一步分离显示、业务和控制逻辑,application目录中包含了用于存放model,view和controller文件的次级目录。根据需要,可能还会有其它的次级目录,比如存放一个名叫settings.ini配置文件的config目录。

2.2.2 library目录

所有的应用程序都使用类库,它是事先已经写好的可复用代码。在一个ZendFramework应用程序里,zend框架本身就存放在library文件夹中,其它的类库或框架像是用户自己编写的框架,数据库ORM类Propel,还有Smarty模板引擎等等也都存放于此。
类库可存储在任何应用程序能找到的地方,无论是全局目录还是本地目录。全局目录能被该服务器上的所有应用程序访问,例如/usr/php_include(对于Windows来说,可能是 c:codephp_include),我们可以使用php.ini配置文件中的include_pathsetting对路径进行默认包含。另外,每个应用程序可以在其自身目录下存储类文件,这种情况下,我们往往把类库存放在一个叫library的文件夹里,当然有时也会命名为lib,include或者inc。

2.2.3 test目录

test目录用来存放所有的单元测试代码。单元测试是用来帮助确保代码在整个应用程序生命周期中随着它的增长和变化而能继续工作。随着程序的壮大,先前编写的代码常常需要因为新功能的加入而被更改(称为refactored)。在PHP的世界里,单元测试很少被认为是重要的,但是如果你对自己的代码进行了单元测试的话,你会很感谢自己的。

2.2.4 web_root目录

为了提高web程序的安全性,从服务器里应该只能存取用户可直接访问的文件。正如ZendFramework所使用的前端控制模式,所有的web请求都要从一个单一的文件通过,这个文件通常是index.php,这个文件是唯一一个能让用户访问的php文件(译者:也就是说访问其它php文件的时候必定会事先访问index.php,这时会进行一些预处理),因此它存放在web_root目录下,其它的普通文件例如images,CSS和Javascript这些允许用户直接访问的文件也在此处设立了自己的分目录。

2.5 Hello World:File By File

(译者:我不知道怎么直接跳到2.5节了,原书上是这样的)
我们需要新建4个文件来创建我们那简单的HelloWorld应用程序:一个启动文件(index.php),一个Apache配置文件,一个控制器(Controller)文件,一个视图(View)模板,当然Zend Framework类库肯定已经在library文件里头了,最终程序会是这个样子,如图2-3:

一个最小化的Zend Framework应用程序

2.5.1 启动(Bootstrapping)

启动是指开始一个程序,在前端控制器模式中,这是唯一存在于根目录的php文件,通常就是index.php。所有的web请求都将用到这个文件,因此它被用来设置整个应用程序的环境,设置Zend Framework的控制器系统,然后启动整个应用程序。过程如代码清单2-1所示:
Listing 2.1: web_root/index.php

error_reporting(E_ALL|E_STRICT);                         #1 设置错误报告
ini_set('display_errors', true);
date_default_timezone_set('Europe/London');
$rootDir = dirname(dirname(__FILE__)); //index.php文件的上级目录的上级目录,在这个文件架构中就是根目录
set_include_path($rootDir . '/library' #2 设置默认的包含路径,PATH_SEPARATOR是分隔符,若服务器操作系统为Linux,它就是指'/',而在
. PATH_SEPARATOR . get_include_path()); #Windows系统中,它是指'',读者可以自己试着输出get_include_path(),看看能得到什么
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Debug');
Zend_Loader::loadClass('Zend_Controller_Front');
// 设置controller
$frontController = Zend_Controller_Front::getInstance(); #3 获得Zend_Controller_Front实例
$frontController->throwExceptions(true); #4 抛出错误信息,在正式产品中不要这么干
$frontController->setControllerDirectory('../application/controllers');
// 启动!
$frontController->dispatch();

让我们来看看这个文件更多的细节。这个文件大部分的工作就是初始化。起初,设置错误报告(#1)以确保所有的错误或警告能显示。PHP5.1所采用的一套新的时间和日期函数需要知道我们处在世界的哪个时区,有很多种方法来设置时区,最简便的是使用date_default_timezone_set()函数。

在编写ZendFramework应用程序时我们要求library目录被包含在php_include里,有好几种方法来做这件事。若要在整个服务器范围内都能使用library目录下的内容,最快的方法是直接在Php.ini里修改include_path设置。一个更具有可移植性的方法是(特别是当你在同一台服务器里使用了多种版本的框架时),像上面那样在启动文件里设置包含路径(#2)。

ZendFramework应用程序不依赖任何特殊的文件,然而事先装载几个帮助类还是很有用的。Zend_Loader::loadClass()是根据类的名字来包含正确的文件,它的功能是将类名中的下划线转化为目录分隔符,经过检查发现没有错误后再包含这个文件。因此Zend_Loader::loadClass(’Zend_Controller_Front’); 和include_once‘Zend/Controller/Front.php’;这两行代码将获得相同的效果。Zend_Debug::dump()会以var_dump()格式输出一段有关变量的调试信息。

Bootstrap的最后一部分设置前端控制器然后启动。前端控制器类Zend_Controller_Front实现了单入口设计模式(#3),类的定义本身意味着只能允许有一个实例对象。单入口设计模式适合前端控制的理由是他确保总是只有一个类在处理请求,这种设计导致的一种结果是我们无法使用new操作符来创建一个新对象而必须使用getInstance()静态成员函数。前端控制器有一个特点是他能捕捉所有默认抛出(throw)的例外(exception)并把它们保存在由它创造的一个响应对象(responseobject)中。这个响应对象保存所有有关针对URL请求和HTML的响应信息,它们是HTML头信息,页面内容和任何被抛出的例外。当处理完请求后,前端控制器自动发送头信息并且显示页面内容。

在我们的Hello World应用程序中,我将命令前端控制器抛出所有已发生的例外(#4)。对于刚接触ZendFramework的新手来说,在响应对象中存储例外的默认行为使人感到迷惑,因此我们把它关掉并且迫使错误信息显示出来。当然了,在一个正式产品中,你绝对不可以把错误显示给用户看,因此你应该让控制器捕捉错误信息或者用try/catch块包住index.php中的代码。

我们调用前端控制器的dispatch()函数来启动应用程序,这个函数将自动创建一个请求和响应对象来为我们encapsulate应用程序的输入输出。然后它将创建一个路由器来获得用户请求的控制器和动作,接着一个分发器对象载入正确的controller类和action成员函数来做”真正的”工作。

最后,正如我们上面所提到的,前段控制器把数据输出到响应对象于是一个web页面呈现在大家面前了。

2.5.2 Apache .htaccess

为了保证除那些images,javascripts和CSS以外的web请求都能被引导至启动(bootstrap)文件,这里,我们会使用到Apache的mod_rewrite模块。重写规则能直接在Apache的httpd.conf文件中配置或者被存放在”web_root/”目录下的一个叫.htaccess文件中进行配置,代码清单2-2所示的为.htaccess文件中的内容:
Listing 2.2:web_root/.htaccess


# Rewrite rules for Zend Framework
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f #1
RewriteRule .* index.php #2
(注释) <#1 Only continue if requested URL is not a file on disk.>
(注释) <#2 Redirect request to index.php.>


幸运的是,这个重写规则并不是很复杂。上面的命令指示Apache把大部分的请求都路由至index.php来显示相应页面,除非那些请求是映射到实际存在于web_root/目录下的文件,如images,javascripts,CSS(都说了几遍了…)。

2.5.3 Index Controller

前端控制模式把URL请求映射到对应类中对应的成员函数(theaction),这个过程叫做路由(routing)和分发(dispatching)。Controller类有严格的名字转换规则来使分发器找到正确的成员函数。控制器类有严格的命名规则,在此规则下,分发器能自动找到正确的函数。比如,若路由想要自动调用在{ControllerName}类里的{actionName}动作,那么这个名叫{ControllerName}的类必须放在一个叫{ControllerName}.php的php文件里,如果请求中类和动作都没给出,那么将其默认为index。因此,http://zfia.example.com/这样一个请求将启动index控制器中的index动作。类似的,
http://zfia.example.com/test这样一个请求将触发test控制器里的index动作。我们以后会发现,映射是很灵活的,尽管大多数时候我们采用的是常规手段。
在前端控制系统中,分发器期望在application/controllers目录中找到名为IndexController.php的文件,这个文件必须包含一个叫做IndexController的类,并且至少、起码要包含一个叫做indexAction()的函数。在我们的HelloWorld程序中,代码清单2-3显示了所必需的IndexController.php的内容:
Listing 2.3: The index controller: application/controllers/IndexController.php


Zend::LoadClass(’Zend_View’);
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$ this->view->assign(’title’, ‘Hello World!’); #1
}
}
(注释) <#1 把title属性指配给视图>


正如你所见到的,类IndexController是类Zend_Controller_Action的子类,它包括一个用来接入action函数的请求响应对象。以及一些有用的helper函数来控制程序流程。我们这个简单程序里,indexAction()函数只需要给视图属性一个值,这个属性是由名为ViewRenderer的一个动作助手所提供的。
注意!
动作助手是一个类,它提供了针对动作的相关具体服务。ViewRenderer动作助手为我们展示了两个有用的特性。首先,在动作被调用之前,ViewRenderer创建一个Zend_View对象并把它设置为动作的$view属性,使我们能把值赋给视图。其次,在我们的动作结束后,它会把视图模板赋给响应对象,这将确保我们的控制器动作函数可以集中在真正的工作上,而不是

原创粉丝点击