Yii2框架从入门到精通

978-7-115-49211-1
作者: 刘琨
译者:
编辑: 陈聪聪
分类: PHP

图书目录:

详情

本书站在框架设计的高度,从源代码级别剖析了Yii2框架的工作机制。共分为15章,其内容涵盖了PHP框架的概念,Yii2框架的工作流程,布局的概念及作用,模块的概念及作用,ActiveRecord模型的原理和作用,ActiveRecord模型的查询方法,小物件的概念及作用,小物件CActiveForm的作用以及调用方法,模型验证的概念及作用等。

图书摘要

版权信息

书名:Yii2框架从入门到精通

ISBN:978-7-115-49211-1

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

著    刘 琨

责任编辑 陈聪聪

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315


Yii是一个基于组件、用于开发大型Web应用的高性能PHP框架,它提供了当今Web 2.0应用开发所需要的大部分功能,是极具开发效率的PHP框架之一。

Yii 2.0版是一个完全重写的版本,采用了最新的技术和协议。本书以框架设计的高度,从源代码级别剖析了Yii2框架的工作机制。全书共分为15章,其内容涵盖了PHP框架的概念、Yii2框架的工作流程、布局的概念及作用、模块的概念及作用、ActiveRecord模型的原理和作用、ActiveRecord模型的查询方法、小部件的概念及作用、小部件ActiveForm的作用以及调用方法、模型验证的概念及作用、Yii2框架中的AJAX验证、与用户登录相关的内容、Yii2框架中基于角色的访问控制系统的设计与实现、Memcached缓存在Yii2框架中的使用、日志在Yii2框架中的实现,以及Yii2框架中URL管理组件。此外,本书还包含3个附录,分别介绍了HTTP状态消息、Yii2相关PHP语法介绍以及依赖管理工具Composer的内容。

本书适合Web开发人员,以及有一定的PHP开发基础进而希望通过学习使用框架来提升开发能力的读者阅读。


本书不是简单地介绍如何使用Yii框架,而是站在框架设计的高度,从源代码级别对其进行深度剖析。本书首先介绍PHP框架技术的概念及其应用领域,然后开始仿照Yii框架源代码自定义框架,其中包括了MVC设计模式、单入口模式和应用(前端控制器模式)的实现。充分了解这部分知识后,读者会对Yii框架有初步的认识,从而深入进行Yii框架的学习。

本书可帮助那些希望借助框架进行开发的读者熟悉Yii的基本结构、规范和开发流程,轻松掌握常用的Yii组件,敏捷、稳健地开发Web 2.0应用程序。

由于PHP开源的特性,虽然作者使用PHP框架技术多年,但将庞大数量的碎片知识整合为一本厚达几百页的书,其中的辛酸非三言两语能够道破。

感谢家人的鼓励,是他们的宽容让我能够安心做好每件事。感谢河北地质大学李文斌老师的耐心指导。感谢韩立刚老师让我回归到教育的本真,静心琢磨提高课堂教学的质量。感谢同事吕建军一直以来无私地向我分享案例、数据和发现。感谢我的学生对我的支持,这是我克服困难的原动力。

本书主要由刘琨写作,参与资料整理的其他人员有刘卓、刘云龙、贾春华、刘雄章、郭元鹏、张继颖、王子乾、刘鑫钰、魏晓特、杨洪昌、郝建瀛、杨硕、柳航、杨姗姗、刘禹含、易江梅等。

由于作者水平有限,书中难免存在不足和疏漏之处,恳请读者批评指正。本书作者的邮箱地址为71873467@qq.com。

为了方便读者更好地学习,作者在51CTO学院创建了本书内容视频,读者可以在51CTO学院搜索“《Yii2从入门到精通》配套视频课程”。欢迎读者加入QQ群231113585获取图书配套代码,并和其他读者一起交流学习体会和心得。


本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。

本书提供如下资源:

要获得以上配套资源,请在异步社区本书页面中点击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,单击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/selfpublish/submission即可)。

如果您是学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。

异步社区

微信服务号


Yii2框架基于PHP语言,本书就从PHP语言的发展历史说起。本章首先介绍PHP语言发展历史及其适合的应用领域,然后讲解PHP框架技术的概念并仿照Yii2框架代码自定义一个框架,其中包括MVC框架模式、单入口模式和应用(前端控制器模式)的实现。读者在充分了解了这部分内容后,将正式进入到Yii2框架的学习。

PHP最初为Personal Home Page的缩写,但现在已经正式更名为PHP: 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也在这个时候改称为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的各种管理软件,如贸易公司及其下属销售中心使用的

图1-1 PHP应用领域

分销系统等。第三大类是定制型、功能型和工具型网站,类似CNZZ网站的访问情况统计。还有就是硬件管控软件的GUI,如路由器中的配置管理页面。

我相信,在当今这个互联网的时代,PHP语言和它的框架技术会有更加辉煌的未来!

框架(Framework)是在给定的问题领域内,实现了应用程序的一部分设计,是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法。简单来说,就是一个“半成品”,帮助项目把“骨架”搭好,并提供丰富的组件库,只需要增加一些内容或调用一些提供好的组件就可以完成自己的系统。

