书名:Yii框架深度剖析
ISBN:978-7-115-47012-6
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
• 著 刘 琨
责任编辑 傅道坤
• 人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
• 读者服务热线:(010)81055410
反盗版热线:(010)81055315
Yii是一个基于组件、用于开发大型Web应用的高性能PHP框架,它提供了当今Web 2.0应用开发所需要的几乎一切功能,是最具开发效率的PHP框架之一。
本书站在框架设计的高度,从源代码级别剖析了Yii框架的工作机制。本书分为15章,其内容涵盖了PHP框架的概念,Yii框架的工作流程,布局的概念及作用,模块的概念及作用,ActiveRecord模型的原理和作用,ActiveRecord模型的查询方法,小物件的概念及作用,小物件CActiveForm的作用以及调用方法,模型验证的概念及作用,Yii框架中的AJAX验证,与用户登录相关的内容,Yii框架中基于角色的访问控制系统的设计与实现,Memcached缓存在Yii框架中的使用,日志在Yii框架中的实现,以及Yii框架中URL管理组件。
本书适合Web开发人员,以及有一定的PHP开发基础,但是希望学习使用框架来提升开发能力的读者阅读。
本书不是简单地介绍如何使用Yii框架,而是站在框架设计的高度,从源代码级别深度剖析。本书首先介绍PHP框架技术的概念及其应用领域,然后开始仿照Yii框架源代码自定义框架,其中包括了MVC设计模式、单入口模式和应用(前端控制器模式)的实现。充分了解了这部分知识后,读者对Yii框架也有了初步认识,然后正式进入Yii框架的学习。
本书可帮助那些希望借助框架进行开发的读者顺利地熟悉Yii的基本结构、规范和开发流程,轻松掌握常用的Yii组件,敏捷、稳健地开发Web 2.0应用程序。
开源技术爱好者。
计算机专业的学生。
广大的Web开发从业人员。
具备PHP开发基础,进而希望通过学习使用框架来提升开发能力的读者。
掌握了PHP面向过程的开发方式,正在转向PHP面向对象编程的读者,通过学习Yii框架,他们可以更加迅速、规范地掌握MVC架构以及面向对象的思想和语法。
第1章,初识PHP框架技术,首先介绍PHP语言的发展历史及其适合的应用领域。然后介绍框架的概念,并且在自定义框架部分实现MVC框架模式、单入口文件设计模式和前端控制器模式,目的是为了让读者能够更好地理解Yii框架的工作机制,因为这些设计模式都是框架技术通用的设计思想。
第2章,Yii框架基础,主要介绍Yii框架的执行流程,其中详细介绍了Yii框架的入口文件、应用(前端控制器)的具体作用、MVC框架模式在Yii中的具体实现方式,以及控制器渲染视图的实现步骤。通过本章的学习,读者对于MVC应有更深层的认识。
第3章,布局,主要介绍布局的概念及作用,重点分析控制器渲染布局的render()方法,帮助读者加深对使用布局文件的认识。最后,为了更加灵活地实现视图文件的渲染,学习了应用级布局和嵌套布局。
第4章,模块,主要介绍模块的概念、作用,以及如何创建和访问模块。
第5章,ActiveRecord模型,主要介绍ActiveRecord模型设计原理和作用,以及Yii框架如何创建ActiveRecord模型,并详细介绍了Yii框架CActiveRecord类中CRUD操作的相关方法。
第6章,CActiveRecord模型类的查询方法,重点介绍CActiveRecord模型类的查询方法。作者通过简单、形象的示例,充分地讲解CActiveRecord模型类的查询方法3种类型参数的使用方法。其中6.5节深入介绍了关联查询。
第7章,Widget(小物件),主要介绍小物件的概念及作用。通过创建首页中幻灯片小物件,讲解小物件如何嵌入到视图中,以及自定义小物件的方法。
第8章,ActiveRecord模型验证,首先介绍模型验证的概念和作用。MVC框架模式下模型验证的步骤,包括模型中编写验证规则、预定义验证器的调用、在控制器中给模型安全赋值、触发验证和显示错误信息的方法等。
第9章,AJAX验证,重点介绍Yii框架中的AJAX验证。因为AJAX验证是服务器端验证,所以是在CActiveForm中实现的。并且为了更好地理解Yii框架中的AJAX验证,在本章开始依次介绍了AJAX、JavaScript实现AJAX验证和jQuery实现AJAX验证。
第10章,用户登录,主要介绍为了实现用户登录,需要掌握的Yii框架的相关内容,包括表单模型、客户端验证、如何自定义验证器来验证用户名和密码的身份类,以及保存用户登录状态的CWebUser类。
第11章,基于角色的访问控制,主要介绍Yii框架中基于角色的访问控制系统(RBAC)的设计与实现。并且,作者结合自己多年的工作经验,在11.9节将该控制系统无缝地移植到实际项目中。
第12章,Yii框架中Memcached缓存应用,主要介绍Yii框架中如何应用Memcached缓存。作者系统、详细地介绍了内存缓存软件Memcached的安装及管理,以及PHP的Memcached客户端扩展方法库。这些都是理解Yii框架CMemCache缓存组件的基础。当然,Yii框架为了更好地使用缓存,还提供了缓存依赖、片段缓存和页面缓存的使用方法。
第13章,日志,主要介绍了Yii框架中的日志记录系统,首先介绍Apache服务器是如何记录访问日志和错误日志的;然后介绍PHP语言如何通过修改配置项或在程序中调用日志方法来生成日志文件;最后,在了解了Apache和PHP的日志功能之后,读者就会更好地理解Yii框架的日志功能的设计思路以及相关方法的使用方式。
第14章,URL重写,主要介绍Yii框架的URL管理组件。为了使用Yii框架的URL管理组件,需要充分了解URL的模式和良好URL的格式,并且也需要借助Apache服务器的重写模块。
第15章,Yii 2.0介绍,Yii 2.0要求开发环境是PHP 5.4以上版本,所以在学习Yii 2.0之前希望读者先熟悉PHP 5.4版本中增加的语法,如命名空间等。本章以输出“Hello World”为例,简单介绍了Yii 2.0框架的执行流程,希望读者结合本书前14章内容的学习思路,循序渐进掌握Yii 2.0框架。
由于PHP开源的特性,尽管作者使用PHP框架技术多年,但将庞大数量的碎片知识整合为一本厚达几百页的书,其中的辛酸非三言两语能够道破。
感谢家人的鼓励,是他们的宽容让我能够安心做好每件事。感谢石家庄经济学院李文斌老师的耐心指导。感谢同事吕建军一直以来无私地向我分享案例、数据和发现。感谢我的学生对我的支持,这是我克服困难的原动力。
本书主要由刘琨写作,参与写作与资料整理的其他人员有刘云龙、贾春华、刘雄章、刘卓、贾月华、刘彦霞、贾婕、贾桂花、朱明生、王宇、张爱净、蒲龙君、张伟等。
由于作者水平有限,书中难免存在不足和疏漏之处,恳请读者批评指正。本书作者的邮箱地址为71873467@qq.com。
为了方便读者更好地学习,作者在51CTO学院创建了本书内容视频,网址为http://edu. 51cto.com/course/course_id-1973.html。欢迎读者加入QQ群:231113585,获取图书配套代码;并和其他读者一起讨论学习体会和心得。
Yii框架基于PHP语言,本书就从PHP语言的发展历史说起。本章首先介绍PHP语言发展历史及其适合的应用领域,然后讲解PHP框架技术的概念并仿照Yii框架源码自定义一个框架,其中包括MVC框架模式、单入口模式和应用(前端控制器模式)的实现。读者在充分了解了这部分内容后,将正式进入到Yii框架的学习。
PHP最初为Personal Home Page的缩写,但现在已经正式更名为Hypertext Preprocessor(中文名为“超文本预处理器”)。PHP于1994年由拉斯姆斯·勒多夫(Rasmus Lerdorf)创建,它起初是勒多夫为了要维护个人网页而制作的一个简单的用Perl语言编写的程序。这些工具程序用来显示他的个人履历,以及统计网页流量。后来他又用C语言重新编写,并增加了访问数据库的功能。他将这些程序和一些表单直译器整合起来,称为PHP/FI。PHP/FI可以和数据库连接,产生简单的动态网页程序。
1995年,勒多夫以Personal Home Page Tools(PHP Tools)开始对外发布第一个版本,并写了一些介绍此程序的文档。在发布的PHP 1版本中,提供了访客留言本、访客计数器等简单的功能。此后,越来越多的网站开始使用PHP,并且强烈要求增加一些特性,如循环语句和数组变量等。在新的成员加入开发行列之后,勒多夫在1995年6月8日将PHP/FI公开发布,希望可以通过社群来加速程序开发与寻找错误。这个发布的版本命名为PHP 2,已经有PHP的一些雏型,具有类似Perl的变量命名方式、表单处理功能,以及嵌入到HTML中执行的能力。程序语法上也类似Perl,有较多的限制,不过更简单,更有弹性。PHP/FI加入了对MySQL的支持,从此建立了PHP在动态网页开发上的地位。到了1996年年底,有大约15000个网站使用PHP/FI。
1997年,任职于Technion IIT公司的两个以色列程序设计师:齐弗·苏拉斯基(Zeev Suraski)和安迪·古特曼斯(Andi Gutmans),重写了PHP的解释器,这成为PHP 3的基础。而PHP也在这个时候改称为Hypertext Preprocessor。经过几个月的测试,开发团队在1997年11月发布了PHP/FI2。随后就开始了PHP 3的开放测试,在1998年6月正式发布PHP 3。苏拉斯基和古特曼斯在PHP 3发布后开始改写PHP的核心,随后在1999年发布了Zend Engine解释器。同年,在以色列的拉马特甘成立了Zend Technologies公司来管理PHP的开发。
2000年5月22日,以Zend Engine 1.0为基础的PHP 4正式发布。2004年7月13日,发布了PHP 5。PHP 5使用了第二代的Zend Engine解释器,使PHP包含了更多新特性,如面向对象功能、引入PDO(PHP Data Object,一个存取数据库的延伸方法库),以及许多效能上的增强。PHP 4已经不会继续更新,以鼓励用户转移到PHP 5。随着PHP语言面向对象功能的实现,PHP 5版本后出现了框架技术,我们要学习的Yii框架就是其中一个“佼佼者”。
当框架技术出现后,基于PHP的产品逐渐多了起来。如图1-1所示,首先我们来看第一大类,我把它们称为PHP开源产品,其中一些适合作为开发企业、政府、公司门户网站的内容管理系统,如DedeCMS、PHPCMS和帝国CMS等,还有制作论坛的Discuz系统,开发商城可以选择ECShop等系统,开发博客选择WordPress。开源PHP产品很多,这里不再一一列举。虽然本书作者没有研究过所有的开源产品,但作者分析过的开源产品都使用了框架技术。接下来是作者想说的重点,也是我们学习Yii框架后经常选择应用的领域,就是第二大类,即基于Web的各种管理软件,如贸易公司和其下属销售中心使用的分销系统等。第三大类是定制型、功能型和工具型网站,类似CNZZ网站的访问情况统计。还有就是硬件管控软件的GUI,如路由器中的配置管理页面。
图1-1 PHP应用领域
作者相信,在当今这个互联网的时代,PHP语言和它的框架技术会有更加辉煌的未来!
框架(Framework)是在给定的问题领域内,实现了应用程序的一部分设计,是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法。简单来说,就是一个“半成品”,帮助项目把“骨架”搭好,并提供丰富的组件库,只需要增加一些内容或调用一些提供好的组件就可以完成自己的系统。
如图1-2所示,已经有一个成型的房子“骨架”和一些建筑材料,我们可以把它比喻成一个程序的框架。其中“骨架”可以看做是为我们创建的项目管理结构(半成品),而建筑材料则相当于为我们提供的现成组件库。在这个已有房子框架结构的基础上,结合现成的建筑材料,再经过我们的“装修”,就可以将这个“半成品”建造成私有住宅、办公楼、超市或酒吧等。同理,使用程序框架也会很快开发出个人主页、OA系统、电子商城和SNS系统等软件产品。
图1-2 框架说明
框架的最大好处之一就是重用。面向对象系统获得最大的复用方式就是框架,一个大的应用系统往往可能由多层互相协作的框架组成。Web系统发展到今天已经变得很复杂,特别是服务器端软件,涉及的知识、内容和问题已经非常多。在项目开发中,如果使用一个成熟的框架,就相当于让别人帮你完成一些基础工作(大约50%以上),你只需要集中精力完成系统的业务逻辑设计。
框架一般是成熟稳健的,它可以处理系统的很多细节问题,如事物处理、安全性、数据流控制等。
还有,框架一般经过很多人使用,结构很好,扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。
框架也可以将问题划分开来各个解决,易于控制,易于延展,易于分配资源。应用框架强调的是软件的设计重用性和系统的可扩充性,以缩短大型应用软件系统的开发周期,提高开发质量。
框架能够采用一种结构化的方式对某个特定的业务领域进行描述,也就是将这个领域相关的技术以代码、文档、模型等方式固化下来。
在学习Yii框架前,我们先自定义一个框架,这样能更好地掌握框架技术的工作机制,为以后学习Yii框架做好准备。同时,设计一个自己的PHP框架并一步步地实现,可以及时融入最新的思想和理念。更重要的是,属于自己的框架可以根据项目的需要为其量身定制。
那么,就先从实现MVC框架模式开始。
Yii使用了Web开发中广泛采用的MVC框架模式,因此,使用者在使用Yii建立应用系统时,必须对MVC的原理有一些了解。MVC一直以来是Yii框架初学者很难跨过的一个障碍,本节仿照Yii框架代码实现MVC的软件架构,希望能够通过深入浅出的方式,让读者对MVC有清楚的认识。
传统的基于PHP语言的Web应用程序把PHP代码和HTML、CSS、JavaScript代码混合在一起,这样不利于代码的后期维护,同时也不利于程序功能的扩展。基于MVC的应用程序,把应用程序中的各个功能独立出来,可以很好地实现程序功能的分工合作,对于代码的维护和扩展也十分方便。
MVC是一种目前广泛流行的框架模式。近年来,随着PHP的成熟,它正在成为在LAMP平台上推荐的一种框架设计模式,也是广大PHP开发者非常感兴趣的框架设计模式,并有不断成长的趋势。随着网络应用的快速增加,MVC模式对于Web应用的开发无疑是一种非常先进的设计思想。无论用户选择哪种语言,无论应用多么复杂,都能为用户理解分析应用模型提供最基本的分析方法,为用户构造产品提供清晰的设计框架,为用户的软件工程提供规范的依据。MVC的设计思想是把一个应用的输入、处理和输出流程按照模型(Model)、视图(View)和控制器(Controller)的方式进行分离,这样的一个应用分成3个层——模型层、视图层和控制层,下面分别进行介绍。
(1)视图
视图是用户看到的并与之交互的界面。视图可以向用户显示相关的数据,并能接收用户的输入数据,但它并不进行任何实际的业务处理。视图可以向模型查询业务状态,但不能改变模型。视图还能接收模型发出的数据更新事件,从而对用户界面进行同步更新。作为视图,它只是作为一种输出数据并允许用户操作的方式。
(2)模型
在MVC的3个部件中,模型是主体部分,包含业务数据和业务逻辑,同时负责访问和更新持久化数据。一个模型能为多个视图提供数据,每个视图都从不同角度来表达模型。由于应用于模型的代码只需写一次就可以被多个视图重用,因此降低了代码的重复性。
(3)控制器
控制器负责协调整个应用程序的运转,作用就是接收浏览器端的请求。它接收用户的输入并调用模型和视图去完成用户的需求,当用户单击Web页面中的超链接或发送HTML表单时,控制器本身不输出,只是接收请求并决定调用哪个模型去处理浏览器端发出的请求,然后确定用哪个视图来显示模型处理返回的数据。
MVC处理过程如图1-3所示,首先控制器接收用户的请求,并决定应该调用哪个模型来处理;然后模型根据用户请求进行相应的业务逻辑处理,并返回数据;最后控制器调用相应的视图来格式化模型返回的数据,并通过视图呈现给用户。
图1-3 MVC设计模式
使用PHP开发出来的Web应用,初始的开发模板就是混合的数据编程。例如,直接向数据库发送请求并用HTML显示,开发速度往往比较快,但由于数据页面的分离不是很直接,因此很难体现出业务模型的样子或者模型的重用性。产品设计弹性力度很小,很难满足用户的多样化的需求。MVC要求对应用分层,虽然要进行额外的工作,但产品的结构清晰,产品的应用通过模型可以得到更好的体现。
首先,最重要的是应该有多个视图对应一个模型的能力。在目前用户需求快速变化的情况下,可能有多种方式访问应用的要求。例如,订单模型可能有本系统的订单,也有网上订单,或者其他系统的订单,但对于订单的处理都是一样,也就是说,订单的处理是一致的。按照MVC设计模式,一个订单模型以及多个视图即可解决问题。这样减少了代码的复制,即减少了代码的维护量,一旦模型发生改变,也易于维护。其次,由于模型返回的数据不带任何显示格式,因而这些模型也可直接应用于接口的使用。再次,由于一个应用被分离为3层,因此有时改变其中的一层就能满足应用的改变。面对一个应用的业务流程或者业务规则的改变,只需改动MVC的模型层。
控制器还有一个好处,就是可以用它来连接不同的模型和视图去完成用户的需求,这样它可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后通过视图将处理结果显示给用户。
最后,MVC还有利于软件工程化管理。由于不同的层各司其职,每一层不同的应用具有某些相同的特征,因此有利于通过工程化、工具化特性产生管理程序代码。
综上所述,MVC是构筑软件非常好的框架模式,即将业务处理与显示分离,强制地将应用分为模型、视图及控制层。总之,MVC模式会使得应用更加强壮,更加有弹性,更加个性化。
在实现MVC框架模式之前,我们先来介绍一下不使用MVC的开发流程。这里有一个网站的3个页面,分别是首页(index.html)、列表页(arc_list.html)和内容页(article.html)。这3个页面都是静态页面。接下来实现由静态页面改写成PHP动态页面,以便能及时从数据库中读取最新内容。这里只实现首页中的“行业百科”模块,效果如图1-4所示。
图1-4 首页中“行业百科”模块效果图
静态页面index.html中“行业百科”模块代码如下。
<div class="title2 indextt4">
<span><a title=行业百科 href="#">行业百科</a></span>
<em><a href="#">更多 >> </a></em>
</div>
<div class="rightList2 marginbtm15">
<ul class=ulRightList1s>
<li><a title=洗碗机分类 href="#" target=_blank>洗碗机分类</a></li>
<li><a title=家用全自动洗碗机分类 href="#" target=_blank>家用全自动洗碗机分类</a></li>
<li><a title=洗碗机在中国的三起三落 href="#" target=_blank>洗碗机在中国的三起三落</a></li>
<li><a title=家用全自动洗碗机发展历史 href="#" target=_blank>家用全自动洗碗机发展历史</a></li>
<li><a title=什么是洗碗机? href="#" target=_blank>什么是洗碗机?</a></li>
</ul>
</div>
使用PHP语言从数据库中读取“行业百科”栏目下的文章标题,重新编写成index.php文件,代码如下所示。
<div class="title2 indextt4">
<span><a title=行业百科 href="#">行业百科</a></span>
<em><a href="#">更多 >> </a></em>
</div>
<div class="rightList2 marginbtm15">
<ul class=ulRightList1s>
<?php
$dbh = new PDO('mysql:dbname=dscms;host=127.0.0.1','root','aa09090909');
$dbh->exec("set names 'utf8'");
$query = "SELECT title FROM ds_article WHERE cid='14'";
try {
//执行SELECT查询,并返回PDOstatement对象
$pdostatement = $dbh->query($query);
$result=$pdostatement->fetchAll();
foreach ($result as $row)
{
?>
<li><A title=<?php echo $row["title"]; ?> href="#"
target=_blank><?php echo $row["title"];?></A></li>
<?php
}
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
</ul>
</div>
实现了首页中“行业百科”从数据库查询功能后,首页中其他功能,还有列表页、内容页,实现过程和“行业百科”类似,这里就不列举了。
如此编写代码是很多初学者经历的一个阶段,就是将PHP代码和HTML、CSS、JavaScript代码混合在一起使用。如果有人之前这样去做的话,能否体会出代码混合在一起编写所带来的麻烦?
首先就是不利于代码的重复使用,如上文中的“行业百科”模块。如果在列表页和内容页中也有一样的模块,则需要重复编写,或者把这部分的代码放到一个文件中,频繁使用include语句去调用。或者代码连重复利用都不行,如数据库操作等。其次就是不利于较大项目的团队合作,如后端开发不需要使用HTML、CSS和JavaScript等技术;前端开发不需要使用数据库、PHP开发。最后,不利于代码的后期扩展,如在后期的项目维护过程中,代码混杂,层次不清,将导致重复修改。
如何解决这些不利于软件开发的问题呢?毫无疑问,MVC框架模式就能解决,具体处理流程如下。
步骤1:创建models/Article.php,并在文件中定义文章表模型类Article,其中的find()方法返回查询数据的结果。
<?php
class Article
{
public function find()
{
$dbh = new PDO('mysql:dbname=dscms;host=127.0.0.1','root','');
$dbh->exec("set names 'utf8'");
$query = "SELECT title FROM ds_article WHERE cid='14'";
try {
//执行SELECT查询,并返回PDOstatement对象
$pdostatement = $dbh->query($query);
return $result=$pdostatement->fetchAll();
} catch (PDOException $e) {
echo $e->getMessage();
}
}
}
?>
步骤2:在framework/framework.php文件中创建控制器的基类CController,并实现控制器渲染视图方法render(),这个方法的功能是加载指定目录下的视图文件,并将控制器中的数据传递到视图文件中。
<?php
class CController{
/**
加载指定目录下的模板文件,并将控制器中的数据传递到视图文件中
@param string $fileName 提供模板文件的文件名
@param array 变量名=>变量值
*/
public function render($viewName, $data){
extract($data, EXTR_PREFIX_SAME,'data');//将数组$data变成变量的形式
require($viewName);//包含视图文件
}
}
?>
步骤3:创建Controllers/DefaultController.php文件,创建控制器DefaultController继承父类CController,创建首页管理方法actionIndex(),在其中创建模型Article对象,并调用find()方法获取数据,渲染视图,并把数据输出到视图页面。
<?php
require '../framework/CController.php';//导入框架文件
require '../models/Article.php';//导入文章表模型类文件
class DefaultController extends CController
{
//首页管理
public function actionIndex()
{
//创建模型对象
$article=new Article();
//获得数据
$result=$article->find();
//渲染视图,并把数据输出到视图页面
$this->render("../views/index.php",array("result"=>$result));
}
//列表页管理
public function actionList(){}
//内容页管理
public function actionArticle(){}
}
$default_con = new DefaultController();
$default_con->actionIndex();
?>
步骤4:创建views/index.php,在视图文件中,对查询结果变量$result进行循环处理,生成完整的HTML页面。
<DIV class="rightList2 marginbtm15">
<UL class=ulRightList1s>
<?php
foreach ($result as $row)
{
?>
<li><A title=<?php echo $row["title"]; ?> href="#"
target=_blank><?php echo $row["title"];?></A></li>
<?php
}
?>
</UL>
</DIV>
实现的MVC框架执行流程如图1-5所示。
图1-5 MVC框架执行流程
1.用户直接调用控制器实例对象。控制器调用类中的action方法(动作)。
2.控制器调用模型实例对象从数据库中读取数据。
3.渲染视图。
4.视图读取并显示模型的属性。
5.动作完成视图渲染并将其返回给用户。
本节按照MVC框架模式的工作思想,完成了控制器、模型、视图3个部分的代码分离。我们访问程序,需要去访问controllers目录下的控制器文件,这样做存在明显的设计缺陷。如果控制器文件较多,则会导致系统结构访问混乱,并存在后期维护困难、安全性差等一系列问题,而且不便于系统的统一管理。
下一节将新增入口文件,通过解析用户请求的URL,提取出控制器名和动作方法名,创建相应控制器实例对象,并执行动作方法。
本节首先介绍系统多个请求入口设计带来的不便,然后介绍单一请求入口设计模式实现原理。本节的学习目标是明确单一入口文件设计模式的优点,避免在以后的开发项目中出现多入口。
系统中凡是能够被访问的PHP文件称为入口文件。如果用户的不同请求直接对应到Web服务器中的不同PHP文件,即系统是多入口设计。在刚开始学习PHP的时候,通常一个项目都会这样做:
又或者在1.4.1节实现MVC框架模式后,访问不同的控制器类文件,如DefaultController. php或SiteController.php。
对于这些项目来说,都有多个入口文件,随着项目规模的不断扩大,多入口的设计缺陷会越来越明显,如系统目录结构混乱,后期维护困难,容易暴露程序漏洞,不便于系统的统一管理等。为了避免多入口设计带来的诸多问题,可以使用单一入口设计模式。单一入口设计模式就是一个文件处理所有的HTTP请求,也就是说,访问任何控制器文件,无论是DefaultController.php、SiteController. php,还是其他控制器类文件。每一次请求都是指向服务器的同一个文件,如入口文件index.php,该文件负责URL解析,最终转向所要访问的页面,如图1-6所示。
图1-6 单一入口文件模式
PHP单一入口模式可谓是现在一种比较流行的大型Web应用开发模式。当前比较流行的一些PHP开发框架,如Zend、ThinkPHP和Yii等都是采用单一入口模式。
使用单一入口文件模式的优点如下。
在上文中提到入口文件的URL解析,即入口文件会将原始请求转发给相应的处理控制器,完成具体的业务处理。例如,有以下URL地址:
http://<hostname>/
http://<hostname>/index.php
http://<hostname>/index.php?r=site
http://<hostname>/index.php?r=site/index
提示:
自定义框架模仿Yii框架采用路径(PATH)URL模式访问规则。路径URL模式采用目录分层的思想,路径格式简洁,URL解析效率高,此URL格式为:http://<hostname>/appname/index.php?r= controllerID/actionID
我们希望上面所有URL被解析后都会访问SiteController控制器的actionIndex()方法。URL解析执行流程如图1-7所示,首先访问入口文件,在其中分析请求URL的参数,在没有“r”参数的情况下默认访问SiteController的actionIndex()方法,否则依据“r”参数访问SiteController的actionIndex()方法,即所有的访问由URL的参数来统一解析和调度。
图1-7 URL解析执行流程图
入口文件index.php中代码实现如下。
<?php
//默认控制器是SiteController
$defaultController="site";
//默认动作actionIndex
$defaultAction="index";
//如URL为http://hostname/index.php?r=controllerid/actionid
//得到controllerid/actionid
if(!empty($_GET['r']))
{
$route=$_GET['r'];
//得到controllerid赋值给成员变量
$pos=strpos($route,'/');
$defaultController=substr($route,0,$pos);
$defaultController=strtolower($defaultController);
//得到actionid赋值给成员变量
$defaultAction=(string)substr($route,$pos+1);
}
//得到控制器类名
$className=ucfirst($defaultController).'Controller';
//获得控制器文件路径
$classFile="./controllers/".$className.'.php';
//最后一步操作:该类文件存在则导入,该类存在则创建对象并调用acion方法
if(is_file($classFile))
{
if(!class_exists($className,false))
{
require($classFile);
$class= new $className();
$functionName="action".ucfirst($defaultAction);
$class->$functionName();
}
}
?>
由上面的程序可知,默认的控制器是SiteController,默认的执行方法是actionIndex()方法。控制器的类名首字母大写,以“Controller”结尾,而且控制器类文件必须保存在controllers文件夹中;动作方法名必须以“action”为前缀,acitonID首字母大写。从这段程序中也可以了解到代码规范的重要性,因为文件名或类名等都会在程序中使用。同样的道理,在将要学习的Yii框架开发过程中,也要遵守一定的编码规范。例如,命名类时,使用驼峰风格,即每个单词的首字母大写并连在一起,中间无空格;变量名和方法名应该使它们的第一个单词全部小写,其余单词首字母大写,以使其区别于类名,如$basePath、runController();对私有类成员变量来说,推荐以下画线作为其名字前缀,如$_actionList。
提示:
为了使PHP语言开发的框架能够遵循共同的编码风格,在2009年由几个框架的开发者组成了PHP-FIG(PHP Framework Interoperability Group)小组,一直扩展到现在已经拥有20多位成员。
实现入口文件后,框架执行流程如图1-8所示。
图1-8 框架执行流程
1.用户发出了访问URL的请求,Web服务器通过执行入口文件index.php处理此请求。
2.入口文件负责完成URL的解析,根据URL请求创建控制器并调用动作处理用户请求。
3.控制器调用模型实例对象从数据库中读取数据。
4.渲染视图。
5.视图读取并显示模型的数据。
6.动作完成视图渲染并将其返回给用户。
实现单一入口模式之后,需要确保应用根目录下,除入口文件外的PHP文件(所有安全敏感的PHP文件)都不允许访问。通过实践证明,使用Apache服务器的目录级配置文件.htaccess文件保护目录比使用其他方式更为有效和安全。更重要的是,使用.htaccess的方式进行设置,不需要编写程序就可以实现,具体操作比较容易。
(1)目录级配置文件.htaccess
.htaccess是一个纯文本文件,其中存放着Apache服务器配置相关的一些指令,它类似于Apache的站点配置文件,如httpd.conf文件。.htaccess与httpd.conf配置文件不同的是,它只作用于此目录及其所有子目录。另外,httpd.conf是在Apache服务启动的时候就加载的,而.htaccess只有在用户访问目录时加载,其中,修改.htaccess文件不需要重启Apache服务器。.htaccess的功能包括设置网页密码、设置发生错误时出现的文件、禁止读取文件、重新定向文件等。
在需要针对目录改变服务器的配置,而对服务器系统没有root权限时,应该使用.htaccess文件。如果服务器管理员不愿意频繁修改配置,则可以允许用户通过.htaccess文件自己修改配置,尤其是在一台机器上提供多个用户站点,而又期望用户可以自己改变配置的情况下,一般会开放部分.htaccess的功能给使用者自行设置。
注意:
.htaccess是一个完整的文件名,不是***.htaccess或其他格式。
如何允许用户使用.htaccess文件呢?在Apache服务器的配置文件httpd.conf中,查找服务器的根目录的配置信息:
<Directory "e:/wamp/www/">
……
AllowOverride None
……
</Directory>
在此块配置项中,把“AllowOverride None”修改成“AllowOverride All”,即允许Apache服务器调用.htaccess文件,在需要时针对目录改变服务器的配置。
提示:
httpd. conf配置文件中的AllowOverride会根据设定的值决定是否读取目录中的.htaccess文件,来改变原来所设置的权限。为避免用户自行建立.htaccess文件修改访问权限,httpd.conf文件中默认设置每个目录为:AllowOverride None。
All:读取.htaccess文件的内容,修改原来的访问权限。
None:不读取.htaccess文件。
(2)实现禁止访问除入口文件之外的PHP文件
在Apache服务器的目录级配置文件.htaccess文件中添加“deny from all”(表示全部IP地址都不许可,相对地,“allow from all”表示全部都允许),即可实现包含该.htaccess的文件夹不允许被外部访问。接下来创建protected目录,并把需要保护的文件移到该目录下。
改进后的目录结构如下:
│ index.php
├─css
├─framework
│ .htaccess
│ CController.php
├─images
├─js
└─protected
│ .htaccess
├─controllers
│ DefaultController.php
│ SiteController.php
├─models
│ Article.php
└─views
index.php
1.4.2节中对原有的MVC模式进行了改进,在入口文件中实现了URL的解析。用户的每一次请求都指向服务器的唯一可访问文件。经过解析URL,最终转向所要访问的控制器。但是当系统日趋复杂和多样时,如URL参数和POST数据需要进行必要的检查和特殊字符过滤、记录日志、访问统计等,如果各种可以集中处理的任务都放在入口文件执行,那么将会出现代码重复、业务逻辑混乱且分散的情况。因此,为了降低系统代码逻辑的复杂度,进一步集中控制系统,并提高系统的安全控制能力,以及可维护性、可重用性和可伸缩性,本节中对原有的MVC模式进行了改进,提出了应用(前端控制器)的概念,实现MVC在复杂系统中的前端控制器开发模式优化策略。
采用前端控制器模式,提供一个处理不同请求的中心,处理工作包括安全事务、视图选择、异常处理和响应内容的生成,通过将这些处理工作集中在一点进行,大大降低了PHP代码量,同时也减少了视图层的程序逻辑,保证了在不同请求之间可以大量地重用逻辑代码。
应用(前端控制器)的URL解析功能在文件framework/Cweb- Application.php文件中实现,流程图如图1-9所示。解析URL代码如下。
图1-9 应用中解析URL流程图
<?php
class CWebApplication {
public $name;
//默认控制器是SiteController
public $defaultController="site";
//默认动作是actionIndex
public $defaultAction="index";
//执行应用
public function run()
{
//如URL为http://hostname/index.php?r=controllerid/actionid
//得到controllerid/actionid
if(!empty($_GET['r']))
{
$route=$_GET['r'];
//得到controllerid赋值给成员变量
$pos=strpos($route,'/');
$this->defaultController=substr($route,0,$pos);
$this->defaultController=strtolower($this->defaultController);
//得到actionid赋值给成员变量
$this->defaultAction=(string)substr($route,$pos+1);
}
//得到控制器类名
$className=ucfirst($this->defaultController).'Controller';
//获得控制器文件路径
$classFile="./protected/controllers/".$className.'.php';
//最后一步操作:该类文件存在及该类存在,则导入并调用acion方法
if(is_file($classFile))
{
if(!class_exists($className,false))
{
require($classFile);
$class= new $className();
$functionName="action".ucfirst($this->defaultAction);
$class->$functionName();
}
}
}
}
对于系统中的某些类来说,只有一个实例很重要。例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。例如,在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此,确保系统中某个对象的唯一性(即一个类只能有一个实例)是非常重要的。
我们希望系统中的应用(前端控制器)只有一个实例对象而且该实例对象易于外界访问,从而方便应用实例对象个数的控制并节约系统资源,单例模式是最好的解决方案之一。
单例模式是一种常用的软件设计模式。其要点有3个:一是类只能有一个实例,二是它必须自行创建这个实例,三是它必须自行向整个系统提供这个实例。
从具体实现角度来说,就是以下3点:一是单例模式的类只提供私有的构造方法,二是类定义中含有一个该类的静态私有对象,三是该类提供静态的公有方法用于创建或获取它本身的静态私有对象。在framework/CWebApplication.php文件中添加下面所示的部分代码。
<?php
class CWebApplication {
……
//定义类的静态私有对象
private static $_app;
//构造方法在实例对象被创建时自动执行
private function __construct($config=null)
{
//获取配置文件中的数组
}
//静态的公有方法用于创建它本身的静态私有对象
public static function createApplication($config=null)
{
if(self::$_app===null)
self::$_app = new CApplication($config);
return self::$_app;
}
//静态的公有方法用于获取它本身的静态私有对象
public static function app()
{
return self::$_app;
}
//执行应用
public function run(){……}
}
默认情况下,应用是一个CWebApplication的实例。要自定义它,通常需要提供一个配置文件以在创建应用实例时初始化其属性值。这就好比去组装计算机,客户拿来具体的配置单,按照要求就可以组装符合要求的计算机。而CWebApplication就是组装工人,配置单就是下面要说明的配置文件。
配置信息在配置文件中以数组元素的方式存放,一个元素就是两个字符串组成的键值对,一个字符串是键(key),另一个字符串是这个键的对应的值(value)。大多数的系统都有一些配置常量,将这些常量放在配置文件中,系统通过访问这个配置文件取得配置常量,就可以通过修改配置文件而无须修改程序达到更改系统配置的目的。系统也可以在配置文件中存储一些工作环境信息,这样在系统每次访问时,这些信息可以运行在每一个应用的生命周期中。
通常在一个单独的PHP 脚本(protected/config/main.php)中保存这些配置。在脚本中,通过以下方式返回此配置数组。
<?php
return array(
//默认控制器
"defaultController"=>"default",
//通过应用全局访问方法Yii::app()->name;直接访问。
"name"=>"my application",
);
?>
在应用的构造方法中添加对配置文件操作的代码:
<?php
class CWebApplication {
……
//构造方法在实例对象被创建时自动执行
private function __construct($config=null)
{
//获取配置文件中的数组
if(is_string($config))
$config=require($config);
/*
把配置文件中数组定义的元素赋值给CWebApplication类中相同成员属性
array(
"name"=>"my application",
"defaultController"=>"default",
);
*/
if(is_array($config))
{
/*
第一次循环:$this->name=“my application”;
第二次循环:$this->defaultController=“default”;
*/
foreach($config as $key=>$value)
$this->$key=$value;
}
}
……
}
要应用此配置,将配置文件的名字作为参数传递给应用的构造器,或像下面这样传递到CApplication::createApplication(),这通常在入口脚本中完成。
<?php
//加载framework文件夹下的所有文件,为了清楚演示,这里没有用__autoload()
require "./protected/framework/CController.php";
require "./protected/framework/CApplication.php";
//定义配置文件路径
$config="./protected/config/main.php";
//创建应用(前端控制器)对象
$app=CApplication::createApplication($config);
$app->run();
?>
实现前端控制器模式后,框架执行流程如图1-10所示。
图1-10 框架执行流程
1.用户发送访问URL的请求,Web服务器通过执行入口脚本index.php处理此请求。
2.入口脚本创建一个应用实例并执行。
3.创建一个所请求控制器的实例以进一步处理用户请求。控制器决定动作指向控制器类中的action方法。
4.控制器调用模型实例对象从数据库中读取数据。
5.渲染视图。
6.视图读取并显示模型的数据。
7.动作完成视图渲染并将其呈现给用户。
在前几节中,介绍了框架的概念及使用框架技术的优势,并结合PHP的发展历史总结了现阶段PHP及其框架技术的应用领域。
为了让读者更好地理解Yii框架,并认识到框架技术并不是多么复杂,本节自定义了一个MVC框架,实现控制器、模型、视图的分离,创建单一入口模式的目录结构,实现应用的预处理、初始化和执行。显然,自定义的框架功能还很少,不能满足框架作为“半成品”的需要。因此,接下来要进入到Yii框架的学习,因为Yii提供了目前Web 2.0应用开发所需要的几乎一切功能。下面是这些特性的简短说明。
本章首先介绍了PHP语言的发展历史及其适合的应用领域,希望读者对PHP技术未来的发展空间充满信心。
框架是特定应用程序的“半成品”,是面向对象系统最大的复用方式。在项目开发中,如果使用一个成熟的框架,就相当于让别人帮你完成一些基础工作。
本章中自定义框架部分是PHP面向技术的实践,分别实现了MVC框架模式、单入口文件设计模式和前端控制器设计模式,目的是为了让读者能够更好地理解Yii框架的工作机制,并为后续Yii框架的学习做好准备。
下面我们将正式进入Yii框架的学习,希望读者通过学习Yii框架,掌握Web开发相关的内容。
从本章开始,我们将通过由浅入深的方式介绍Yii框架的各个部分,希望读者逐步了解Yii框架。
Yii框架作为一种热门的PHP框架技术,在当前的PHP开发领域正受到越来越多的关注。本节将首先介绍什么是Yii、Yii有什么优点、开发团队、性能及应用案例等内容。通过对本节的学习,读者会对Yii有一个大致的认识。
Yii 是一个基于组件的高性能PHP框架,用于快速开发大型Web应用。它使Web开发中的可复用度最大化,可以显著提高Web应用开发速度。Yii读作“易(Yee)”或“[ji:]”,这个名字是“Yes it is!”的缩写。
“Yii快不快?安全吗?专业吗?是否适用于我的下一个项目?”“Yes,it is!”
Yii是创始人薛强的心血结晶,于2008年1月1日开始开发。在此之前,薛强开发和维护PRADO框架多年,他从这些年的经验和所得到的反馈中了解到,用户需要一个更容易、可扩展、更快速的基于PHP 5的框架,以满足应用程序开发人员不断增长的需求。
Yii正式发布于2008年10月,最初是alpha版本,与其他基于PHP的框架表现相比,令人印象深刻,立即引起非常积极的关注。2008年12月3日,Yii 1.0正式发布;2013年8月11日,发布稳定版本1.1.14;2014年4月13日,发布了Yii 2.0的beta测试版。本书中采用的是目前使用广泛且相对比较成熟的Yii 1.1.17版本。
Yii框架有一个不断成长的开发团队,团队部分成员见表2-1。
表2-1 开发团队中的部分成员
姓 名 |
地 点 |
时 间 |
职 责 |
---|---|---|---|
Qiang Xue(qiang) |
美国华盛顿 |
创建者 |
参与所有事务 |
Wei Zhuo(wei) |
澳大利亚悉尼 |
2008年1月加入 |
核心框架的开发和项目的网站 |
Sebastián Thierer(sebas) |
阿根廷 |
2009年9月加入 |
开发官方的扩展库和核心框架的发布 |
Александр Макаров(samdark) |
俄罗斯 |
2010年3月加入 |
核心框架开发 |
Maurizio Domba(mdomba) |
克罗地亚 |
2010年8月加入 |
核心框架开发 |
Y!! |
德国 |
2010年8月加入 |
核心框架开发 |
Jeffrey Winesett(jefftulsa) |
美国德州 |
2010年9月加入 |
官方文档和市场推广 |
István Beregszászi(pestaa) |
匈牙利 |
2009年9月加入 |
维护论坛和核心框架的发布 |
Jonah Turnquist(jonah) |
美国加利福尼亚 |
2009年9月加入 |
开发官方的扩展库 |
要运行一个基于Yii框架的Web应用,需要有一个支持PHP 5.1.0或以上版本的Web服务器。
对于打算使用Yii的开发者来说,懂得面向对象编程(OOP)会非常有帮助,因为Yii是一个纯面向对象的框架。
Yii在设计时借鉴和集成了很多其他著名Web编程框架和应用的思想。
可以通过“Hello World”程序在同一计算机下运行的性能对比检测不同框架的性能。测试环境如下所示。
Yii是一个高性能的框架,表2-2展示了其与其他流行的PHP框架比较时的高效率。在表2-2中,“fetches/sec”代表“每秒查询次数”,这个数字越大,此框架的性能越高。在这个比较中,Yii可以达到原生PHP的32%,除了比原生PHP差一些,比其他框架都强不少。
表2-2 PHP框架性能比较
框架/代码类型 |
性能(每秒查询次数) |
---|---|
原生HTML |
1318.9 fetches/sec |
原生PHP |
8220.17 fetches/sec |
CodeIgniter 1.7.0 |
655.156 fetches/sec |
CodeIgniter 1.5.4 |
768.199 fetches/sec |
Zend Framework 1.7 |
37.9999 fetches/sec |
Solar 1.0.0 alpha2 |
243.4 fetches/sec |
Cakephp 1.1.20.7692 |
288.4 fetches/sec |
Yii 1.0.3 |
2505.58 fetches/sec |
Yii如此快速是因为它广泛地使用“懒性加载”(lazy loading)技术。例如,直到第一次使用到这个类,才会包含进来;直到对象第一次访问,才会创造这个对象。
提示:
为什么用“Hello World”?进行“Hello World”的测试主要是为了达到我们的目标,如找出每个框架的最小代价。很多人抱怨说应用程序“hello world”很没意义,因为真实世界中的应用程序经常需要涉及更复杂的任务,如数据库查询。这是不对的。实际上,尤其是在一些大规模的Web 2.0应用程序中,经常遇到的情况通常是相当地接近“Hello World”。例如,应用程序要响应AJAX请求并返回当前服务器的时间。页面有大部分内容在缓存,应用程序只需要抓取缓存的内容并显示。
Yii是一个通用的Web编程框架,可以用于开发几乎所有的Web应用。它是轻量级的,而且具备成熟的缓存解决方案,特别适用于开发高流量的应用,如门户网站、论坛、内容管理系统和电子商务系统等。图2-1和图2-2是使用Yii构建的一些Web项目的 经典案例。图2-3为后台管理页面。
图2-1 门诊预约系统首页
图2-2 渡手网站首页
图2-3 内容管理系统操作页面
除上述案例之外,还有很多互联网应用项目也使用了Yii框架技术,这里不再赘述。
从Yii的官方站点www.yiiframework.com可下载Yii的程序包,单击“Download Yii”按钮即可下载,如图2-4所示。
图2-4 Yii下载
下载之后的文件夹中包含了如下3个文件夹。
├─demos 包含演示代码
├─framework 框架源码目录
└─requirements Yii配置需求检查
requirements文件夹用于确认当前服务器配置是否能满足运行Yii Web项目的要求。它将检查服务器所运行的PHP版本,查看是否安装了合适的PHP扩展模块,以及确认php.ini文件是否正确设置,如图2-5所示。
图2-5 环境配置检测
将未通过的选项调整为符合要求的。
注意:
Yii框架可以安装在文件系统的任何地方,而不是必须在Web目录中。它的 framework 目录包含了框架的代码,这也是部署Yii应用时唯一一个必要的目录。一个单独的framework 目录可以用于多个Yii应用。
在项目开发时,直接将framework目录及子目录的所有文件复制到项目根目录中即可,并不需要对这个框架源文件做任何修改,如图2-6所示。
图2-6 Yii解压缩保存目录
Yii框架中的framework目录结构功能说明如下。
|-framework 框架核心库
|--base 底层类库文件夹,包含:
CApplication(应用类,负责全局的用户请求处理,它管理的应用组件集,将提供特定功能给整个应用程序)
CComponent(组件类,该文件包含了基于组件和事件驱动编程的基础类,从版本1.1.0开始,一个行为的属性(或者它的公共成员变量,或者它通过getter和/或setter方法定义的属性)可以通过组件的访问来调用)
CBehavior(行为类,主要负责声明事件和相应事件处理程序的方法、将对象的行为附加到组件等)
CModel(模型类,为所有的数据模型提供的基类)
CModule(是模块和应用程序的基类,主要负责应用组件和子模块等)
|--caching 所有缓存方法,其中包含了Memcache缓存、APC缓存、数据缓存、CDummyCache虚拟缓存、CEAcceleratorCache缓存等各种缓存方法
|--cli Yii项目生成脚本
|--collections 用PHP语言构造传统面向对象语言的数据存储单元,如队列、栈、散列表等
|--console Yii控制台
|--db 数据库操作类
|--gii Yii代码生成器(脚手架),能生成包括模型、控制器、视图等代码
|--i18n Yii多语言,提供了各种语言的本地化数据,信息、文件的翻译服务,本地化日期和时间格式,数字等
|--logging 日志组件,Yii提供了灵活和可扩展的日志记录功能。消息记录可分为根据日志级别和信息类别。应用层次和类别过滤器,可进一步选择消息路由到不同的目的地,如文件、电子邮件和浏览器窗口等
|--messages 提示信息的多语言包
|--test Yii提供的测试,包括单元测试和功能测试
|--utils 提供了常用的格式化方法
|--validators 提供了各种验证方法
|--vendors 第三方由Yii框架使用的资料库
|--views 提供了Yii错误、日志、配置文件的多语言视图
|--web Yii所有开发应用的方法
|---actions 控制器操作类
|---auth 权限认识类,包括身份认证、访问控制过滤、基本角色的访问控制等
|---filters 过滤器,可被配置在控制器动作执行之前或之后执行。例如,访问控制过滤器将被执行以确保在执行请求的动作之前用户已通过身份验证;性能过滤器可用于测量控制器执行所用的时间
|---form 表单生成方法
|---helpers 视图助手,包含GOOGLE AJAX API,创建HTML、JSON、JavaScript相关功能
|---js JS库
|---renderers 视图渲染组件
|---services 封装SoapServer并提供了一个基于WSDL的Web服务
|---widgets 部件
|---CArrayDataProvider.php 可以配置的排序和分页属性自定义排序和分页的行为
|---CActiveDataProvider.php ActiveRecord方法类
|---CController.php 控制器方法,主要负责协调模型和视图之间的交互
|---CPagination.php 分页类
|---CUploadedFile.php 上传文件类
|---CUrlManager.php URL管理
|---CWebModule.php 应用模块管理,应用程序模块可被视为一个独立的子应用
|--.htaccess .htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置
通过.htaccess文件,可以帮用户实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能
|--Yii.php 引导文件
|--YiiBase.php YiiBase类最主要的功能是注册了自动加载类方法,加载框架要用到的所有接口
|--Yiic Yii Linux 命令行脚本
|--Yiic.bat Yii Windows 命令行脚本
|--Yiic.php Yii命令行脚本文件。这个脚本是运行在命令行执行一个预先定义的控制台命令
|--Yiilite.php 它是一些常用到的Yii类文件的合并文件。在文件中,注释和跟踪语句都被去除
因此,使用Yiilite.php将减少被引用的文件数量并避免执行跟踪语句
|--Yiit.php Yii测试脚本文件。这个脚本是为了被包括在开始的单元测试和功能测试引导文件中
要创建一个新的项目程序,将使用Yii框架附带的一个小工具yiic,这是一个命令行工具,可以快速地建立一个全新的Yii项目。不是必须使用此工具才能创建Yii项目,但使用它将节省大量的时间,并保证文件及目录的结构。
1.在如图2-7所示的“运行”对话框中输入“cmd”命令,打开命令输入框,如图2-8所示。
图2-7 “运行”对话框
图2-8 命令输入框
2.输入如图2-9所示的命令后转到Yii框架目录。
图2-9 跳转目录(change directory)
3.由于Yii自带的yiic.bat 找不到php.exe,因此需要明确php.exe文件保存路径。使用记事本工具打开framework/yiic.bat文件。
if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
修改成
if "%PHP_COMMAND%" == "" set PHP_COMMAND=C:/AppServ/php5/php.exe
注意:
如果出现“没有找到php_mbstring.dll,文件无法启动”这个提示框,则解决办法为:在php.ini文件中将extension=php_mbstring.dll 移动到extension=php_exif.dll之前即可。
因为exif要调用mbstring,所以mbstring必须在前面。
4.使用 yiic (命令行工具)在网站的根目录下创建一个新的Yii项目dscms。在Yii框架源码目录下输入命令行,如图2-10所示。
yiic webapp ../dscms
图2-10 创建项目dscms
随着在命令行执行一条简单的命令,已建立了Yii框架的目录结构和默认所需的文件。项目的目录结构并不需要开发人员手动创建,系统会在执行yiic(命令行工具)的时候自动生成大多数所需要的目录结构。自动生成目录结构及说明如下所示。
├─assets 包含公开的资源文件
├─css 包含 CSS 文件
├─images 包含图片文件
├─protected 包含受保护的文件
└─themes 包含主题
│ index-test.php 功能测试使用的入口脚本文件
│ index.php Web 入口脚本文件
提示:
上例是项目目录和框架目录不在同一级时,默认生成的目录结构。具体的每个目录和文件的作用,在应用时可以参照后面部分的详细介绍。
5.生成的代码包括了开发者创建大多数项目需要的最基本文件目录,可以在浏览器中访问如下URL来看看Yii自带项目。
只要Web服务器正在运行,就可以打开浏览器访问http://hostname/dscms/index.php。将看到Web项目程序的首页并显示Welcome to My Web Application和一些帮助信息,如图2-11所示。
http://hostname/dscms/index.php
这个项目包含3个页面:首页、联系页和登录页。首页展示一些用户登录状态的信息,联系页显示一个联系表单以便用户填写并提交他们的咨询,登录页允许用户先通过认证然后访问已授权的内容。
图2-11 默认首页效果图
使用Yii框架的命令行工具yiic创建好项目后,会发现很多地方都和之前我们自定义的框架相符合。在本章接下来的内容中,我们参照之前的内容,继续深入学习。
首先,在新的应用上编写一个“Hello World”程序来试用这个框架。“Hello World”程序在Yii中是一个简单的Web程序,它发送信息到浏览器。
一个典型Yii的Web应用程序执行流程从用户通过浏览器输入一个请求后开始,Yii应用程序首先解析该请求的信息,去查找一个对应的控制器,然后调用该控制器内的动作方法。在该动作方法中,可以渲染一个特定的视图,然后将渲染后的内容返回给用户。如果需要处理数据,那么控制器可以调用模型来处理创建、读取、更新和删除(CRUD)等数据库操作。
本章中的这个“Hello World”示例,只需要一个控制器和视图,不处理任何数据,这样将不需要模型。接下来让我们开始创建控制器。
创建一个新的控制器,PHP文件名是MessageController.php,并放到控制器目录protected/controllers中。新创建的MessageController类继承应用程序的基类Controller,它的位置是protected/components/Controller.php。由于MessageController类继承了框架的基础类Ccontroller,因此,它继承了Ccontroller类默认的所有行为。在MessageController类中创建一个actionOutput()动作方法。下面的代码是MessageController类的内容。
<?php
class MessageController extends Controller
{
public function actionOutput()
{
$this->render('helloWorld');
}
}
视图文件与控制器关联,默认存放在protected/views/message下。编辑protected/views/ message/helloWorld.php,修改成如下代码:
<h1>Hello, World!</h1>
保存代码,并访问http://hostname/dscms/index.php?r=message/output,页面如图2-12所示。
图2-12 输出“Hello,World!”效果图
回顾一下运行这个应用程序时Yii框架是如何分析的,如图2-13所示。
图2-13 输出“Hello,World!”工作流程图
1.用户发送了访问http://hostname/index.php?r=message/output的请求, Web 服务器通过执行入口脚本index.php处理此请求。
2.入口脚本创建了一个应用实例对象并执行。
3.应用实例对象分析这个URL,controllerID是message,它将告诉Yii应该去请求MessageController.php文件,这个文件的位置是protected/controllers/MessageController.php。Yii还发现,actionID指定的是output,因此,会调用MessageController类中的actionOutput()操作方法。
4.actionOutput()方法会渲染helloworld.php视图文件,这个文件的位置是protected/ views/message/helloworld.php。
5.动作方法完成视图渲染并将其返回给浏览器。
Yii框架为了提高安全性,把生成的项目中大多数文件都保存在protected文件夹中,并且不可以被Apache服务器访问。Yii框架应用就需要通过入口文件调用。入口文件可以自己定义名称,如index.php、admin.php、blog.php等之类文件名都可以。但在入口文件中至少要编写两行代码:
编写的入口文件index.php 的内容示例如下所示。
<?php
// 定义Yii框架引导文件路径
$Yii=dirname(__FILE__).'/../framework/Yii.php';
//定义应用配置文件路径
$config=dirname(__FILE__).'/protected/config/main.php';
// 在生产环境中请删除此行
defined('Yii_DEBUG') or define('YII_DEBUG',true);
//导入Yii的引导文件
require_once($Yii);
// 创建一个应用实例并执行
Yii::createWebApplication($config)->run();
Yii应用可以按常量“YII_DEBUG”的值运行在调试模式或生产模式。在默认情况下,此常量值定义为false,意为生产模式,以最高效率运行。在调试模式下,框架要维护许多内部日志,并且在错误产生时提供了丰富的调试信息。查看framework/yii.php文件,该文件调用了YiiBase.php文件,在此文件中定义了如下代码:
defined('YII_DEBUG') or define('YII_DEBUG',false);
由以上代码可知,如果要运行调试模式,则需要在包含yii.php文件之前定义此常量为true。
应用是指请求处理中的最上层对象,它的主要任务是分析用户请求并将其分派到合适的控制器中以做进一步处理。
Yii框架静态结构图如图2-14所示,采用应用(前端控制器)模式。应用提供了一个处理不同请求的中心,处理工作包括安全事务、视图选择、异常处理和响应内容的生成,通过将这些处理工作集中在一点进行,大大减少了PHP代码量,同时也降低了视图层的程序逻辑,保证了在不同请求之间可以最大限度地重用相同逻辑代码。
图2-14 Yii框架静态结构图
提示:
Yii中的应用由入口脚本创建为一个单例对象。这个应用单例对象可以在任何地方通过Yii::app() 访问。
Yii框架实现了MVC框架模式的设计思想,把应用的输入、处理、输出流程按照模型、视图、控制器的方式进行分离,直接把模型、视图和控制器分别保存到了不同目录下,如图2-15所示。
实现MVC框架模式的目标是将业务逻辑从用户界面中分离,这样开发者就可以更容易地改变每一部分而不会影响其他。
图2-15 Yii框架中的MVC目录结构
Yii框架中的控制器是CController或其子类的实例,它在用户请求时由应用创建。当一个控制器运行时,它执行所请求的动作,动作通常会引入所必要的模型并渲染相应的视图。动作的最简形式,就是一个名字以action开头的控制器类方法。
下面的代码定义了MessageController控制器类,其中包括动作方法actionOutput(),保存在MessageController.php文件中。
class MessageController extends CController{
public function actionOutput()
{
$this->render('helloWorld');
}
}
注意:
控制器通常有一个默认的动作。当用户的请求未指定要执行的动作时,默认动作将被执行。默认情况下,默认的动作名为index。它可以通过设置CController::defaultAction修改。
在Yii框架自带的演示代码中,有的控制器继承Controller类,有的却继承了CController类,CController和Controller的关系是什么?首先来分析下面的源码。
<?php
/**
* Controller is the customized base controller class.
* All controller classes for this application should extend from this base class.
*/
class Controller extends CController
{
/**
* @var string the default layout for the controller view. Defaults to '//layouts/column1',
*meaning using a single column layout. See 'protected/views/ layouts/ column1.php'.
*/
public $layout='//layouts/column1';
/**
* @var array context menu items. This property will be assigned to {@link CMenu::items}.
*/
public $menu=array();
/**
* @var array the breadcrumbs of the current page. The value of this property will
* be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links}
* for more details on how to specify this property.
*/
public $breadcrumbs=array();
}
由源码可知,Controller是CController的子类,作为一个组件保存在Components文件夹下,定义了$breadcrumbs、$layout和$menu 3个成员属性的值,这3个成员属性可以在布局文件中使用。
提示:
通过这两个类可知,在使用Yii框架创建应用时,可以继承所有的框架自带类,并且可以根据需要重新定义public类型成员属性的值。
视图是一个包含了主要的用户交互元素的PHP脚本,可以包含PHP语句,但是建议这些语句不要去改变数据模型,且最好能够保持其单纯性(单纯作为视图)。为了实现逻辑和界面分离,大段的逻辑应该被放置于控制器或模型中,而不是视图中。
视图有一个名字,当渲染(render)时,这个名字会被用于识别视图脚本文件,视图的名称与其视图脚本名称是一样的。例如,视图helloWorld的名称出自一个名为helloWorld.php的脚本文件。要渲染时,需通过传递视图的名称调用CController::render()。这个方法将在protected/views/ControllerID目录下寻找对应的视图文件。也就是说,对应在protected/views中,文件夹名称应该和默认路由中的控制器ID保持一致。
在视图脚本内部,可以通过$this来访问控制器实例。可以在视图中以$this->propertyName的方式读取控制器的任何属性。也可以用以下推送的方式传递数据到视图文件。
$this->render('helloWorld', array( 'var1'=>$value1));
在以上的代码中,render()方法将提取数组的第二个参数到变量中。其产生的结果是,在视图脚本中可以直接访问变量$var1。控制器父类CController的render()方法的详细说明见表2-3。
表2-3 CController的成员方法render ()
public string render(string $view, array $data=NULL, boolean $return=false) |
||
---|---|---|
$view |
string |
视图文件名 |
$data |
array |
数组中元素的键转化为在视图文件中可以使用的变量名,对应元素的值转化为该变量的值 |
$return |
boolean |
当为“true”时,渲染视图的结果调用“renturn”语句返回当为“false”时,渲染视图的结果调用“echo”语句输出 |
{return} |
string |
返回渲染视图的结果 |
本章以输出“Hello,World!”为案例,介绍了Yii框架的执行流程。本章是第1章中自定义框架部分在Yii框架中的体现,希望读者能够参照“自定义框架”部分(1.4节)深入理解Yii框架中的相关内容。
读者通过学习本章内容首先能够理解入口文件需要包含Yii框架的引导文件,并且按指定的配置创建Web应用实例并执行。
其次,能够理解应用(前端控制器)是最上层对象,主要任务是分析用户请求并将其分派到合适的控制器中以做进一步处理。
再次,能够掌握Yii框架中控制器和视图的编写规范,完成控制器渲染视图的操作。
下一章将深入介绍视图部分的内容。