ZendFramework的安装只需要到Zend Framework的官方网站http://framework.zend.com/download下载Zend Framework的程序安装包解压到某个指定目录下,然后再php.ini(PHP的配置文件)中的include_path加入Zend的解压目录即可。
另外需要注意的是,默认的Zend Framework使用MVC机制,它采用rewrite的方式进行跳转,这就需要在apache的配置文件中(一般是httpd.conf)修改加入允许rewrite的选项,需要的步骤是:
-
找到LoadModule rewrite_modulemodules/mod_rewrite.so将其前面的#去掉
-
在项目所在的目录下<Directory “projectPath”>中修改AllowOverride的值为All,Order allow,deny / Allow from all
- 在项目所在根目录下建立.htaccess文件,内容为
RewriteEngine on #重写引擎打开
RewriteRule!.(jsicogifjpgpngcss)$ index.php
制定除js,ico,gif,jpg,png,css以外的文件全都被重置到index.php,index.php为项目的首页(其实是前端转发控制页)简单的Zend Framework安装测试方法:
创建文件test.php内容:
<?php
require_once(‘Zend/Date.php’);
$date=new Zend_Date();
echo $date
?>
如果能正常输出则说明Zend安装大体正常。
另外,如果想要在View文件中使用<?=someVariable?>代替<?php echo $value ?>的功能,需要在php.ini中开启short opentag, short_open_tag = On才可,否则<?=?>的表达式不能被正常解析。
二、MVC框架
- MVC框架是什么
MVC(Model-View-Controller)是一种集成了很多设计模式(Design Pattern)的一种设计模式,它强制的将程序的输入、处理、输出分开,它把程序分为三个核心部件:Model,View和Controller,其中
Model:负责数据的处理,包含业务逻辑。
View:负责数据的展现,并获取输入。
Controller:负责从View处接收输入,并操作Model来完成用户需求,然后调用View返回数据给用户。
- 典型的MVC框架例子
在这里举两个MVC的例子,一个是在传统Desktop程序,另一个以Zend Framework为例,详细介绍ZendFramework的MVC。
2.1传统Desktop程序:文本编辑器
文本编辑器大家应该都用过,在这里使用一个在《深入浅出MFC》中侯捷先生使用的例子,其所要实现的功能不必赘述,有一个文本框负责展示文字,用户可以对文字进行修改。
其中的:
Model,负责调用操作系统底层文件操作API,完成的功能有
a) 读取文件内容
b) 修改文件内容:包括编辑和删除内容
c) 创建文件、删除文件、
View,负责展示当前文件的内容给用户,并且负责提供UI界面,用以操作文本,完成的功能有:
a) 展示文本当前内容
b) 获取用户可能的输入:文本选择,文本复制,文本粘贴,文本编辑,文件的打开,删除和新建等等。
Controller,负责响应从View层传递来的用户请求,调用相应的Model操作,来执行用户的需求,完成的功能有:
a) 响应View请求,将其转换成对应的Model方法调用
b) 完成操作后,将返回结果返回给指定View,向用户展示结果
(注:在文本编辑器的功能中,有这样一种情况,有多个文本窗口,展示文件内容,当有多个窗口同时对一个文件进行操作,这样,就有了共同资源的修改问题,而在一个文本窗口修改了文本之后,其它的文本编辑器也应该显示修改过的值,这就需要每个文本窗口对文件状态有实时的了解,该如何实现?使用观察者模式.)
- Zend中的MVC框架
在这里,我们将简单介绍Zend Framework的MVC组件,并大致给出一个Zend Framework程序的简单实现。ZendFrameworkd的MVC框架与传统的MVC程序框架思想类似,但是由于应用于互联网环境中,又有了少许不同。
由于使用MVC框架,我们需要对项目的目录结构给出规范,典型的目录结构如下
app/
----controllers/
----models/
----views/
----------scripts/
其中app是根目录下的子目录,保存MVC代码,controllers中保存所有的controllers,models目录保存所有models代码,views/scripts中保存所有的view代码。
3.1 Zend_Controller
Zend_Controller是Zend Framework中的Controller部分,其中的Zend_Controller_Front类实现了前端控制器设计模式,
3.1.1前端控制器
前端控制器设计模式:由于互联网环境的所有请求都有httpurl请求从浏览器发送给服务器,Web应用的输入对于服务器来说就是请求,为了实现从请求->对应Controller的映射,我们将所有请求都首先发送到前端控制器front controller,由前端控制器进行配置,选择合适的Controller进行分发(dispatch)请求,再由具体Controller进行请求的响应处理。(前端控制器设计模式在Web程序中很常见,Structs也采用这种模式)
一般的前端控制器都在入口文件index.php中定义(根目录下),声明代码为:
// 初始化frontcontroller实例
$front=Zend_Controller_Front::getInstance();
// 开启分发过程中抛出异常的能力,默认异常引起之后会被放置在想要对象中
$front->throwExceptions(true);
// 设置默认的处理Controller名称
$front->setDefaultControllerName('home');
// 设置controllers目录
$front->setControllerDirectory($dir.'\app\controllers');
在所有的配置都已完毕之时,前端控制器需要负责整个程序(像是Win32程序中的主程序)的请求转发,(类似处理消息循环),并处理分发请求:如
try {
// 开始运行
$front->dispatch();
} catch (Exception $e) {
// 处理异常
echo $e;//include_once'exception.php';
}
FrontController的dispatch方法可以实现Controller的路由、分发、响应等工作,分发过程由三个不同的事件组成:
² 路由(Routing),使用路由器router,只执行一次,在调用dispatch()方法时利用请求对象中的值进行。
² 分发(Dispatching),使用分发器dispatcher,发生在循环中
² 响应(Response),完成处理后前端控制器返回响应对象
3.1.2动作控制器
Zend_Controller:每一个具体的Controller都应该是继承于类Zend_Controller_Action,是一个动作控制器类,此类有最基本的类Controller方法,用来处理相关的View和Model之间的操作,每个具体的Controller都应该继承这个类。
动作控制器:每个Controller中的publicfunction xxxAction()方法都是一个控制器动作,其中定义了Controller面对不同的请求时应该进行的动作。比如在请求http://localhost/home/index这个url的时候,请求就会转发到home Controller的indexAction动作中,indexAction会定义对这个请求的处理方法。
l 操作request\response对象、获取参数:
在动作控制器函数(xxxAction)中,可以通过API访问一些常用的数据,如:
² request对象:由getRequest()方式取得,返回一个Zend_Controller_Request_Abstract实例
² response对象:由getResponse()取得,返回一个Zend_Controller_Response_Abstract实例,可用此对象上的方法来修改response对象
² request参数:由post或者get传递过来的参数,可以用_getParam($key)方法或者_getAllParams()获取,也可以由_setParam方法来设置request参数(一般用于转发请求到其他动作)
l 操作视图:
² 视图由Zend_View对象来表示,在Zend_Controll_Action中可以通过initView()或render()方法来初始化对应的Zend_View对象,默认情况下假定视图存在views/scripts/子目录中,指定view脚本时基准目录为views/scripts/
(一般是在Zend_Controller_Action的init方法中使用initView,在其他xxxAction中使用render)
² render()用于解析视图,声明为render(string$action=null,string $name=null,bool $noController=false),如果不传递参数,默认的解析脚本是在views/scripts/[controller名]/[action名].$viewSuffix的值
² 注:控制器和动作名称中包含”_”,”.”,”-”的话,这些分隔符会被格式化成”-”
l 转向方法:
² _forward($action…)方法,用于执行另外一个动作
² _redirect($url…)方法,用于重定向到另外一个地方
3.1.3动作助手
动作助手(Zend_Controller_Action_Helper)可以向Zend_Controller_Action类的动作控制器加入功能(runtimeand/or on-demand functionality).
助手经纪人存在Zend_Controller_Action类中$_helper成员中,用来获取和调用助手。是一个Zend_Controller_Action_HelperBroker类的对象,其支持addHelper方法向经纪人中加入助手(也可以用addPrefix和addPath方法加入)。相当于Action的助手管家。
内建的动作助手
l FlashMessenger:允许用户传递可能需要在下个请求看到的消息,使用Zend_Session_Namespace来存储消息(用于内部的消息传递)
l Redirector: 转向器,可以帮助程序重定向到新的URL
l ViewRenderer:视图渲染助手,可以帮助我们不在controller中创建view实例,view对象自动在controller注册,可以根据当前模块自动设置视图脚本、助手、过滤器路径,指派当前的模块名为助手和过滤器的类名前缀,可以自动渲染view脚本,我们就可以不写initView和render了。
使用方法,
// View Render helper
$view=new Zend_View(array('encoding'=>'UTF-8'));
// 创建view render
$view_render=new Zend_Controller_Action_Helper_ViewRenderer($view);
// 设置view脚本的后缀,这里使用.php
$view_render->setViewSuffix('php');
// 将view renderhelper加入动作助手中,这样我们就可借助渲染助手自动渲染后缀为.php的view脚本啦,渲染的寻找规则和上述默认initView的规则一致.
Zend_Controller_Action_HelperBroker::addHelper($view_render);
3.2 Zend_View
Zend_View的使用方法:在Controller中创建一个Zend_View实例(也可以由View Render Helper帮忙创建,我们就不用自己写了)。然后将view中需要的变量赋给它就可以了,然后使用render渲染view即可。
一般代码为:
<?php
$view=newZend_View()
$view->a=’’
$view->c=’’//或者用view的assign(),支持关联数组赋值
echo$view->render(‘someView.php’)
?>
我们采用的方式为在view脚本中使用html与php相混合的形式,要用到的变量由controller传递给它,$this->view->xxx;在脚本中使用$this->xxx的方法访问。
3.3 Zend_Model
在Model部分一般是完成对数据的访问,管理以及实现业务逻辑,一般model的存储目录在和Controllers同根目录的models文件夹中,Model一般就是之间的PHP类就可以了。而一般我们在构建Web应用中数据都存在数据库里,在这里就简单讨论下Zend中对数据库的支持吧。
Zend_Db组件是Zend Framework中的数据库支持部分,由Zend_Db_Adapter、Zend_Db_Statement、Zend_Db_Profiler、Zend_Db_Select、Zend_Db_Table、Zend_Db_Table_Row以及Zend_Db_Table_Rowset等组成
Zend_Db_Adapter是Zend Framework的数据库抽象层API,是基于PDO的,可以支持多种数据库。Adapter的配置方式为
// 连接mysql数据库
$db=Zend_Db::factory('PDO_MYSQL', $config);
$config中存储一些连接数据库的配置信息,如地址,端口,用户名,密码,使用的数据库名等。在连接之后就可以直接使用$db->query()的方法查询数据库了。
Zend_Db_Adapter的支持的操作:
l query($sql,$bind=array()),查询数据库,$bind为需要绑定的数字
l queryInto($text,$value,$type=null)实现对SQL的无害化处理
l insert($table,array $bind),插入数据,$table为表明,$bind为表的字段与插入数据直接的绑定数组。
l lastInserId($tableName=null,$primaryKey=null),返回刚刚插入数据的ID
l fetchRow($sql,$bind=array()),用于查询SQL的返回结果,返回的结果可以用foreach($resultas $key=>$value)的形式遍历。
l delete($table,$where=’’)在数据库删除表$table记录
l update($table,array $bind,$where=’’)用于在表$table上根据$where的限制条件改变$bind数组相关的键值内容。
由上述的Zend_Db_Adapter就可以大体上的完成数据库的操作,我们的实验室网站也基本就用了Zend_Db_Adapter的相关内容,而其他的Zend_Db控件则提供了各种更强大的功能,大家可以自己发掘,这里不再赘述
三、Zend常用控件
上面说了这么多MVC什么的,下面通过给出在登录过程中的用到的ZendFramework的具体实例,说明Zend Framework的一些简单的组件用法。
- 从登录说起
一个简单的登录过程我们都再了解不过了,首先我们需要给出一个输入用户名密码的Form,然后用户输入完用户名密码之后,点击submit提交,我们的程序将用户输入与后台数据库中的数据进行比较,查看是否身份验证通过,验证通过则保存一份Session便于用户访问,否则则提示用户用户名密码输入错误,重新输入。复杂的登录控制还包括验证码等内容,我们在此处不讨论。
注:关于登录有很多内容可谈,比如单点登录又是什么?大家可以自己查查看看
下面给出登录控制Controller的Init方法和Login动作:相关的内容在后面补齐:
请求的URL应该类似为:http://localhost/member/login;表明form被提及到memberController的loginAction中进行处理。
首先给出Controller的初始化函数init,一般在这初始化各种属性
function init()
{
// 取得数据库接口,事先存储在对象注册表Zend_Registry中
$this->db=Zend_Registry::get('database');
// 设置view的action和controller属性
$this->view->action=$this->_getParam('action');
$this->view->controller=$this->_getParam('controller');
// 默认载入的js,方便在view中使用
$this->view->javascript=array('jquery.inline.js', 'paper.js');
// 取得登录状态,状态存储在Zend_Session中
$session=new Zend_Session_Namespace();
$login=$session->login;
if (isset($login)) {
// 给view中相关变量赋值
$this->view->aid=$login['AId'];
$this->view->priv=$login['Priv'];
$this->view->name=$login['Name'];
}
else {
$this->view->aid=$this->view->priv=$this->view->name=0;
}
// 默认标题
$this->view->title='Web首页 ';
}
然后给出login动作处理器
function loginAction()
{
$url='/home/index';
//从request参数中获取用户输入的用户名和密码
$user=$this->_getParam('User');
$pass=md5($this->_getParam('Pass'));
//启动Session
$session=new Zend_Session_Namespace();
try{
$acc=member::login($this->db, $user, $pass); // 调用model查询指定表中有无$user和$pass满足的条目
if (is_array($acc)) {
// $acc是数组,说明有满足条件的条目,验证成功,接下来将得到的信息放到$session中里,方便其他模块使用
$session->login= array
('AId'=>$acc['AId'],
'Name'=>$acc['Name'],
'Priv'=>$acc['Priv'],
'User'=>$acc['User']);
}
catch(Zend_Exception $e){
}
// 使用home/index.php页面进行渲染
$this->renderScript('home/index.php');
}
- Zend_Session
在登录过程成功之后,我们不可避免的需要保存用户的Session信息,而这个Session信息在Zend Framework中由控件Zend_Session实现。(即由Zend_Session管理PHP中$_SESSION的内容)
我们知道在PHP中要使用Session首先要开启Session,而我们使用Zend_Session::newZend Session_Namespace()方法则会默认开启Zend_Session::start()方法,而设置Zend_Session::setOptions(array(‘strict’=>true)); 可以阻止在new Zend_Session_Namespace()时调用Zend_Session::Start()方法。
(一般我们在使用Session前都会开启Zend_Session::start();)
所以我们使用Zend_Session的一般使用如下两种:
l Zend_Session::setOptions(array(‘strict’=>true));
Zend_Session::Start();
$mySpace=newZend_Session_Namespace(‘MySpace’);
l 或者需要时直接使用
$mySpace=newZend_Session_Namespace(‘MySpace’);
上例中采用第二种方式(其实上面的$mySpace其实就被保存在$_SESSION['MySpace']中)。
- Zend_Registry
Register是一个对象注册表,可以将任何对象存储在对象注册表中,这样就可以在任何地方随时随地调用,可以说就是一个全局变量管理器。
可以用
$reg=Zend_Registry::getInstance();
$reg->set('database', $db);
来获得一个默认的Zend_Registry注册表变量(本例中的数据库对象),并在其上调用set($key,$value)方法设置全局变量的值,此后在任何地方可以通过Zend_Registry::isRegistered($key)检查是否有对象存在,可以用Zend_Registry::get($key)获得默认的注册表中的对象内容,还可以通过Zend_Registry::_unsetInstance($key)来删除对象注册表内的内容。
Zend_Registry和Zend_Session的区别(翻译自网上):
Zend_Registry和Zend_Session的主要区别是在它们的作用域上,Zend_Registry的作用域是对当前的请求(current request)有效,也就是Zend_Register的数据不能被不同页面所共享,但是如果我们在index.php(前端转发控制的页面)定义Zend_Register数据,那么在所有的controllers/actions中都可以使用Zend_Register的,因为它们刚刚被重定向到index.php后才传到这些具体的action中,所以这些数据是可以用的。而一般Zend_Registry用于存储配置,共享变量等信息。在每次请求不同的页面的时候Zend_Register的数据都会被清除。
而Zend_Session使用的是PHP的Session,可以被任何页面共享,它是真的有Session的作用域的(即关闭浏览器之前、或Session过期之前都有效)
- Zend_Loader
熟悉PHP的人都知道,在PHP中,如何加载Load(include)一个类是一个很麻烦的话题,由此,PHP5中提供了__autoload的机制,方便类文件的自动加载。而Zend框架中也提供了一个可以实现对文件和类进行动态加载的功能Zend_Loader。
提供Zend_Loader的加载功能可用的方法:
l 加载文件
Zend_Loader::loadFile($filename,$dirs=null,$once=false);
$filename是要加载的文件名,$dirs为文件所在的目录,如果为空的话程序会自动到PHP的include_path中找,$once指定是否为只加载一次,为true加载一次,否则重复加载。如果loadFile加载文件失败,此方法会抛出Zend_Exception异常。
l 加载PHP类
Zend_Loader::loadClass($class,$dirs)还可以支持对PHP类的加载,其中$class为类名,$dirs为包含类的所在路径及文件名,另外,此方法会根据下划线对应到目录的文件,Zend_Controller_Action会指向Zend/Controller/Action.php文件。如果$dirs为一个字符串或数组,则方法会顺序查找相应目录,加载第一个匹配的文件,如果没找到的话还会在include_path中查找。
- Zend_Acl
Zend_Acl组件给出了实现一个完整的访问控制的解决方案,Zend_Acl中定义了两个重要的概念资源和角色,而分别和其对应的两个Zend控件是Zend_Acl_Role和Zend_Acl_Resource。
Zend_Acl_Resource:在Zend_Acl中,任何文件都可以被当做资源对待,Zend_Acl还提供了一个资源的树结构,可以自由添加多个Resources。Zend_Acl也支持基于Resources的create,read,update和delete权限等待。资源可以通过实现Zend_Acl_Resource_Interface来实现,而Zend_Acl_Resource是基本的一个Resource实现,也可以进行扩展实现资源
Zend_Acl_Role: 在Zend_Acl中,角色就是所有进行访问的对象的统称,Role之间可以有继承关系,可以通过Zend_Acl_Role_Interface接口开发Roles,同样Zend_Acl_Role是一个基本的Role实现,可以用来继承扩展
创建ACL的方法
$acl=newZend_Acl();
$acl->addRole($roles,$parentnode);//加入角色
$acl->allow($roles,$resources,$privilege,$assert);//指定用户组$roles在$resources上的权限,$resources使用null的时候代表所有资源,$privilege可以为create,edit,delete等等,而$assert用于指定有特定约束的控制,如在一定时间段内不允许等等。
$acl->deny($roles,$resources,$privilege,$assert);//为指定用户组$roles在$resources上添加拒绝权限
定义了Acl之后,我们就可以用Acl的isAllowed方法来检查某个用户在某个资源上是否有权限了如
$acl->isAllowed($roles,$resources,$privilege);
- Zend_Auth
访问认证适配器,用于给出一个对用户访问进行认证的方法,Zend_Auth依靠特定的认证服务来进行认证(如RDBMS)
Zend_Auth提供了很多种的系统默认认证访问支持,现在在这里只简单说明数据库认证方式。
Zend_Auth_Adapter_DbTable是Zend_Auth提供的数据库访问方案,它的默认构造函数的参数需要有:
l $zendDb,一个Zend_Db_Adapter的适配器实例
l $tableName,数据库的表名称
l $identityColumn:指定表中的记录列
l $credentialColum:指定表中的凭记列(密码列)
l $credentialTreatment:指定对表中的凭记列进行的加密操作
- 对象的初始化方式也可以如下:(以set的方式替代构造函数参数)
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('zendLogin')
->setIdentityColumn('username')
->setCredentialColumn('password')
->setCredentialTreatment('MD5(?)');
- 对象初始化后,就可以调用
$auth->setIdentity(username);
$auth->setCredential(password);
$result=$auth->authenticate();
If($result->isValid()){
…
// the default storage is a sessionwith namespace Zend_Auth
$authStorage = $auth->getStorage();
$authStorage->write($userInfo);
}
进行用户的认证了。
- 而在用户退出时则可以使用:
Zend_Auth::getInstance()->clearIdentity();
清除保存的认证信息。
(在用户进行成功通过认证之后,需要为其设置一个持久的认证,这在Zend_Auth中默认使用Zend_Auth_Storage_Session实现认证信息保存功能。)
- Zend_Log
在Zend Framework中给出了一个通用性的日志组件,Zend_Log,它负责进行各种日志保存的操作,它又分为
Log对象:Zend_Log的实例,常用的对象
Write对象:继承与Zend_Log_Writer_Abstract,负责向存储中保存数据
Filter对象:实现了Zend_Log_Filter_Interface,过滤一些不需要的数据
Formatter对象:实现了Zend_Log_Formatter_Interface,在Write写入数据之前对日志数据进行格式化工作,每个Writer只能有一个Formatter对象
在下面只介绍Zend_Log对象。
大体使用方法:
$writer=newZend_Log_Writer_Stream(‘log.txt’);//初始化Zend_Log_Writer_Stream实例
$log=newZend_Log($writer); //初始化Zend_Log对象
$log->log(‘someitems’ Zend_Log::ALERT); //向日志中写入数据,使用alert消息级别,可以使用的消息级别有很多…具体查网上吧。。