如图1-2所示,已经有一个成型的房子“骨架”和一些建筑材料,我们可以把它比喻成一个程序的框架。其中“骨架”可以看作是为我们创建的项目管理结构(半成品),而建筑材料则相当于为我们提供的现成组件库。在这个已有房子框架结构的基础上,结合现成的建筑材料,再经过我们的“装修”,就可以将这个“半成品”建造成私有住宅、办公楼、超市或酒吧等。同理,使用程序框架也会很快开发出个人主页、OA系统、电子商城和SNS系统等软件产品。

图1-2 框架说明

框架的最大好处之一就是重用。面向对象系统获得最大的复用方式就是框架,一个大型的应用系统往往可能由多层互相协作的框架组成。Web系统发展到今天已经变得很复杂,特别是服务器端软件,涉及的知识、内容和问题已经非常多。在项目开发中,如果使用一个成熟的框架,就相当于让别人帮你完成一些基础工作(大约50%以上),而你只需要集中精力完成系统的业务逻辑设计即可。

框架一般是成熟稳健的,它可以处理系统的很多细节问题,如事物处理、安全性、数据流控制等。

此外,框架一般经过多人反复使用,其结构和扩展性已都得到优化,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。

框架也可以将问题划分开来各个解决,易于控制,易于延展,易于分配资源。应用框架强调的是软件的设计重用性和系统的可扩充性,以缩短大型应用软件系统的开发周期,提高开发质量。

框架能够采用一种结构化的方式对某个特定的业务领域进行描述,也就是将这个领域相关的技术以代码、文档、模型等方式固化下来。

在学习Yii2框架前,我们先自定义一个框架,这样能更好地掌握框架技术的工作机制,为以后学习Yii2框架做好准备。同时,设计一个自己的PHP框架并一步步地实现,可以及时融入最新的思想和理念。更重要的是,属于自己的框架可以根据项目的需要为其量身定制。

那么,就先从实现MVC框架模式开始。

Yii2使用了Web开发中广泛采用的MVC框架模式,因此,使用者在使用Yii2建立应用系统时,必须对MVC的原理有一些了解。MVC一直以来是Yii2框架初学者很难跨过的一个障碍,本节仿照Yii2框架代码实现MVC的软件架构,希望能够通过深入浅出的方式,让读者对MVC有清楚的认识。

1.MVC框架模式的工作原理

传统的基于PHP语言的Web应用程序把PHP代码和HTML、CSS、JavaScript代码混合在一起,这样不利于代码的后期维护,同时也不利于程序功能的扩展。基于MVC的应用程序,把应用程序中的各个功能独立出来,可以很好地实现程序功能的分工合作,对于代码的维护和扩展也十分方便。

MVC是一种目前广泛流行的框架模式。近年来,随着PHP的成熟,它正在成为在LAMP平台上推荐的一种框架设计模式,也是广大PHP开发者非常感兴趣的框架设计模式,并有不断成长的趋势。随着网络应用的快速增加,MVC模式对于Web应用的开发无疑是一种非常先进的设计思想。无论用户选择哪种语言,无论应用多么复杂,都能为用户理解分析应用模型提供基本的分析方法,为用户构造产品提供清晰的设计框架,为用户的软件工程提供规范的依据。MVC的设计思想是把一个应用的输入、处理和输出流程按照视图(View)、模型(Model)和控制器(Controller)的方式进行分离,这样的一个应用分成3个层——模型层、视图层和控制层,下面分别进行介绍。

(1)视图

视图是用户看到的并与之交互的界面。视图可以向用户显示相关的数据,并能接收用户的输入数据,但它并不进行任何实际的业务处理。视图可以向模型查询业务状态,但不能改变模型。视图还能接收模型发出的数据更新事件,从而对用户界面进行同步更新。作为视图,它只是作为一种输出数据并允许用户操作的方式。

(2)模型

在MVC的3个部件中,模型是主体部分,包含业务数据和业务逻辑,同时负责访问和更新持久化数据。一个模型能为多个视图提供数据,每个视图都从不同角度来表达模型。由于应用于模型的代码只需写一次就可以被多个视图重用,因此降低了代码的重复性。

(3)控制器

控制器负责协调整个应用程序的运转,作用就是接收浏览器端的请求。它接收用户的输入并调用模型和视图去完成用户的需求,当用户单击Web页面中的超链接或发送HTML表单时,控制器本身不输出,只是接收请求并决定调用哪个模型去处理浏览器端发出的请求,然后确定用哪个视图来显示模型处理返回的数据。

MVC处理过程如图1-3所示,首先控制器接收用户的请求,并决定应该调用哪个模型来处理;然后模型根据用户请求进行相应的业务逻辑处理,并返回数据;最后控制器调用相应的视图来格式化模型返回的数据,并通过视图呈现给用户。

图1-3 MVC设计模式

2.MVC模式的优点

使用PHP开发出来的Web应用,初始的开发模板就是混合的数据编程。例如,直接向数据库发送请求并用HTML显示,开发速度往往比较快,但由于数据页面的分离不是很直接,因此很难体现出业务模型的样子或者模型的重用性。产品设计弹性力度很小,很难满足用户的多样化的需求。MVC要求对应用分层,虽然要进行额外的工作,但产品的结构清晰,产品的应用通过模型可以得到更好的体现。

首先,最重要的是应该有多个视图对应一个模型的能力。在目前用户需求快速变化的情况下,可能有多种方式访问应用的要求。例如,订单模型可能有本系统的订单,也有网上订单,或者其他系统的订单,但对于订单的处理都是一样,也就是说,订单的处理是一致的。按照MVC设计模式,一个订单模型以及多个视图即可解决问题。这样减少了代码的复制,即减少了代码的维护量,一旦模型发生改变,也易于维护。其次,由于模型返回的数据不带任何显示格式,因而这些模型也可直接应用于接口的使用。再次,由于一个应用被分离为3层,因此有时改变其中的一层就能满足应用的改变。面对一个应用的业务流程或者业务规则的改变,只需改动MVC的模型层。

控制器还有一个好处,就是可以用它来连接不同的模型和视图去完成用户的需求,这样它可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后通过视图将处理结果显示给用户。

最后,MVC还有利于软件工程化管理。由于不同的层各司其职,每一层不同的应用具有某些相同的特征,因此有利于通过工程化、工具化特性产生管理程序代码。

综上所述,MVC是构筑软件的常用框架模式,它将业务处理与显示分离,强制地将应用分为模型、视图及控制层。总之,MVC模式会使得应用更加强壮、更加有弹性、更加个性化。

3.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="#">更多 &gt;&gt; </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="#">更多 &gt;&gt; </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
namespace models;
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:在vendor/Controller.php文件中创建控制器的基类Controller,并实现控制器渲染视图方法render(),这个方法的功能是加载指定目录下的视图文件,并将控制器中的数据传递到视图文件中。

<?php
namespace vendor;
class Controller{
    /**
     加载指定目录下的模板文件,并将控制器中的数据传递到视图文件中
     @param    string    $fileName    提供模板文件的文件名
     @param array                变量名=>变量值
     */
    public function render($viewName, $data){
        extract($data, EXTR_PREFIX_SAME,'data');//将数组$data变成变量的形式
        require($viewName);//包含视图文件
    } 
}
?>

步骤3:创建controllers/DefaultController.php文件,创建控制器DefaultController继承父类Controller,创建首页管理方法actionIndex(),在其中创建模型Article对象,并调用find()方法获取数据,渲染视图,并把数据输出到视图页面。

<?php
namespace controllers;

//use只是使用了命名空间,但是要想调用类,必须要加载类文件
require '../vendor/Controller.php';//导入框架文件
require '../models/Article.php';//导入文章表模型类文件

use framework\Controller;
use models\Article;

class DefaultController extends Controller
{
    //首页管理
    public function actionIndex()
    {
        //创建模型对象
        $article=new Article();
        //获得数据
        $result=$article->find();
        //渲染视图,并把数据输出到视图页面
        return $this->render("../views/index.php",["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,提取出控制器名和动作方法名,创建相应控制器实例对象,并执行动作方法。

本节首先介绍系统多个请求入口设计带来的不便,然后介绍单一请求入口设计模式实现原理。本节的学习目标是明确单一入口文件设计模式的优点,避免在以后的开发项目中出现多入口。

1.入口文件设计

系统中凡是能够被访问的PHP文件都称为入口文件。如果用户的不同请求直接对应到Web服务器中的不同PHP文件,即系统是多入口设计。在刚开始学习PHP的时候,通常一个项目都会这样做:

又或者在1.4.1节实现MVC框架模式后,访问不同的控制器类文件,如DefaultCon- troller.php或SiteController.php。

对于这些项目来说,都有多个入口文件,随着项目规模的不断扩大,多入口的设计缺陷会越来越明显,如系统目录结构混乱,后期维护困难,容易暴露程序漏洞,不便于系统的统一管理等。为了避免多入口设计带来的诸多问题,可以使用单一入口设计模式。单一入口设计模式就是一个文件处理所有的HTTP请求,也就是说,访问任何控制器文件,无论是DefaultController.php、SiteController. php还是其他控制器类文件,每一次请求都是指向服务器的同一个文件。如入口文件index.php,该文件负责URL解析,最终转向所要访问的页面,如图1-6所示。

图1-6 单一入口文件模式

PHP单一入口模式可谓是现在一种比较流行的大型Web应用开发模式。当前比较流行的一些PHP开发框架,如Zend、ThinkPHP和Yii2等都是采用单一入口模式。

使用单一入口文件模式的优点如下。

2.入口文件中实现URL的解析

在上文中提到入口文件的URL解析,即入口文件会将原始请求转发给相应的处理控制器,完成具体的业务处理。例如,有以下URL地址:

http://<hostname>/
http://<hostname>/index.php
http://<hostname>/index.php?r=site
http://<hostname>/index.php?r=site/index

我们希望上面所有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"; 
    //http://localhost/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';
    //加上命名空间
    $className_ns = 'controllers\\'.ucfirst($defaultController).'Controller';
    //获得控制器文件路径
    $classFile="./controllers/".$className.'.php';
    //最后一步操作:该类文件存在则导入,该类存在则创建对象并调用acion方法。
    if(is_file($classFile))
    {
        if(!class_exists($className,false))
        {
            require($classFile);
            $class= new $className_ns();
            //$class= new controllers\.$className();
            $functionName="action".ucfirst($defaultAction);
            $class->$functionName();//actionList()
        }    
    }
?>

由上面的程序可知,默认的控制器是SiteController,默认的执行方法是actionIndex()方法。控制器的类名首字母大写,以“Controller”结尾,而且控制器类文件必须保存在controllers文件夹中;动作方法名必须以“action”为前缀,acitonID首字母大写。从这段程序中也可以了解到代码规范的重要性,因为文件名或类名等都会在程序中使用。同样的道理,在将要学习的Yii2框架开发过程中,也要遵守一定的编码规范。例如,命名类时,使用驼峰风格,即每个单词的首字母大写并连在一起,中间无空格;变量名和方法名应该使它们的第一个单词全部小写,其余单词首字母大写,以使其区别于类名,如$basePath、runController();对私有类成员变量来说,推荐以下划线作为其名字前缀,如$_actionList。

提示:

 

为了使PHP语言开发的框架能够遵循共同的编码风格,在2009年由几个框架的开发者组成了PHP-FIG(PHP Framework Interoperability Group)小组,一直扩展到现在已经拥有20多位成员。

实现入口文件后,框架执行流程如图1-8所示。

图1-8 框架执行流程

① 用户发出了访问URL的请求,Web服务器通过执行入口文件index.php处理此请求。

② 入口文件负责完成URL的解析,根据URL请求创建控制器并调用动作处理用户请求。

③ 控制器调用模型实例对象从数据库中读取数据。

④ 渲染视图。

⑤ 视图读取并显示模型的数据。

⑥ 动作完成视图渲染并将其返回给用户。

3.单一入口模式服务器环境配置

实现单一入口模式之后,需要确保应用根目录下,除入口文件外的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的文件夹不允许被外部访问。

1.4.2节中对原有的MVC模式进行了改进,在入口文件中实现了URL的解析。用户的每一次请求都指向服务器的唯一可访问文件。经过解析URL,最终转向所要访问的控制器。但是当系统日趋复杂和多样时,如URL参数和POST数据需要进行必要的检查和特殊字符过滤、记录日志、访问统计等,如果各种可以集中处理的任务都放在入口文件执行,那么将会出现代码重复、业务逻辑混乱且分散的情况。因此,为了降低系统代码逻辑的复杂度,进一步集中控制系统,并提高系统的安全控制能力以及可维护性、可重用性和可伸缩性,本节中对原有的MVC模式进行了改进,提出了应用(前端控制器)的概念,实现MVC在复杂系统中的前端控制器开发模式优化策略。

1.在应用中实现URL解析

采用前端控制器模式,提供一个处理不同请求的中心,处理工作包括安全事务、视图选择、异常处理和响应内容的生成,通过将这些处理工作集中在一点进行,大大降低了PHP代码量,同时也减少了视图层的程序逻辑,保证了在不同请求之间可以大量地重用逻辑代码。

应用(前端控制器)的URL解析功能在vendor/Application.php文件中实现,流程如图1-9所示。解析URL代码如下。

图1-9 应用中解析URL流程图

<?php
class Application {
     public $name;
     //默认控制器是SiteController
     public $defaultController="site";
     //默认动作是actionIndex
     public $defaultAction="index"; 
     //执行应用
     public function run()
     {
        //http://localhost/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';
        //加上命名空间
        $className_ns= 'controllers\\'.ucfirst($this->defaultController). 'Controller';
        //获得控制器文件路径
        $classFile="./controllers/".$className.'.php';
        //最后一步操作:该类文件存在及该类存在则导入并调用acion方法。
        if(is_file($classFile))
        {
            if(!class_exists($className,false))
            {
                require($classFile);
                $class= new $className_ns();

                $functionName="action".ucfirst($this->defaultAction);
                $class->$functionName();
            }
        }

     }
}

2.单例模式创建应用(前端控制器设计模式)

对于系统中的某些类来说,只有一个实例很重要。例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。例如,在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此,确保系统中某个对象的唯一性(即一个类只能有一个实例)是非常重要的。

我们希望系统中的应用(前端控制器)只有一个实例对象而且该实例对象易于外界访问,从而方便应用实例对象个数的控制并节约系统资源,单例模式是一种非常好的解决方案。

单例模式是一种常用的软件设计模式。其要点有3个:一是类只能有一个实例,二是它必须自行创建这个实例,三是它必须自行向整个系统提供这个实例。

从具体实现角度来说,就是以下3点:一是单例模式的类只提供私有的构造方法,二是类定义中含有一个该类的静态私有对象,三是该类提供静态的公有方法用于创建或获取它本身的静态私有对象。在vendor/Application.php文件中添加下面所示的部分代码。

<?php
class Application {
     ……
     //定义类的静态私有对象
     private static $_app;
     //构造方法在实例对象被创建时自动执行
     private function __construct($config=null)
     {
          //获取配置文件中的数组
     }
     //静态的公有方法用于创建它本身的静态私有对象
     public static function createApplication($config=null)
     {
          if(self::$_app===null)
               self::$_app = new Application($config);
          return self::$_app;
     }
     //静态的公有方法用于获取它本身的静态私有对象
     public static function app()
     {
          return self::$_app;
     }
     //执行应用
     public function run(){……}
}

3.应用的配置文件

默认情况下,应用是一个Application的实例。要自定义它,通常需要提供一个配置文件以在创建应用实例时初始化其属性值。这就如同去组装计算机,客户拿来具体的配置单,按照要求就可以组装符合要求的计算机。而Application就是组装工人,配置单就是下面要说明的配置文件。

配置信息在配置文件中以数组元素的方式存放,一个元素就是两个字符串组成的键值对,一个字符串是键(key),另一个字符串是这个键的对应的值(value)。大多数的系统都有一些配置常量,将这些常量放在配置文件中,系统通过访问这个配置文件取得配置常量,就可以通过修改配置文件而无须修改程序达到更改系统配置的目的。系统也可以在配置文件中存储一些工作环境信息,这样在系统每次访问时,这些信息可以运行在每一个应用的生命周期中。

通常在一个单独的PHP 脚本(config/main.php)中保存这些配置。在脚本中,通过以下方式返回此配置数组。

<?php
    return [
        //默认控制器
        "defaultController"=>"default",
        //默认动作方法
        "defaultAction" => "list",
        //通过应用全局访问函数Yii::$app->name;直接访问。
        "name"=>"my application",
    ];
?>

在应用的构造方法中添加对配置文件操作的代码。

<?php
namespace vendor;
class Application {
     ……
    //构造方法在实例对象被创建时自动执行
     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;
          }
     }
……
}

要应用此配置,将配置文件的名字作为参数传递给应用的构造器,或像下面这样传递到Application::createApplication(),这通常在入口脚本中完成。

<?php
    //加载vendor文件夹下的文件,为了清楚演示这里没有用__autoload()。
    require "./vendor/Application.php";
    use vendor\Application;
    //定义配置文件路径
    $config="./config/main.php";
    //创建应用(前端控制器)对象
    $app = Application::createApplication($config);
    $app -> run();
?>

实现前端控制器模式后,框架执行流程如图1-10所示。

图1-10 框架执行流程

① 用户发送访问URL的请求,Web服务器通过执行入口脚本index.php处理此请求。

② 入口脚本创建一个应用实例并执行。

③ 创建一个所请求控制器的实例以进一步处理用户请求。控制器决定动作指向控制器类中的action方法。

④ 控制器调用模型实例对象从数据库中读取数据。

⑤ 渲染视图。

⑥ 视图读取并显示模型的数据。

⑦ 动作完成视图渲染并将其呈现给用户。

在前几节中,介绍了框架的概念及使用框架技术的优势,并结合PHP的发展历史总结了现阶段PHP及其框架技术的应用领域。

为了让读者更好地理解Yii2框架,并认识到框架技术并不是多么复杂,本节自定义了一个MVC框架,实现控制器、模型、视图的分离,创建单一入口模式的目录结构,实现应用的预处理、初始化和执行。显然,自定义的框架功能还很少,不能满足框架作为“半成品”的需要。因此,接下来要进入到Yii2框架的学习,因为Yii2提供了目前Web 2.0应用开发所需要的几乎一切功能。下面是这些特性的简短说明。

本章首先介绍了PHP语言的发展历史及其适合的应用领域,希望读者对PHP技术未来的发展空间充满信心。

框架是特定应用程序的“半成品”,是面向对象系统获得的最大的复用方式。在项目开发中,如果使用一个成熟的框架,就相当于让别人帮你完成一些基础工作。

本章中自定义框架部分是PHP面向技术的实践,分别实现了MVC框架模式、单入口文件设计模式和前端控制器设计模式,目的是让读者能够更好地理解Yii2框架的工作机制,并为后续Yii2框架的学习做好准备。

下面我们将正式进入Yii2框架的学习,希望读者通过学习Yii2框架,掌握Web开发相关的内容。


从本章开始,我们将通过由浅入深的方式介绍Yii2框架的各个部分,希望读者逐步了解Yii2框架。

Yii框架作为一种热门的PHP框架技术,在当前的PHP开发领域正受到越来越多的关注。本节将首先介绍什么是Yii、Yii有什么优点及应用案例等内容。通过对本节的学习,读者会对Yii有一个大致的认识。

Yii是一个基于组件的高性能PHP框架,用于快速开发大型Web应用。它使Web开发中的可复用度最大化,可以显著提高Web应用开发速度。Yii读作“易(Yee)”或“[ji:]”,这个名字是“Yes, it is!”的缩写。

“Yii快不快?安全吗?专业吗?是否适用于我的下一个项目?”“Yes,it is!”(是的。)

1.历史

Yii是创始人薛强的心血结晶,于2008年1月1日开始开发。在此之前,薛强开发和维护PRADO框架多年,他从这些年的经验和所得到的反馈中了解到,用户需要一个更容易、可扩展、更快速的基于PHP5的框架,以满足应用程序开发人员不断增长的需求。

Yii正式发布于2008年10月,最初是Alpha版本,其与其他基于PHP的框架表现相比令人印象深刻,立即引起非常积极的关注。在2008年12月3日Yii1.0正式发布;2013年8月11日,发布稳定版本1.1.14;在2014年4月13日,发布了Yii2.0的beta测试版。本书中采用的是使用最广泛且相对比较成熟的Yii2.0.15版。

提示:

 

\Yii::getVersion();可以获取到当前Yii框架的版本号

2.环境需求

要运行一个基于Yii2框架的Web应用,需要有一个支持PHP 5.4.0或以上版本的Web服务器。

对于打算使用Yii2的开发者来说,懂得面向对象编程(OOP)会非常有帮助,因为Yii2是一个纯面向对象的框架。

3.特点

Yii2是一个通用的Web编程框架,可以用于开发几乎所有的Web应用。它是轻量级的,而且具备成熟的缓存解决方案,特别适用于开发高流量的应用,如门户网站、论坛、内容管理系统和电子商务系统等。图2-1和图2-2是使用Yii2构建的一些Web项目的经典案例。图2-3为后台管理页面。

图2-1 门诊预约系统首页

图2-2 渡手网站首页

图2-3 内容管理系统操作页面

除上述案例之外,还有很多互联网应用项目也使用了Yii2框架技术,这里不再赘述。

从Yii2的官方站点可下载程序包。Yii2的安装方式有多种,本章只是Yii2入门,所以使用比较简单的“Install from an Archive File”(通过归档文件安装),并下载其“Yii2 with basic application template”(基础版本),如图2-4所示。

图2-4 Yii2下载

将下载的文件解压缩到 Web 访问的文件夹中。修改config/web.php文件,给 cookieValidationKey 配置项添加一个密钥。

'request' => [
    // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
    'cookieValidationKey' => '在此处输入你的密钥',
],

安装完成后,使用浏览器访问http://hostname/basic/web/index.php,正常情况下页面如图2-5所示。

图2-5 安装完成页面

如果没有在浏览器中看到如上所示的“Congratulations!”页面,可以通过浏览器访问/requirements.php文件,该文件用于确认当前服务器能否满足运行Yii2 Web项目的要求。它将检查服务器所运行的PHP版本,查看是否安装了合适的PHP扩展模块,以及确认php.ini文件是否正确设置,如图2-6所示。

图2-6 环境配置检测

安装完的基本应用包含4页,主页、“About” 页、“Contact” 页和“Login” 页。另外,在浏览器底部可以看到一个工具栏,这是 Yii2提供的调试工具,可以记录并显示大量的调试信息,例如日志信息,响应状态,数据库查询等,如图2-7所示。

图2-7 调试工具

Yii2基于模型—视图—控制器(MVC)设计模式,这点在下面的目录结构中得以体现,models目录包含了所有模型类,views目录包含了所有视图脚本,controllers目录包含了所有控制器类。

basic/                应用根目录
    composer.json     Composer 配置文件, 描述包信息
    config/           包含应用配置及其他配置
        console.php  控制台应用配置信息
        web.php       Web 应用配置信息
    commands/         包含控制台命令类
    controllers/     包含控制器类
    models/           包含模型类
    runtime/          包含Yii2在运行时生成的文件,例如日志和缓存文件
    vendor/           包含已经安装的 Composer 包,包括 Yii 框架自身
    views/            包含视图文件
    web/               Web 应用根目录,包含 Web 入口文件
        assets/       包含 Yii2发布的资源文件(JavaScript和CSS)
        index.php     应用入口文件
yii                   Yii2控制台命令执行脚本

提示:

 

应用中的文件可被分为两类:在 basic/web 下的文件和在其他目录下的文件。前者可以直接通过 HTTP 访问(例如浏览器),后者不能也不应该被直接访问。

每个应用都有一个入口脚本web/index.php,这是整个应用中唯一可以访问的 PHP 脚本。入口脚本接受一个Web请求并创建应用实例去处理它。应用在它的组建辅助下解析请求,并分派请求至MVC元素。视图使用小部件去创建复杂和动态的用户界面。Yii2应用静态结构如图2-8所示。

图2-8 Yii2应用静态结构图

输出“Hello World”示例,只需要一个控制器和视图,不处理任何数据。首先,在basic/controllers/SiteController.php文件中创建actionSay()动作方法,代码如下所示。

class SiteController extends Controller
{
    public function actionSay($message = 'Hello')
    {
        return $this->render('say', ['message' => $message]);
    }
    ……
}

actionSay()动作方法实现了从请求中接收message参数并显示给最终用户。如果请求没有提供message参数,动作将显示默认参数“Hello”。

在动作方法中,render()方法用来渲染视图文件。视图文件与控制器关联,存放在views/site目录下,编辑views/site/say.php,代码如下。

<?php
    echo $message;
?>

保存代码,并访问http://hostname/index.php?r=site/say&message=HelloWorld,页面如图2-9示。

图2-9 Yii2输出“Hello World!”效果图

如果省略URL中的message参数,将会看到页面只显示“Hello”。这是因为message 被作为一个参数传给actionSay()方法,当省略它时,参数将使用默认的“Hello”代替。

综上所述,输出“Hello World”时Yii2框架请求的生命周期如图2-10所示。

图2-10 输出“Hello World!”工作流程图

① 用户发出了访问http://localhost/index.php? r=site/say&message=HelloWorld的请求,Web 服务器通过执行入口脚本index.php处理此请求。

② 入口脚本创建了一个应用实例对象并执行。

③ 应用实例对象分析这个URL,controllerID是site,它将告诉Yii2应该去请求SiteController.php文件,这个文件的位置是controllers/SiteController.php。actionID指定的是say,因此,会调用SiteController类中的actionSay()方法。

④ actionSay()方法会渲染say.php视图文件,这个文件的位置是views/site/say.php。

⑤ 动作方法完成视图渲染并将其返回给用户。

入口脚本是应用启动流程中的第一环,一个应用只有一个入口脚本。Web 应用的入口脚本必须放在终端用户能够访问的目录下,可以自己定义名称,如index.php、admin.php、blog.php等之类的文件名都可以。编写的入口文件内容示例如下所示。

<?php
//定义全局常量,在生产环境中请注释下面两行
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
//注册Composer自动加载器
require(__DIR__ . '/../vendor/autoload.php');
//包含Yii2类文件
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
//加载应用配置
$config = require(__DIR__ . '/../config/web.php');
//创建、配置、运行一个应用
(new yii\web\Application($config))->run();

入口脚本主要完成以下工作。

Yii2应用可以按常量 “YII_DEBUG” 的值来确定是运行调试模式或生产模式。在默认情况下,此常量值定义为 false,意为生产模式,以最高效率运行。在调试模式下,框架要维护许多内部日志,并且在错误产生时提供了丰富的调试信息。

应用主体是管理 Yii2 应用系统整体结构和生命周期的对象。每个Yii2应用系统只能包含一个应用主体,即由入口脚本创建为一个单例对象。这个应用单例对象可以在任何地方通过Yii::$app访问,在入口文件创建应用时,“(new yii\web\Application($config))-> run();”调用Application的构造方法,而其继承自yii\base\Application的构造方法,代码如下所示。

abstract class Application extends Module
{
     /**
     * Constructor.
     * @param array $config name-value pairs that will be used to initialize the object properties.
     * Note that the configuration must contain both [[id]] and [[basePath]].
     * @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
     */
    public function __construct($config = [])
    {
        Yii::$app = $this;
        static::setInstance($this);
        $this->state = self::STATE_BEGIN;
        $this->preInit($config);
        $this->registerErrorHandler($config);
        Component::__construct($config);
    }
    ……
}

应用主体配置文件中有许多重要的属性要配置,这些属性指定应用主体的运行环境。 比如,应用主体需要知道如何加载控制器,临时文件保存到哪儿等。以下我们简述这些属性。

在一个应用中,至少要配置2个属性:yii\base\Application::id和 yii\base\Application::basePath。

yii\base\Application::id

yii\base\Application::id 属性用来区分其他应用的唯一标识ID。主要给程序使用。 为了方便协作,最好使用数字作为应用主体ID,但不强制要求为数字。

yii\base\Application::basePath

yii\base\Application::basePath 指定该应用的根目录。根目录包含应用系统所有受保护的源代码。在根目录下可以看到对应MVC设计模式的models,views和controllers等子目录。

当运行入口脚本处理请求时,应用主体会经历生命周期,如图2-11所示。

① 入口脚本加载应用主体配置数组。

图2-11 Yii2框架应用生命周期图

② 入口脚本创建一个应用主体实例。

③ 入口脚本调用 yii\base\Application::run() 运行应用主体。

④ 入口脚本接收应用主体传来的退出状态并完成请求的处理。

Yii2框架实现了MVC框架模式的设计思想,把应用的输入、处理、输出流程按照View、Model、Controller的方式进行分离,直接把视图、模型和控制器分别保存到了不同目录下,如图2-12所示。

图2-12 Yii2框架中的MVC目录结构

实现MVC框架模式的目标是将业务逻辑从用户界面中分离,这样开发者就可以更容易地改变每一部分而不会影响其他。

Yii2框架中的控制器是yii\web\Controller或其子类的实例,它在用户请求时由应用创建。当一个控制器运行时,它执行所请求的动作,动作通常会引入所需要的模型并渲染相应的视图。动作的最简形式,就是一个名字以action开头的控制器类方法。

下面的代码定义了MessageController控制器类,其中包括动作方法actionOutput(),保存在MessageController.php文件中。

<?php
namespace app\controllers;
use yii\web\Controller;

class MessageController extends Controller
{
    echo 'helloWorld';    
}

注意:

 

控制器通常有一个默认的动作。当用户的请求未指定要执行的动作时,默认动作将被执行,默认的动作名为index,它可以通过设置yii\base\Controller::defaultAction修改。

视图是一个包含了主要的用户交互元素的PHP脚本,可以包含PHP语句,但是建议这些语句不要去改变数据模型,且最好能够保持其单纯性(单纯作为视图)。为了实现逻辑和界面分离,大段的逻辑应该被放置于控制器或模型中,而不是视图中。

视图有一个名字,当渲染(render)时,这个名字会被用于识别视图脚本文件,视图的名称与其视图脚本名称是一样的。例如,视图helloWorld的名称出自一个名为helloWorld.php的脚本文件。要渲染时,调用yii\base\Controller::render()方法。这个方法将在/views/ ControllerID 目录下寻找对应的视图文件。也就是说在views中,文件夹名称应该和默认路由中的控制器ID保持一致。

在视图脚本内部,可以用以下推送的方式传递数据到视图文件。

$this->render('viewname',['var1'=>$value1]);

在以上的代码中,render()方法将提取数组的第二个参数到变量里。其产生的结果是,在视图脚本里可以直接访问变量$var1。控制器yii\base\Controller的render()方法详细说明如表2-1所示。

综上所述,Yii2框架请求的生命周期如图2-13所示。

表2-1 yii\base\Controller的成员方法render()

public string render(string $view, array $data=NULL, boolean $return=false)
$view string 视图文件名
$data array 数组中元素的键转化为在视图文件中可以使用的变量名,对应元素的值转化为该变量的值
{return} string 返回渲染视图的结果

图2-13 Yii2框架请求声明周期图

① 用户向入口脚本 web/index.php 发起请求。

② 入口脚本加载应用配置并创建一个应用实例去处理请求。

③ 应用通过请求组件解析请求的路由。

④ 应用创建一个控制器实例去处理请求。

⑤ 控制器创建一个动作实例并针对操作执行过滤器。

⑥ 如果任何一个过滤器返回失败,则动作取消。

⑦ 如果所有过滤器都通过,则动作将被执行。

⑧ 动作会加载一个数据模型。

⑨ 动作会渲染一个视图,把数据模型提供给它。

⑩ 渲染结果返回给响应组件。

⑪ 响应组件发送渲染结果给用户浏览器。

本书是基于内容管理系统的Yii2框架深度剖析,由一个项目贯穿始终,从浅入深迭代完成,本节完成控制器渲染前台首页、列表页和内容页视图界面,具体实现步骤如下。

步骤1:创建首页控制器。创建controllers/IndexController.php文件,控制器代码如下。

<?php
namespace app\controllers;
use yii\web\Controller;
class IndexController extends Controller{
    public function actionIndex(){        
//不使用布局
            $this->layout = false;
        $this->render ('index');
    }
}

步骤2:创建首页视图。因为控制器名为IndexController,所以在views文件夹下创建index文件夹,然后把本书配套的首页静态页面创建成首页视图文件“index.php”,并把相应images、js和css文件夹复制到入口文件index.php同一路径。最后,通过浏览器访问首页控制器IndexController的actionIndex()方法。

http://hostname/index.php?r=index/index

控制器渲染视图的步骤比较简单,这里会出现的问题是找不到css文件、js文件和图片文件,我们只需要把在视图文件中引入的css文件、js文件和图片文件的路径设置成相对入口文件即可。

渲染列表页、内容页视图页面和渲染首页视图页面类似,需要创建控制器ArlistController和ArticleController,这里不再赘述。效果如图2-14~图2-16所示。

图2-14 首页效果图

图2-15 列表页效果图

图2-16 内容页效果图

本章以输出“Hello,World!”为案例,介绍了Yii2框架的执行流程。本章是第1章中自定义框架部分在Yii2框架中的体现,希望读者能够参照1.4节部分深入理解Yii2框架中的相关内容。

读者通过学习本章内容,首先能够理解入口文件需要包含Yii2框架的引导文件,并且按指定的配置创建Web应用实例并执行;其次,能够理解应用(前端控制器)是最上层对象,主要任务是分析用户请求并将其分派到合适的控制器中以做进一步处理;最后,能够掌握Yii2框架中控制器和视图的编写规范,完成控制器渲染视图的操作。


相关图书

轻松学PHP
轻松学PHP
PHP、MySQL和JavaScript入门经典(第6版)
PHP、MySQL和JavaScript入门经典(第6版)
PHP和MySQL Web开发学习指南
PHP和MySQL Web开发学习指南
B/S项目开发实战 HTML+CSS+jQuery+PHP
B/S项目开发实战 HTML+CSS+jQuery+PHP
Tomcat内核设计剖析
Tomcat内核设计剖析
PHP、MySQL和Apache入门经典(第5版)
PHP、MySQL和Apache入门经典(第5版)

相关文章

相关课程