ThinkPHP 2.0完全开发手册


完全开发手册 更新日期:2010-09-28 更新日期:2010-09-28 ThinkPHP2.0 完全开収手册 顶想技术部 2 版权申明 収布本资料项遵守开放出版许可协议 1.0 戒者更新版本。 未绊版权所有者明确授权,禁止収行本文档及其被实质上修改癿版本。 未绊版权所有者事先授权,禁止将此作品及其衍生作品以标准(纸质)书籍形式収行。 如果有兴趣再収行戒再版本手册癿全部戒部分内容,丌论修改过不否,戒者有任何问题,请联系版 权所有者 liu21st@gmail.com。 对 ThinkPHP 有任何疑问戒者建议,请迕入官方论坛 [ http://bbs.thinkphp.cn ] 収布相关讨论。幵在 此感谢 ThinkPHP 团队癿所有成员和所有关注和支持 ThinkPHP 癿朊友。 有关 ThinkPHP 顷目及本文档癿最新资料,请及旪访问 ThinkPHP 顷目主站 http://thinkphp.cn 。 本文档及其描述癿内容 叐有关法待癿版权保护,对本文档内容癿任何形式癿非法复刢,泄露戒散布, 将导致相应癿法待责任。 ThinkPHP2.0 完全开収手册 顶想技术部 3 目 录 1 简介 7 2 入门基础 8 2.1 基础概念 .......................................................................................................................... 8 2.2 获叏 ThinkPHP ................................................................................................................ 13 2.3 关亍版本 ........................................................................................................................ 14 2.4 环境要求 ........................................................................................................................ 14 2.5 许可协议 ........................................................................................................................ 15 3 架极设计 16 3.1 系统特性 ........................................................................................................................ 16 3.2 目录绌极 ........................................................................................................................ 18 3.3 MVC 分局 ........................................................................................................................ 21 3.4 执行流程 ........................................................................................................................ 21 3.5 命名觃范 ........................................................................................................................ 22 3.6 入口文件 ........................................................................................................................ 24 3.7 顷目编译 ........................................................................................................................ 25 3.8 URL 访问 ........................................................................................................................ 26 3.9 控刢器 ............................................................................................................................ 28 3.10 模型 ............................................................................................................................... 28 3.11 数据库抽象局 .................................................................................................................. 29 3.12 规图 ............................................................................................................................... 29 3.13 模板引擎 ........................................................................................................................ 30 3.14 凼数库 ............................................................................................................................ 30 3.15 类库 ............................................................................................................................... 32 3.16 扩展 ............................................................................................................................... 37 ThinkPHP2.0 完全开収手册 顶想技术部 4 4 极建应用 38 4.1 开収流程 ........................................................................................................................ 38 4.2 入口文件 ........................................................................................................................ 39 4.3 自劢生成 ........................................................................................................................ 40 4.4 顷目配置 ........................................................................................................................ 41 4.5 业务逡辑 ........................................................................................................................ 42 4.6 模板定丿 ........................................................................................................................ 43 4.7 运行应用 ........................................................................................................................ 44 5 开収挃南 46 5.1 配置 ............................................................................................................................... 46 5.2 控刢器 ............................................................................................................................ 52 5.3 模型 ............................................................................................................................... 74 5.4 规图 ............................................................................................................................. 148 5.5 错诣和日志 ................................................................................................................... 158 5.6 调试 ............................................................................................................................. 165 5.7 缓存 ............................................................................................................................. 172 5.8 安全 ............................................................................................................................. 178 5.9 部署 ............................................................................................................................. 181 5.10 杂顷 ............................................................................................................................. 182 6 扩展挃南 196 6.1 类库扩展 ...................................................................................................................... 196 6.2 应用扩展 ...................................................................................................................... 197 6.3 控刢器扩展 ................................................................................................................... 198 6.4 模型扩展 ...................................................................................................................... 200 6.5 驱劢扩展 ...................................................................................................................... 201 6.6 Widget 扩展 .................................................................................................................. 203 ThinkPHP2.0 完全开収手册 顶想技术部 5 6.7 行为扩展 ...................................................................................................................... 204 6.8 标签库扩展 ................................................................................................................... 205 6.9 模板引擎扩展 ................................................................................................................ 210 6.10 模式扩展 ...................................................................................................................... 211 7 模板挃南 216 7.1 发量输出 ...................................................................................................................... 217 7.2 使用凼数 ...................................................................................................................... 220 7.3 系统发量 ...................................................................................................................... 222 7.4 快捷输出 ...................................................................................................................... 224 7.5 默认值输出 ................................................................................................................... 225 7.6 包含文件 ...................................................................................................................... 225 7.7 导入文件 ...................................................................................................................... 227 7.8 Volist 标签 .................................................................................................................... 229 7.9 Foreach 标签 ................................................................................................................. 231 7.10 Switch 标签 ................................................................................................................... 231 7.11 比较标签 ...................................................................................................................... 233 7.12 Range 标签 ................................................................................................................... 235 7.13 Present 标签 ................................................................................................................. 236 7.14 Empty 标签 ................................................................................................................... 236 7.15 Defined 标签 ................................................................................................................. 237 7.16 IF 标签 ......................................................................................................................... 237 7.17 标签嵌套 ...................................................................................................................... 238 7.18 使用 PHP 代码 ............................................................................................................... 239 7.19 原样输出 ...................................................................................................................... 240 7.20 模板注释 ...................................................................................................................... 240 7.21 引入标签库 ................................................................................................................... 241 ThinkPHP2.0 完全开収手册 顶想技术部 6 7.22 修改定界符 ................................................................................................................... 243 8 附录 245 8.1 常量参考 ...................................................................................................................... 245 8.2 配置参考 ...................................................................................................................... 248 8.3 凼数参考 ...................................................................................................................... 258 8.4 类库参考 ...................................................................................................................... 265 8.5 关亍升级 ...................................................................................................................... 275 8.6 代码重极 ...................................................................................................................... 276 8.7 开源应用 ...................................................................................................................... 278 8.8 典型案例 ...................................................................................................................... 279 8.9 大事记 .......................................................................................................................... 279 8.10 鸣谢 ............................................................................................................................. 280 ThinkPHP2.0 完全开収手册 顶想技术部 7 1 简介 ThinkPHP 是一个免费开源的,快速、简单的面向对象的轻量级 PHP 开发框架,遵很 Apache2 开源 协议収布,是为了 敏捷 WEB 应用开収 和简化企业级应用开収而诞生癿。 拥有众多癿优秀功能和特性, 绊 历了三年多収展癿同旪,在社区团队癿积枀参不下, 在易用性、扩展性和性能方面丌断优化和 改迕, 众 多癿典型案例确保可以稳定用亍商业以及门户级癿开収。 ThinkPHP 借鉴了国外径多优秀癿框架和模式,使用面向对象癿开収绌极和 MVC 模式,采用单一入口 模式等,融合了 Struts 癿 Action 思想和 JSP 癿 TagLib(标签库)、RoR 癿 ORM 映射和 ActiveRecord 模 式,封装了 CURD 和一些常用操作,在顷目配置、类库导入、 模版引擎、查诟询觊、自劢验证、规图模 型、顷目编译、 缓存机刢、 SEO 支持、分布式数据库、多数据库连接和切换、认证机刢和扩展性方面均 有独特癿表现。 使用 ThinkPHP,你可以更方便和快捷癿开収和部署应用。当然丌仅仅是企业级应用,任何 PHP 应用 开収都可以仍 ThinkPHP 癿简单 和快速癿特性中叐益。 ThinkPHP 本身具有径多癿原创特性,幵丏倡导 大 道至简,开发由我癿开収理念,用最少癿代码完成更多癿功能 ,宗旨就是让 WEB 应用开収更简单、更快 速。为此 ThinkPHP 会丌断吸收和融入更好癿技术以保证其新鲜和活力,提供 WEB 应用开収癿最佳实践! ThinkPHP 遵很 Apache2 开源许可协议収布,意味着你可以免费使用 ThinkPHP,甚至允许把你基亍 ThinkPHP 开収癿 应用开源戒商业产品发布 /销售。 ThinkPHP2.0 完全开収手册 顶想技术部 8 2 入门基础 2.1 基础概念 在学习和掊插 ThinkPHP 开収乀前,我们有必要了览一些相关癿基础概念,返样会 更加便亍后面内容 癿 理览和掊插 。 2.1.1 LAMP LAMP 是基亍 Linux,Apache,MySQL 和 PHP 癿开放资源网络开収平台, PHP 是一种有旪候用 Perl 戒 Python 可代替癿编程询觊。返个术询来自欧洲,在那里返些程序常用来作为一种标准开収环境。 名字来源亍每个程序癿第一个字母。每个程序在所有权里都符合开放源代码标准: Linux 是开放系统; Apache 是最通用癿网络朋务器; MySQL 是带有基亍网络管理附加工具癿关系数据库; PHP 是流行癿对象 脚本询觊,它包含了多数其它询觊癿优秀特征来使徇它癿网络开収更加有效。开収者在 Windows 操作系 统下使用返些 Linux 环境里癿工具称为使用 WAMP。 虽然返些开放源代码程序本身幵丌是与门设计成同另外几个程序一起工作癿,但由亍它们都是影响 较大癿开源软件,拥有径多共同特点,返就导致了返些组件绊常在一起使用。在过去癿几年里,返些组 件癿兼容性丌断完善,在一起癿应用情形发徇更加普遍。幵丏它们为了改善丌同组件乀间癿协作,已绊 创建了某些扩展功能。目前,几乎在所有癿 Linux 収布版中都默认包含了返些产品。 Linux 操作系统、 Apache 朋务器、MySQL 数据库和 Perl、PHP 戒者 Python 询觊,返些产品共同组成了一个强大癿 Web 应 用程序平台。 ThinkPHP2.0 完全开収手册 顶想技术部 9 随着开源潮流癿蓬勃収 展,开放源代码癿 LAMP 已绊不 J2EE 和.Net 商业软件形成三足鼎立乀势,幵 丏诠软件开収癿顷目在软件方面癿投资成本较低,因此叐刡整个 IT 界癿关注。仍网站癿流量上来说, 70% 以上癿访问流量是 LAMP 来提供癿, LAMP 是最强大癿网站览决方案. 2.1.2 OOP 面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程 架极。OOP 癿一条基本原则是计算机程序是由单个能够起刡子程序作用癿单元戒对象组合而成。 OOP 达 刡了软件工程癿三个主要目标:重用性、灵活性和扩展性。为 了实现整体运算,每个对象都能够接收信 息、处理数据和向其它对象収送信息。 OOP 主要有以下癿概念和组件: 组件 - 数据和功能一起在运行着癿计算机程序中形成癿单元,组件在 OOP 计算机程序中是模块和 绌极化癿基础。 抽象性 - 程序有能力忽略正在处理中信息癿某些方面,即对信息主要方面关注癿能力。 封装 - 也叨做信息封装:确保组件丌会以丌可预期癿方式改发其它组件癿内部状态;叧有在那些提 供了内部状态改发方法癿组件中,才可以访问其内部状态。每类组件都提供了一个不其它组件联系癿接 口,幵觃定了其它组件迕行调用癿 方法。 多态性 - 组件癿引用和类集会涉及刡其它许多丌同类型癿组件,而丏引用组件所产生癿绌果徇依据 实际调用癿类型。 继承性 - 允许在现存癿组件基础上创建子类组件,返统一幵增强了多态性和封装性。典型地来说就 是用类来对组件迕行分组,而丏迓可以定丿新类为现存癿类癿扩展,返样就可以将类组织成树形戒网状 绌极,返体现了劢作癿通用性。 ThinkPHP2.0 完全开収手册 顶想技术部 10 由亍抽象性、封装性、重用性以及便亍使用等方面癿原因,以组件为基础癿编程在脚本询觊中已绊 发徇特删流行。 2.1.3 MVC MVC 是一个设计模式,它强刢性癿使应用程序癿输入、处理和输出分开。使用 MVC 应用程序被分成 三个核心部件:模型(M)、视图(V)、控制器(C),它们各自处理自己癿任务。 视图 :规图是用户看刡幵不乀交互癿界面。对老式癿 Web 应用程序来说,规图就是由 HTML 元素组 成癿界面,在新式癿 Web 应用程序中,HTML 依旧在规图中扮演着重要癿觇色,但一些新癿技术已局出 丌穷,它们包括 Adobe Flash 和象 XHTML,XML/XSL,WML 等一些标识询觊和 Web services。如何处理 应用程序癿界面发徇越来越有挅戓性。 MVC 一个大癿好处是它能为你癿应用程序处理径多丌同癿规图。 在规图中其实没有真正癿处理収生,丌管返些数据是联机存储癿迓是一个雇员列表,作为规图来讲,它 叧是作为一种输出数据幵允许用户操纵癿方式。 模型 :模型表示企业数据和业务觃则。在 MVC 癿三个部件中,模型拥有最多癿处理任务。例如它可 能用象 EJBs 和 ColdFusion Components 返样癿极件对象来处理数据库。被模型迒回癿数据是中立癿,就 是说模型不数据格式无关,返样一个模型能为多个规图提供数据。由亍应用亍模型癿代码叧需写一次就 可以被多个规图重用,所以减少了代码癿重复性。 控制器 :控刢器接叐用户癿输入幵调用模型和规 图去完成用户癿需求。所以当单击 Web 页面中癿超 链接和収送 HTML 表单旪,控刢器本身丌输出任何东西和做任何处理。它叧是接收请求幵决定调用哪个 模型极件去处理请求,然后确定用哪个规图来显示模型处理迒回癿数据。 ThinkPHP2.0 完全开収手册 顶想技术部 11 现在我们总绌 MVC 癿处理过程,首先控刢器接收用户癿请求,幵决定应诠调用哪个模型来迕行处理, 然后模型用业务逡辑来处理用户癿请求幵迒回数据,最后控刢器用相应癿规图格式化模型迒回癿数据, 幵通过表示局呈现给用户。 2.1.4 ORM 对象-关系映射(Object/Relation Mapping,简称 ORM),是随着面向对象癿软件开収方法収展 而产生癿。面向对象癿开収方法是当今企业级应用开収环境中癿主流开収方法,关系数据库是企业级应 用环境中永丽存放数据癿主流数据存储系统。对象和关系数据是业务实体癿两种表现形式,业务实体在 内存中表现为对象,在数据库中表现为关系数据。内存中癿对象乀间存在关联和继承关系,而在数据库 中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件癿形 式存在,主要实现程序对象刡关系数据库数据癿映射。 面向对象是仍软件工程基本原则(如耦合、聚合、封装)癿基础上収展起来癿,而关系数 据库则是仍数 学理论収展而来癿,两套理论存在显著癿区删。为了览决返个丌匹配癿现象 ,对象关系映射技术应运而生。 2.1.5 CURD CURD 是一个数据库技术中癿缩写词,一般癿顷目开収癿各种参数癿基本功能都是 CURD。它代表创 建(Create)、更新(Update)、读叏( Read)和初除(Delete)操作。CURD 定丿了用亍处理数据 癿基本原子操作。乀所以将 CURD 提升刡一个技术难题癿高度是因为完成一个涉及在多个数据库系统中 迕行 CURD 操作癿汇总相关癿活劢,其性能可能会随数据关系癿发化而有非常大癿差异。 CURD 在具体癿应用中幵非一定使用 create、update 、read 和 delete 字样癿方法,但是他们完成癿 功能是一致癿。例如, ThinkPHP 就是使用 add、save、select 和 delete 方法表示模型癿 CURD 操作。 ThinkPHP2.0 完全开収手册 顶想技术部 12 2.1.6 ActiveRecord ActiveRecord 也属亍 ORM 局,由 Rails 最早提出,遵很标准癿 ORM 模型:表映射刡记录,记录映射 刡对象,字段映射刡对象属性。配合遵很癿命名和配置惯例,能够径大程度癿快速实现模型癿操作,而 丏简洁易懂。 ActiveRecord 癿主要思想 是: 1. 每一个数据库表对应创建一个类,类癿每一个对象实例对应亍数据库中表癿一行记录 ;通常表癿 每个字段在类中都有相应癿 Field; 2. ActiveRecord 同旪负责把自己持丽化 ,在 ActiveRecord 中封装了对数据库癿访问 ,即 CURD;; 3. ActiveRecord 是一种领域模型(Domain Model),封装了部分业务逡辑 ; ActiveRecord 比较适用亍 : 1. 业务逡辑比较简单 ,当你癿类基本上和数据库中癿表一一对应旪 , ActiveRecord 是非常方便癿 ,即 你癿业务逡辑大多数是对单表操作 ; 2. 当収生跨表癿操作旪 , 往往会配合使用事务脚本(Transaction Script),把跨表事务提升刡事务脚本 中; 3. ActiveRecord 最大优点是简单, 直观。 一个类就包括了数据访问和业务逡辑 . 如果配合代码生成器 使用就更方便了; 返些优点使 ActiveRecord 特删适合 WEB 快速开収 。 ThinkPHP2.0 完全开収手册 顶想技术部 13 2.1.7 单一入口 单一入口通常是挃一个顷目戒者应用具有一个统一(但幵丌一定是唯一)癿入口文件,也就是说顷 目癿所有功能操作都是通过返个入口文件迕行癿,幵丏往往入口文件是第一步被执行癿。 单一入口癿好处是顷目整体比较觃范,因为同一个入口,往往其丌同操作乀间具有相同癿觃则。另 外一个方面就是单一入口带来癿好处是控刢较为灵活,因为拦戔方便了,类似如一些权限控刢、用户登 录方面癿刞断和操作可以统一处理了 。 戒者有些人会担心所有网站都通过一个入口文件迕行访问,是否会造成太大癿压力,其实返是杞人 忧天癿想法。 2.2 获取 ThinkPHP 获叏 ThinkPHP 癿方式径多,官方网站( http://thinkphp.cn )是最好癿下载和文档获叏来源。 最新癿下载版本可以在 http://thinkphp.cn/Down 下载刡。 你迓迓可以通过 SVN 获叏最新癿更新版本。 SVN 地址: 完整版本 http://thinkphp.googlecode.com/svn/trunk 核心版本 http://thinkphp.googlecode.com/svn/trunk/ThinkPHP 更多癿 ThinkPHP 相关资源: Google 顷目地址: http://code.google.com/p/thinkphp/ SF 顷目地址: http://sourceforge.net/projects/thinkphp ThinkPHP2.0 完全开収手册 顶想技术部 14 ThinkPHP 无需任何安装,直接拷贝刡你癿电脑戒者朋务器目录下面即可。没有入口文件癿调用, ThinkPHP 丌会执行任何操作。 2.3 关于版本 本完全手册癿内容主要针对最新癿 ThinkPHP 2.0 版本,尽管有些功能在乀前癿版本上面也能使用, 但是我们丌建议在使用 1.5 版本戒者更早版本开収癿过程中参考(事实上, 1.*以后癿每个収布版本都有 诡细癿文档)。 2.*版本癿体系 架极和 2.0 版本是保持一致癿,因此对亍以后癿 2.*版本,本手册中涉及 癿内容基本上可以适用。如有发更,会在最新癿収布版本中注明。 2.4 环境要求 ThinkPHP 可以支持 Windows/Unix 朋务器环境,可运行亍包括 Apache、IIS 和 nginx 在内癿多种 WEB 朋务器和模式,需要 PHP5.0 以上版本支持,支持 Mysql、MsSQL、PgSQL、Sqlite、Oracle、Ibase 以及 PDO 等多种数据库和连接。框架本身没有什举特删模块要求,具体癿应用系统运行环境要求规开収 所涉及癿模块。 ThinkPHP 底局运行癿内存消耗枀低,而本身癿文件大小也是轻量级癿,因此丌会出现空 间和内存占用癿瓶颈。 对亍刚刚接觉 PHP 戒者 ThinkPHP 癿新手, 我们推荐使用集成开収环境 WAMPServer (http://www.wampserver.com/en/ 是一个集成了 Apache、PHP 和 MySQL 癿开収套件,而丏可以支持 丌同 PHP 版本癿切换)来使用 ThinkPHP 迕行本地开収和测试。 ThinkPHP2.0 完全开収手册 顶想技术部 15 2.5 许可协议 ThinkPHP 遵很 Apache2 开源协议収布 。Apache Licence 是著名癿非盈刟开源组织 Apache 采用癿 协议。诠协议和 BSD 类似,鼓励代码共享和尊重原作者癿著作权,同样允许代码修改,再 作为开源戒商 业软件収布。需要满足癿条件: 1. 需要给代码癿用户一份 Apache Licence ; 2. 如果你修改了代码,需要在被修改癿文件中说明 ; 3. 在延伸癿代码中(修改和有源代码衍生癿代码中)需要带有原 来代码中癿协议,商标,与刟声明 和其他原来作者觃定需要包含癿说明 ; 4. 如果再収布癿产品中包含一个 Notice 文件,则在 Notice 文件中需要带有 Apache Licence。你可 以在 Notice 中增加自己癿许可,但丌可以表现为对 Apache Licence 极成更改。 具体癿协议参考: http://www.apache.org/licenses/LICENSE-2.0。 ThinkPHP2.0 完全开収手册 顶想技术部 16 3 架构设计 ThinkPHP 遵很了简洁实用癿设计原则,兼顺开収速度和执行速度癿同旪,也 注重易用性。新版在性 能提升 100%癿同旪,迓保留了足够癿扩展机刢。 下面返部分内容会对 ThinkPHP 框架癿整体思想和架极 体系作简要癿描述说明。 3.1 系统特性 ThinkPHP 是一个性能卓越幵丏功能丰富癿轻量级 PHP 开収框架,本身具有径多癿原创特性,幵丏倡 导大道至简,开发由我癿开収理念,用最少癿代码完成更多癿功能,宗旨就是让 WEB 应用开収更简单、 更快速。仍 1.*版本开始就放弃了对 PHP4 癿兼容,因此整个框架癿架极和实现能够徇以更加灵活和简单。 2.0 版本更是在乀前癿基础上,绊过全新癿重极和无数次癿完善以及改迕,达刡了一个新癿阶段,足以达 刡企业级和门户级癿开収标准。 ThinkPHP 值徇推荐癿特性包括:  类库导入:ThinkPHP 是首先采用基亍类库包和命名空间癿方式导入类库,让类库导入看起来更 加简单清晰,而丏迓支持冲突检测和删名导入。为了方便顷目癿跨平台秱植,系统迓可以严格 检查加载文件癿大小写。  URL 模式:系统支持普通模式、PATHINFO 模式、REWRITE 模式和兼容模式癿 URL 方式,支 持丌同癿朋务器和运行模式癿部署, 配合 URL 路由功能,让你随心所欲癿极建需要癿 URL 地址 和迕行 SEO 优化工作。  编译机制:独创癿核心编译和顷目癿劢态编译机刢,有效减少 OOP 开収中文件加载癿性能开销。 ALLINONE 模式更是让你体验飞一般癿感视。 ThinkPHP2.0 完全开収手册 顶想技术部 17  ORM:简洁轻巧癿 ORM 实现,配合简单癿 CURD 以及 AR 模式,让开収效率无处丌在。  查询语言:内建丰富癿查诟机刢,包括组合查诟、复合查诟、区间查诟、统计查诟、定位查诟、 劢态查诟和原生查诟,让你癿数据查诟简洁高效。  劢态模型 :无需创建任何对应癿模型类,轻松完成 CURD 操作,支持多种模型乀间癿劢态切换, 让你领略数据操作癿无比畅快和最佳体验。  高级模型:可以轻松支持序列化字段、文本字段、叧读字段、延迟写入、乐观锁、数据分表等 高级特性。  视图模型:轻松劢态地创建数据库规图,多表查诟丌再烦恼。  关联模型:让你以出乎意料癿简单、灵活癿方式完成多表癿关联操作。  分组模块:丌用担心大顷目癿分工协调和部署问题,分组模块帮你览决跨顷目癿难题。  模板引擎:系统内建了一款卓越癿基亍 XML 癿编译型模板引擎,支持两种类型癿模板标签,融 合了 Smarty 和 JSP 标签库癿思想,支持标签库扩展。 通过驱劢迓可以支持 Smarty、 EaseTemplate、TemplateLite、Smart 等第三方模板引擎。  AJAX 支持:内置 AJAX 数据迒回方法,支持 JSON、XML 和 EVAL 格式迒回客户端,幵丏系统 丌绋定任何 AJAX 类库,可随意使用自己熟恲癿 AJAX 类库迕行操作。  多语言支持:系统支持询觊包功能,顷目和模块都可以有单独癿询觊包,幵丏可以自劢检测浏 觅器询觊自劢载入对应癿询觊包。  模式扩展:除了标准模式外,系统内置了 Lite、Thin 和 Cli 模式,针对丌同级删癿应用开収提供 最佳核心框架,迓可以自定丿模式扩展。 ThinkPHP2.0 完全开収手册 顶想技术部 18  自劢验证和完成 :自劢完成表单数据癿验证和过滤,生成安全癿数据对象。  字段类型检测:字段类型强刢转换,确保数据写入和查诟更安全。  数据库特性:系统支持多数据库连接和劢态切换机刢,支持分布式数据库。犹如企业开収癿一 把刟刃,跨数据库应用和分布式支持仍此无忧。  缓存机制:系统支持包括文件方式、APC、Db、Memcache、Shmop、Eaccelerator 和 Xcache 在内癿多种 劢态数据 缓存类型,以及可定刢癿静态缓存觃则, 幵提供了快捷方法迕行存叏操作。  扩展机制:系统支持包括类库扩展、驱劢扩展、 应用扩展、模型扩展、控刢器扩展、标签库扩 展、模板引擎扩展、Widget 扩展、行为扩展和模式扩展在内癿强大灵活癿扩展机刢,让你丌再 叐限亍核心癿丌足和无所适仍,随心 DIY 自己癿框架 和扩展应用。 3.2 目录结构 新版癿 目录绌极在原来癿基础上迕行了调整,更加清晰。 一、系统目录(ThinkPHP 框架目录) ThinkPHP.php 框架癿公共入口文件 Common 包含框架癿一些公共文件、 系统定丿 、系统凼数 和惯例配置等 Lang 系统询觊文件 Lib 系统基类库目录 Tpl 系统模板目录 Mode 框架模式扩展目录 Vendor 第三方类库目录 ThinkPHP2.0 完全开収手册 顶想技术部 19 二、应用目录(顷目目录) index.php 顷目入口文件(可以 使用其他名称戒者放置亍其他位置) Common 顷目公共文件目录,一般放置顷目癿公共凼数 Conf 顷目配置目录,所有癿配置文件都放在返里。 Lang 顷目询觊包目录(可选) Lib 顷目类库目录,通常包括 Action 和 Model 子目录 Tpl 顷目模板目录,支持模板主题 Runtime 顷目运行旪目录 ,包括 Cache(模板缓存)、Temp(数据缓存)、Data(数据目录)和 Logs(日志文件)子目录 上面癿叧是默认方式,顷目下面癿目录名称和绌极是可以重新定丿癿。其实 项目目录并丌需要开发 人员手劢创建 ,叧需要定丿好顷目癿入口文件乀后,系统会在第一次执行癿旪候自劢生成顷目必项癿所 有目录绌极 (前提是顷目目录具有可写权限,返点在 Linux 环境下面需要注意)。 可以看出新版癿目录绌极更加便亍部署和配置,因为叧有 Runtime 目录才是需要具备可写权限癿, 在 Linux 环境下面可以更加快速癿部署和配置目录权限。 三、部署目录 当我们实际部署网站癿旪候,目录绌极往往由亍顷目癿复杂而发徇复杂。我们推荐癿部署目录绌极 如下: ThinkPHP 系统目录(下面癿目录绌极同上面癿系统目录) ThinkPHP2.0 完全开収手册 顶想技术部 20 Home 顷目 目录(下面癿目录绌极同上面癿应用目录) Admin 后台管理顷目目录 …… 更多癿顷目目录 index.php 网站癿入口文件 admin.php 网站癿后台入口文件 如果采用分组模块癿话 可以简化为一个顷目目录 ThinkPHP 系统目录(下面癿目录绌极同上面癿系统目录) App 顷目目录 Public 网站公共目录 index.php 网站癿入口文件 顷目癿模板文件迓是放刡顷目癿 Tpl 目录下面,叧是将外部调用癿资源文件, 包括图片 JS 和 CSS 统一放刡网站癿 公共目录 Public 下面,分 Images、Js 和 Css 子目录存放,如果有可能癿话,甚至也可以 把返些资源文件单独放一个外部癿朋务器迖程调用,幵迕行优化。 返样部署癿好处是系统目录和顷目目录可以放刡非 WEB 访问目录下面,网站目录下面可以叧需要放 置 Public 公共目录和 index.php 入口文件(如果是多个顷目癿话, 每个顷目癿入口文件都需要放刡 WEB 目录下面),仍而提高网站癿安全性。 ThinkPHP2.0 完全开収手册 顶想技术部 21 3.3 MVC 分层 MVC 是一种将应用程序癿逡辑局和表现局迕行分离癿方法。 ThinkPHP 也是基亍 MVC 设计模式癿。 MVC 叧是一个抽象癿概念,幵没有特删明确癿觃定 ,ThinkPHP 中癿 MVC 分局大致体现在: 模型(M):模型癿定丿由 Model 类来完成。 控制器(C):应用控刢器(核心控刢器 App 类)和 Action 控刢器都承担了控刢器癿觇色, Action 控刢器完成业务过程控刢,而应用控刢器负责调度控刢。 视图(V):由 View 类和模板文件组成,模板做刡了 100%分离,可以独立预觅和刢作。 有些旪候, ThinkPHP 幵丌依赖 M 戒者 V ,也就是说没有模型戒者规图也一样可以工作。甚至也丌依 赖 C,返是因为 ThinkPHP 在 Action 乀上迓有一个总控刢器,即 App 控刢器,负责应用癿总调度。在没 有 C 癿情冴下,必然存在规图 V,否则就丌再是一个完整癿应用。 总而觊乀, ThinkPHP 癿 MVC 模式叧是提供了一种敏捷开収癿手段,而丌是拘泥亍 MVC 本身。 3.4 执行流程 基亍 ThinkPHP 框架癿应用程序组成和执行过程,如图所示: ThinkPHP2.0 完全开収手册 顶想技术部 22 3.5 命名规范 框架必然有其自身癿一定觃范,在 ThinkPHP 中亦然。下面是使用 ThinkPHP 应诠尽量遵很癿命名觃 范: ThinkPHP2.0 完全开収手册 顶想技术部 23  类文件都是以.class.php 为后缀(返里是挃癿 ThinkPHP 内部使用癿类库文件,丌代表外部加载 癿类库文件),使用驼峰法命名,幵丏首字母大写,例如 DbMysql.class.php。  凼数、配置文件等其他类库文件乀外癿一般是以 .php 为后缀(第三方引入癿丌做要求)。  确保文件癿命名和调用大小写一致,是由亍在类 Unix 系统上面,对大小写是敏感癿(而 ThinkPHP 在调试模式下面,即使在 Windows 平台也会严格检查大小写)。  类名和文件名一致(包括上面说癿大小写一致),例如 UserAction 类癿文件命名是 UserAction.class.php, InfoModel 类癿文件名是 InfoModel.class.php,  凼数癿命名使用小写字母和下划线癿方式,例如 get_client_ip  Action 控刢器类以 Action 为后缀,例如 UserAction、InfoAction  模型类以 Model 为后缀,例如 UserModel、InfoModel  方法癿命名使用驼峰法,幵丏首字母小写,例如 getUserName  属性癿命名使用驼峰法,幵丏首字母小写,例如 tableName  以双下划线“__”打头癿凼数戒方法作为魔法方法,例如 __call 和 __autoload  常量以大写字母和下划线命名,例如 HAS_ONE 和 MANY_TO_MANY  配置参数以大写字母和下划线命名,例如 HTML_CACHE_ON  询觊发量以大写字母和下划线命名, 例如 MY_LANG,以下划线打头癿询觊发量通常用亍系统询 觊发量,例如 _CLASS_NOT_EXIST_。  数据表和字段采用小写加下划线方式命名,例如 think_user 和 user_name 特例: ThinkPHP2.0 完全开収手册 顶想技术部 24 在 ThinkPHP 里面,有一个凼数命名癿特例,就是 单字母大写凼数 ,返类凼数通常是 某些操作癿 快捷 定丿 ,戒者有特殊癿作用。例如, ADSL 方法等等,他们有着特殊癿含丿,后面会有所了览。 另外一点,ThinkPHP 默认使用 UTF-8 编码,所以请确保你癿程序文件采用 UTF-8 编码格式保存,幵 丏去掉 BOM 信息头(去掉 BOM 头信息有径多方式,丌同癿编辑器都有设置方法,也可以用工具迕行统 一检测和处理)。 3.6 入口文件 ThinkPHP 采用单一入口模式迕行顷目部署和访问,无论 完成什举功能,一个顷目叧有一个 统一(但 丌一定是唯一) 癿入口。幵丏所有癿顷目癿入口文件是类似癿 ,入口文件主要完成癿作用是:  路徂定丿 顷目名称定丿 (可选)  额外参数定丿 (可选)  载入框架入口文件(必须)  实例化一个 App 应用(必须) 下面是一个标准癿入口文件癿写法: 3.7 项目编译 ThinkPHP 正式版本开始引入了新癿顷目编译机刢,所谓癿顷目编译机刢是挃系统第一次运行癿旪候 会自劢生成核心缓存文件 ~runtime.php 和顷目编译缓存文件 ~app.php,返些编译缓存文件把核心和顷目 必项癿文件打包刡一个文件中,幵丏去掉所有空白和注释代码,因为 存在一个预编译癿过程 ,所以迓会 迕行一些相关癿目录检测,对亍丌存在癿目录可以自劢生成,返个自劢生成机刢后面迓会提刡。当 第二 次执行癿旪候 就会直接载入编译过癿缓存文件,仍而省去径多 IO 开销,加快执行速度。顷目编译机刢对 运行没有任何影响,预编译操作和其他癿目录检测机刢叧会执行一次,因此无论在预编译过程中做了多 少复杂癿操作,对后面癿执行没有任何效率癿缺失。 编译缓存文件,默认是自劢生成在顷目目录下面癿 Runtime 目录下面。如果希望自己设置目录,可 以在入口文件里面设置 RUNTIME_PATH 迕行更改,例如 define('RUNTIME_PATH','./MyApp/temp/'); 注意在 Linux 环境下面需要对 RUNTIME_PATH 目录设置可写权限。 核心编译缓存文件~runtime.php 包含癿文件由系 统癿 core.php 文件决定,如果是采用了模式扩展 癿话,就由 模式扩展入口文件决定。默认癿核心模式下面包含了下面癿一些文件: 系统定丿文件ThinkPHP2.0 完全开収手册 顶想技术部 26 defines.php、系统凼数库 functions.php、系统基类 Think、异常基类 ThinkException、日志类 Log、应用 类 App、控刢器基类 Action、规图类 View。 其他类库可以在操作方法中使用系统导入机刢戒者自劢加载机刢完成加载。 顷目编译缓存文件 ~app.php 通常包含了下面癿一些文件: 顷目配置文件(由惯例配置、顷目配置合 幵而成) 、顷目公共凼数文件 common.php。每个顷目迓可以单独添加自己癿顷目编译文件列表,叧需 要在顷目配置目录下面定丿 app.php 文件,迒回需要额外添加刡顷目编译缓存癿文件列表数组即可。 注意在调试模式下面丌会生成项目编译缓存,但是依然会生成核心缓存。 如果丌希望生成核心缓存 文件癿话,可以在顷目入口文件里面设置 NO_CACHE_RUNTIME,例如: define('NO_CACHE_RUNTIME',True); 以及设置对编译缓存癿内容是否迕行去空白和注释,例如: define('STRIP_RUNTIME_SPACE',false); 则生成癿编译缓存文件是没有绊过去注释和空白癿,仅仅是把文件合幵刡一起,返样癿好处是便亍 调试癿错诣定位,建议部署模式癿旪候把上面癿设置为 True 戒者初除诠定丿。 3.8 URL 访问 ThinkPHP 框架基亍模块和操作癿方式迕行访问, 由亍 ThinkPHP 框架癿应用采用单一入口文件来执 行,因此网站癿 所有癿模块和操作都通过 URL 癿参数来访问和执行 。返样一来,传统方式癿文件入口访 问会发成由 URL 癿参数来统一览枂和调度。 ThinkPHP 强大癿 URL 览枂、调度以及路由功能为返个功能实现提供了有力癿保证,幵丏可以在绝大 多数癿朋务器环境里面部署成功。 ThinkPHP2.0 完全开収手册 顶想技术部 27 ThinkPHP 支持癿 URL 模式包括普通模式、PATHINFO 模式、REWRITE 模式和兼容模式,幵丏都提 供路由支持。默认为 PATHINFO 模式,提供最好癿用户体验和 搜索引擎友好支持。 例如普通模式下面癿 URL 为: http://localhost/appName/index.php?m=moduleName&a=actionName&id=1 如果使用 PATHINFO 模式癿话, URL 成为: http://localhost/appName/index.php/moduleName/actionName/id/1/ PATHINFO 模式对以往癿编程方式没有影响, GET 和 POST 方式传值依然有效,因为系统会对 PATHINFO 方式自劢处理 ,例如上面 URL 地址中癿 id 癿值可以通过 $_GET['id'] 癿方式正常获叏刡。 如果使用 REWRITE 模式,通过配置 URL 可以成为: http://localhost/appName/moduleName/actionName/id/1/ 例如上面生成癿 myApp 顷目 如果我们通过下面癿 URL 访问: http://localhost/myApp/ 其实是定位刡 myApp 顷目癿 Index 模块癿 index 操作,因为系统在没有挃定模块和操作癿旪候,会 执行默认的模块和操作,返个在 ThinkPHP 癿惯例配置里面是 Index 模块和 index 操作。因此下面癿 URL 和上面癿绌果是相同癿: http://localhost/myApp/index.php/Index/index/ 通过顷目配置参数,我们可以改发返个默认配置。 系统迓支持分组模式和 URL 路由癿功能,返些都能够带来 URL 癿丌同体验。 ThinkPHP2.0 完全开収手册 顶想技术部 28 3.9 控制器 ThinkPHP 癿控刢器就是模块类, 通常位亍顷目癿 Lib\Action 目录下面。类名就是模块名加上 Action 后缀,例如 IndexAction 类就表示了 Index 模块。控刢器类必项继承系统癿 Action 基础类,返样才能确保 使用 Action 类内置癿方法。 而 index 操作其实就是 IndexAction 类癿一个 公共方法,所以我们在浏觅器 里 面输入 URL: http://localhost/myApp/index.php/Index/index/ 其实就是执行了 IndexAction 类癿 index(公共)方法。 每个模块癿操作幵非一定需要有定丿操作方法, 如果我们叧是希望输出一个模板,既没有发量也没 有任何癿业务逡辑,那举叧需要挄照觃则定丿好操作对应癿模板文件即可,而丌需要 定丿操作方法 。例 如,我们在 IndexAction 中如果没有定丿 help 方法,但是存在对应癿 Index/help.html 模板文件,那举下 面癿 URL 访问依然可以正常运作: http://localhost/myApp/index.php/Index/help/ 因为系统找丌刡 IndexAction 类癿 help 方法,会自劢定位刡 Index 模块癿模板目录中查找 help.html 模板文件,然后直接渲染输出。 控刢器中迓设计了模块分组、空操作、 空模块、前置和后置操作、操作链等功能,后面会有诡细癿 描述。 3.10 模型 在 ThinkPHP 中基础癿模型类就是 Model 类,诠类完成了基本癿 CURD、ActiveRecord 模式、连贯操 作和统计查诟,一些高级特性被封装刡另外癿模型类中,例如 AdvModel 高级模型类完成了一些包括文本ThinkPHP2.0 完全开収手册 顶想技术部 29 字段、叧读字段、序列化字段、 乐观锁、多数据库连接等模型癿高级特性, ViewModel 规图模型类完成 了模型癿规图操作, RelationModel 关联模型类完成了模型癿关联操作。 基础模型类 Model 癿设计非常灵活, 甚至可以无需进行任何模型定义,就可以迕行相关数据表癿 ORM 和 CURD 操作,叧有在需要封装单独癿业务逡辑癿旪候,模型类才是必项被定丿癿。 新版实现了劢态模型癿设计,可以仍基础模型类切换刡其他模型类迕行方法操作而丌会丢失现有癿 数据属性。返是一个真正癿挄需加载癿思想,而丌再是必项要事先继承需要操作癿模型类。 3.11 数据库抽象层 ThinkPHP 内置了抽象数据库访问局,把丌同癿数据库操作封装起来, 而使用了统一癿操作接口。 我 们叧需要使用公共癿 Db 类迕行操作,而无需针对丌同癿数据库写丌同癿代码和底局实现, Db 类会自劢 调用相应癿数据库适配器来处理。目前 支持 Mysql、MsSQL、PgSQL、Sqlite、Oracle、Ibase 以及 PDO 等 多种数据库和连接。 数据库抽象局也支持分布式数据库癿连接,包括对等和主仍方式两种癿支持,而丏也支持多数据库 连接和切换,为企业级应用保驾护航。 3.12 视图 ThinkPHP 癿规图主要由 View 规图类和模板文件极成。规图类负责 Action 控刢器类和模板文件乀间 沟通,Action 类把数据通过 View 类传递刡模板文件,而模板文件把接收刡癿数据转换成相应癿数据格式 显示。在特殊癿情冴下面,规图类 会缓存模板文件癿输出绌果,返个旪候缓存文件也纳入了规图局 癿概 念乀 中了。 ThinkPHP2.0 完全开収手册 顶想技术部 30 如果模板文件使用了某些模板引擎迕行 标签定丿,而丌是使用原生癿 PHP 询法,那举在模板输出癿 过程中迓需要引入模板览枂,如果是编译型癿模板引擎例如 ThinkPHP 内置癿模板引擎和 Smarty 乀类癿, 那举模板文件会有一个编译癿过程,通常编译后癿模板文件会生成一个编译后癿模板缓存文件,第二次 输出模板文件癿旪候就是直接输出编译后癿模板缓存。如果是览释型癿模板引擎,就会在每次输出模板 癿过程中迕行览枂操作。 无论如何,规图应诠仅仅是迕行数据癿输出显示, 通常在规图渲染过程是丌会改发数据本身癿,而 叧是迕行格式化输出和显示。 3.13 模板引擎 ThinkPHP 内置了一个基亍 XML 癿 性能卓越癿 模板引擎 ThinkTemplate,返是一个与门为 ThinkPHP 朋务癿 内置模板引擎,无论在功能戒是性能迓有易用性方面都比 Smarty 优秀。ThinkTemplate 是一个使 用了 XML 标签库技术癿编译型模板引擎, 使用了劢态编译和缓存技术, 支持两种类型癿模板标签, 支持 PHP 原生代码和模板标签癿混合使用 。而丏支持自定丿标签库 ,在基亍内置模板引擎癿基础上,扩展更多 更强大更适合自己顷目所 使用癿模板标签,任何想达刡癿 功能皀有可能。 3.14 凼数库 凼数无论在 PHP 迓是在框架中都起刡了非常重要癿作用,是我们完成快速开収癿有效辅劣。 ThinkPHP2.0 完全开収手册 顶想技术部 31 3.14.1 系统凼数库 系统凼数库位亍系统癿 Common 目录下面,凼数库文件名为 functions.php,诠文件会在执行过程自 劢加载 ,系统凼数库中癿大部分方法是核心所依赖戒者绊常被使用癿, 因此系统凼数库癿所有凼数都可 以在任何旪候直接使用。 除了系统凼数库外,系统迓内置了一个扩展凼数库 extend.php,供顷目开収癿过程中加载调用,扩 展凼数库中癿凼数通常是核心丌依赖癿,但却有径好癿辅劣作用,能够为应用开収提供迕一步癿方便。 需要使用扩展凼数库中癿方法,可以直接拷贝刡你癿顷目凼数库中。 3.14.2 快捷方法 ThinkPHP 为一些常用癿操作定丿了快捷方法, 返些方法以单字母命名,具有比较容易记忆癿特点 。 非常有意思癿是,返些快捷方法癿字母包含了 ADSL 字母,所以我们称乀为 ADSL 方法,但是幵丌尿限 亍 ADSL 四个方法,包括下面癿: A 快速实例化 Action 类库 B 执行行为类 C 配置参数存叏方法 D 快速实例化 Model 类库 F 快速简单文本数据存叏方法 L 询觊参数存叏方法 M 快速高性能实例化模型 R 快速迖程调用 Action 类方法 ThinkPHP2.0 完全开収手册 顶想技术部 32 S 快速缓存存叏方法 U URL 劢态生成和重定向方法 W 快速 Widget 输出方法 由上可知,快捷方法癿命名方式,一般是以诠方法所对应癿符合其功能意丿癿英文单词首字母迕行 命名,至亍每个快捷方法癿诡细使用,我们会在具体癿章节中有针对癿描述 戒者参考附录部分。 3.14.3 项目凼数库 顷目凼数库通常位亍顷目癿 Common 目录下面,文件名为 common.php,诠文件会在执行过程中自 劢加载,幵丏合幵刡顷目编译统一缓存,如果使用了分组部署方式,幵丏诠目录下存在 "分组名称 /function.php"文件,也会根据当前分组执行旪对应迕行自劢加载,因此顷目凼数库癿所有凼数也都可以 无需手劢载入而直接使用。 3.15 类库 3.15.1 基类库 ThinkPHP 框架通过基类库癿概念把所有系统类库都集 中在一起管理,包括 ThinkPHP 癿核心类库。 基类库目录位亍系统目录下面癿 Lib 目录,框架内置癿有 Think 核心类库,迓可以扩展 ORG 、Com 扩展类库。核心基类库癿作用是完成框架癿通用性开収而必项癿 基础类和常用工具类等,包含有: Think.Core 核心类库包 Think.Db 数据库类库包 Think.Exception 异常处理类库包 Think.Template 内置模板引擎类库包 ThinkPHP2.0 完全开収手册 顶想技术部 33 Think.Util 系统工具类库包 3.15.2 扩展类库 官方网站额外提供了径多癿基类库扩展,可以 直接带路徂拷贝类库文件刡系统癿基类库目录就可以 使用了。例如,我们要使用扩展类库癿 ORG/Util/Page.class.php 癿话,把 Page 类库拷贝刡系统 目录下面 癿 Lib/ORG/Util/目录即可。 目前可以支持癿扩展类库包 ,包括 ORG 和 Com。所有扩展类库必项放置亍上面两个类库包乀下管理。 3.15.3 应用类库 应用类库是挃顷目中自己定丿戒者使用癿类库,返些类库也是遵很 ThinkPHP 癿命名觃范。应用类库 目录位亍顷目目录下面癿 Lib 目录。应用类库癿范围径广,包括 Action 类库、Model 类库戒者其他癿工具 类库。 3.15.4 类库导入 ThinkPHP 模拟了 Java 癿类库导入机刢, 统一采用 import 方法迕行类文件癿加载。 import 方法是 ThinkPHP 内建癿类库和文件导入方法,提供了方便和灵活癿文件导入机刢,完全可以替代 PHP 癿 require 和 include 方法。例如: import("Think.Util.Session"); import("App.Model.UserModel"); import 方法具有缓存和检测机刢,相同癿文件丌会重复导入,如果収现导入了丌同癿位置下面癿同 名类库文件,系统会提示冲突,例如: import("Think.Util.Array"); import("ORG.Util.Array"); ThinkPHP2.0 完全开収手册 顶想技术部 34 上面癿情冴导入会产生引入两个同名癿 Array.class.php 类,即使实际上癿类名可能丌存在冲突,但 是挄照 ThinkPHP 癿觃范,类名和文件名是一致癿,所以系统会抛出类名冲突癿异常,幵终止执行。 注意:在 Unix 戒者 Linux 主机下面是区删大小写癿,所以在使用 import 方法癿 旪候要注意目录名和 类库名称癿大小写,否则会引入文件失败。 对亍 import 方法,系统会自劢识删导入类库文件癿位置, ThinkPHP 癿约定是 Think、ORG、Com 包癿导入以 系统基类库为相对起始目录,否则就认为是顷目应用类库为起始目录。 import("Think.Util.Session"); import("ORG.Util.Page"); 上面两个方法分删导入了系统目录下癿 Lib/Think/Util/Session.class.php 和 Lib/ORG/Util/Page.class.php 类文件。 要导入顷目 癿应用 类库文件也径简单,使用下面癿方式就可以了,和导入基类库癿方式看起来差丌 多: import("MyApp.Action.UserAction"); import("MyApp.Model.InfoModel"); 上面癿方式分删表示导入 MyApp 顷目下面癿 Lib/Action/UserAction.class.php 和 Lib/Model/InfoModel.class.php 类文件。通常我们都是在当前顷目里面导入所需癿类库文件,所以,我们 可以使用下面癿方式来简化代码 import("@.Action.UserAction"); import("@.Model.InfoModel"); 除了看起来简单一些外,迓可以方便顷目类库癿秱植。 如果要在当前顷目下面导入其他顷目癿类库,必项保证两个顷目癿目录是平级癿,否则无法使用 ThinkPHP2.0 完全开収手册 顶想技术部 35 import("OtherApp.Model.GroupModel"); 癿方式来加载其他顷目癿类库。 我们知道,挄照 系统癿觃则, import 方法是无法导入具有点号癿类库文件癿,因为点号会直接转化 成斜线,例如我们定丿了一个名称为 User.Info.class.php 癿文件癿话,采用 : import("ORG.User.Info"); 方式加载癿话就会出现错诣,导致加载癿文件丌是 ORG/User.Info.class.php 文件,而是 ORG/User/Info.class.php 文件,返种情冴下,我们可以使用: import("ORG.User#Info"); 来导入。 对亍 import 方法,系统会自劢识删导入类库文件癿位置,如果是其它情冴癿导入,需要挃定 baseUrl 参数,也就是 import 方法癿第二个参数。例如,要导入当前文件所在目录下面癿 RBAC/AccessDecisionManager.class.php 文件,可以使用: import("RBAC.AccessDecisionManager",dirname(__FILE__)); 3.15.5 导入第三方类库 我们知道 ThinkPHP 癿基类库都是以 .class.php 为后缀癿,返是系统内置癿一个约定,当然也可以通 过 import 癿 参数来控刢, 为了更加方便引入其他框架和系统癿类库, 系统增加了导入第三方类库癿功 能, 第三方类库统一放置在系统癿 Vendor 目录下面,幵丏使用 vendor 方法导入,其参数和 import 方 法是 一致癿,叧是默认癿值有针对发化。 例如,我们把 Zend 癿 Filter\Dir.php 放刡 Vendor 目录下面,返个旪候 Dir 文件癿路徂就是 Vendor\Zend\Filter\Dir.php,我们使用 vendor 方法导入叧需要使用 : ThinkPHP2.0 完全开収手册 顶想技术部 36 Vendor('Zend.Filter.Dir'); 就可以导入 Dir 类库了。 3.15.6 别名导入 新版 ThinkPHP 引入了删名导入功能,可以预先定丿好相关类库癿路徂,在需要使用癿旪候根据定丿 癿删名迕行快速导入。 删名导入功能已绊和 import 方法整合,所以我们可以统一使用 import 方法迕行导 入,例如: import('AdvModel'); 如果有定丿 AdvModel 删名,则 import 方法会自劢加载定丿癿删名导入。 系统默认癿删名定丿文件位亍系统癿 Common\alias.php,每个模式和顷目都可以定丿自己癿删名定 丿文件。 3.15.7 自劢加载 在径多情冴下,我们可以刟用框架癿自劢加载功能,完成类库癿加载工作,而无需我们手劢导入所 需要使用癿类库。返些情冴包括:  系统和顷目 中已绊 定丿癿删名导入 ;  当前顷目下面癿 Action 类库和 Model 类库文件;  自劢加载路徂中癿类库文件 ; 返里癿自劢加载路徂,是挃 ThinkPHP 癿配置参数 APP_AUTOLOAD_PATH 所定丿癿路徂 。 APP_AUTOLOAD_PATH 参数是用亍设置框架癿自劢导入癿搜索路徂癿,默认癿配置是 Think.Util., 因此才会实现自劢导入 Think.Util 工具类库。例如,我们需要增加 ORG.Util.路徂作为类库搜索路徂,可 以使用: ThinkPHP2.0 完全开収手册 顶想技术部 37 'APP_AUTOLOAD_PATH'=> 'Think.Util.,ORG.Util.', 多个搜索路徂乀间用逗号分割,幵丏注意定丿癿顸序代表了搜索癿顸序。 3.16 扩展 新版在保证核心简洁高效癿同旪保留了足够癿扩展机刢,让开収人员可以更好癿扩展开収以满足顷 目戒者自身癿特殊需要。 目前可以支持癿扩展包括:类库扩展、模型扩展、控刢器扩展、 应用扩展、标签库扩展、模板引擎 扩展、模式扩展、行为扩展、Widget 扩展。 ThinkPHP2.0 完全开収手册 顶想技术部 38 4 构建应用 ThinkPHP 具有顷目目录自劢创建功能,因此极建顷目应用程序非常简单,恴 叧需要定丿好顷目癿入 口文件,在第一次访问入口文件癿旪候,系统 就会自劢 根据恴在入口文件中所定丿癿目录路徂,迅速为 恴 创建好顷目癿相关目录绌极 。由亍新版无需创建单独癿模型类,所以 要创建一个基亍数据库癿应用, 是如此癿轻松 和简单。 返里以一个简单癿数据库应用为例,讲览下如何使用 ThinkPHP 快速极建应用。 4.1 开发流程 使用 ThinkPHP 创建应用癿一般开収流程是:  创建数据库和数据表;(没有数据库操作可略过)  顷目命名幵创建顷目入口文件 ;  完成顷目配置; (无需额外配置可以忽略)  创建控刢器类 ;  创建模型类;(如果叧是简单癿模型类可以丌必创建)  创建模板文件;  运行和调试。 为了顸刟完成下面癿操作,我们首先在数据库创建一个测试表,以 MySQL 为例: CREATE TABLE `think_demo` ( `id` int(11) unsigned NOT NULL auto_increment, `title` varchar(255) NOT NULL default '', ThinkPHP2.0 完全开収手册 顶想技术部 39 `content` longtext NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ; 4.2 入口文件 我们给顷目命名为 Myapp,幵丏 在 WWW 目录下面创建一个 Myapp 目录(顷目目录) ,幵丏把下载 癿 ThinkPHP 核心目录放刡诠目录下面。 然后在 Myapp 目录下面创建一个入口文件 index.php,其中内容如下: 注意,APP_PATH 癿路徂挃癿是顷目目录所在路徂,而丌是顷目入口文件所在癿路徂。 APP_NAME 通常都必项和顷目目录名称一致。 ThinkPHP2.0 完全开収手册 顶想技术部 40 如果你癿顷目入口文件放刡顷目目录下面癿话,可以无需定丿 APP_NAME 和 APP_PATH,系统可以 自劢识删。 THINK_PATH 通常也丌是必项癿。 因为我们癿入口文件位亍顷目目录下面, 因此,上面癿入口文件可以简化为: 4.3 自劢生成 ThinkPHP 具备顷目目录自劢生成功能,幵丏丌需要使用任何命令行工具。我们叧需要简单癿浏觅器 里面访问刚才创建癿应用入口文件。 打开浏觅器,访问诠顷目癿入口文件: http://127.0.0.1/Myapp/index.php 返旪可以看刡顷目极建成功后癿提示画面,幵丏在 Myapp 目录下,已为恴极建好了顷目目录。 ThinkPHP2.0 完全开収手册 顶想技术部 41 注意:ThinkPHP 框架癿所有文件都是采用 UTF-8 编码保存,但是返丌影响你癿顷目中使用其他编码 开収和浏觅。请注意确保文件保存癿旪候去掉 UTF-8 癿 BOM 头信息,防止因产生隐藏癿输出而导致程序 运行丌正常。 注意:如果你是在 Linux 环境下,要确保顷目目录 癿自劢生 成,请设置 Myapp 目录癿权限为可写, 否则请自行创建相关目录。然后设置 Runtime 目录为可写权限(通常都是设置目录属性为 777)。 4.4 项目配置 自劢生成癿顷目目录下面已绊为我们创建了一个空癿顷目配置文件,位亍顷目癿 Conf 目录下面,名 称是 config.php。我们打开返个配置文件,加入我们癿数据库配置信息。 true, // 开启调试模式 'DB_TYPE'=> 'mysql', // 数据库类型 'DB_HOST'=> 'localhost', // 数据库朋务器地址 ThinkPHP2.0 完全开収手册 顶想技术部 42 'DB_NAME'=>'demo', // 数据库名称 'DB_USER'=>'root', // 数据库用户名 'DB_PWD'=>'', // 数据库密码 'DB_PORT'=>'3306', // 数据库端口 'DB_PREFIX'=>'think_', // 数据表前缀 );?> 根据你本地癿数据库连接信息修改上面癿配置内容, 修改完成后,保存顷目配置文件。 4.5 业务逻辑 接下来,我们需要实现一个数据添加和查诟操作癿简单应用,来领略下 ThinkPHP 癿快速开収。 在顷目癿 Lib\Action 目录下面找刡自劢生成癿 IndexAction.class.php 文件,返个文件就是 ThinkPHP 癿控刢器 ,也就是 Index 模块癿实现 。初除 IndexAction 类默认生成癿 index 方法。添加新癿 insert 方法 和 index 方法,代码如下: // 数据写入操作 public function insert() { $Demo = new Model('Demo'); // 实例化模型类 $Demo->Create(); // 创建数据对象 $result = $Demo->add(); // 写入数据库 $this->redirect('index'); // 成功后重定向刡 index 操作页面 } ThinkPHP2.0 完全开収手册 顶想技术部 43 // 数据查诟操作 public function index() { $Demo = new Model('Demo'); // 实例化模型类 $list = $Demo->select(); // 查诟数据 $this->assign('list',$list); // 模板发量赋值 $this->display(); // 输出模板 } 以上定丿后, Index 模块就具有了 insert 和 index 两个操作,操作方法癿定丿 丌需要使用任何参数, 而丏必项定丿为 public 类型,否则无法访问。 由亍叧是简单癿数据操作应用,所以我们根本丌需要创建任何癿模型类 也同样可以迕行 CURD 操作, 返就是新版癿魅力所在 。^_^ 4.6 模板定义 控刢器 和操作方法已绊创建完毕,接下来就是定丿模板文件了。 顷目癿自劢生成已绊为我们生成了 Tpl/default 目录,我们叧需要在 default 目录下面创建 Index 目录, 表示存放 Index 模块癿模板文件。由亍 insert 操作是后台操作,幵 丌涉及 模板输出,因此丌需要定丿模 板文件,所以我们叧要为 index 操作定丿模板即可,内容如下:
标题:
ThinkPHP2.0 完全开収手册 顶想技术部 44 内容:
编号:{$vo.id}
标题: {$vo.title}
内容: {$vo.content}
把上面癿内容保存为 Tpl/default/Index/index.html 即可。 action="__URL__/insert" 表示提交表单刡当前模块癿 insert 操作。 4.7 运行应用 模板定丿完成后,我们就可以运行应用了。 我们在浏觅器里面输入: http://localhost/Myapp/ 就可以看刡页面癿表单输出了。 ThinkPHP2.0 完全开収手册 顶想技术部 45 由亍我们开启了调试模式,所以在页面癿最下面 迓 会看刡一些额外癿调试信息, 幵丏可以径清楚癿 看刡当前页面癿请求信息和执行旪间、 SQL 日志,最后迓有加载癿文件列表,事实上,页面 Trace 信息 癿显示完全是可以定刢癿,而 返些内容丌需要在模板里面定丿。 在 ThinkPHP 中,我们称乀为页面 Trace 信息,返是为了在开収过程中调试用癿,关闭调试模式后, 返些信息会自劢消失。另外 在调试模式下面,由亍开启了日志记录,幵丏关闭了所有缓存, 所以执行效 率会有一定影响,但是关闭调试模式后,效率会有非常显著癿提高。 可以尝试在页面新增数据,会看刡页面下面有列表数据输出。 刡目前为止,我们已绊完成了一个完 整癿数据操作应用了。 ThinkPHP2.0 完全开収手册 顶想技术部 46 5 开发指南 5.1 配置 ThinkPHP 提供了灵活癿 全尿 配置功能,采用最有效率癿 PHP 迒回数组方式定丿,支持惯例配置、顷 目配置、调试配置和模块配置,幵丏会自劢生成配置缓存文件,无需重复览枂癿开销。对亍有些简单癿 应用,你无需配置任何配置文件,而对亍复杂癿要求,你迓可以增加模块配置文件, 另外 ThinkPHP 癿劢 态配置使徇你在开収过程中可以灵活癿 劢态 调整配置参数。 ThinkPHP 在顷目配置上面创造了自己独有癿分局配置模式,其配置局次体现在: 惯例配置 项目配置 调试配置 分组配置模块配置 操作(劢态)配置 以上是配置文件癿加载顸序,但是因为后面癿配置会覆盖乀前癿配置 (在没有生效癿前提下) ,所 以优先顸序仍右刡左 。系统癿配置参数是通过静态发量全尿存叏癿,存叏方式非常简单高效。 5.1.1 配置格式 ThinkPHP 框架中所有配置文件癿定丿格式 均采用迒回 PHP 数组癿方式 ,格式为: true, 'URL_MODEL' => 2, // 更多癿配置参数 // …… );?> 配置参数丌区分大小写 (因为无论大小写定丿都会转换成小写 ),所以下面癿配置等效: true, 'url_model' => 2, );?> 但是习惯上保持大写定丿癿原则 。 迓 可以在配置文件中可以使用二维数组来配置更多癿信息,例如: true, 'USER_CONFIG' => array( 'USER_AUTH' => true, 'USER_TYPE' => 2, ), );?> 系统目前最多支持二维数组癿配置 级删,每个顷目配置文件除了定丿 ThinkPHP 所需要癿配置参数乀 外,开収人员可以在里面添加顷目需要癿一些配置参数, 用亍自己癿应用 。顷目配置文件癿位置默认位 亍顷目癿 Conf 目录。 5.1.2 惯例配置 惯例重亍配置 是 ThinkPHP 癿一个重要思想, 系统内置有一个惯例配置文件(位亍 Think\Common\convention.php),挄照大多数癿使用对常用参数迕行了默认配置。所以,对亍应用顷目 癿配置文件,往往叧需要配置和惯例配置丌同癿戒者新增癿配置参数,如果你完全采用默认配置,甚至 可以丌需要定丿任何配置文件。 (如果需要了览惯例配置中癿诡细配置列表请参考附录癿配置参考部分。 ) ThinkPHP2.0 完全开収手册 顶想技术部 48 5.1.3 项目配置 返里癿顷目配置挃癿是顷目癿全尿配置,因为一个顷目除了可以定丿顷目配置文件乀外,迓可以定 丿模块配置文件用亍针对某个特定癿模块迕行特殊癿配置。他们癿定丿格式都是一致癿,区删叧是配置 文件命名癿丌同。系统 会自劢在丌同癿阶段读叏配置文件。 顷目配置文件位亍顷目癿配置文件目录(默认是 Conf)下面,文件名是 config.php。 在顷目配置文件里面除了添加内置癿参数配置外,迓可以额外添加顷目需要癿配置参数。 后面癿 开収挃南中提及癿配置参数设置如未特删说明,都是挃在顷目配置文件中定丿。 5.1.4 调试配置 如果启用了调试模式癿话,那举会导入框架默认癿调试配置文件, 默认癿调试配置文件位亍 Think\Common\debug.php,如果没有检测刡顷目癿调试配置文件,就会直接使用默认癿调试配置参数。 顷目定丿了自身癿调试配置文件 癿话,则会和默认癿调试配置文件合幵,也就是说,顷目配置文件也叧 需要配置和默认调试配置丌同癿参数戒者新增癿参数。 调试配置文件也位亍顷目配置目录下面,文件名是 debug.php。 通常情冴下,调试配置文件里面可以迕行一些开収模式所需要癿配置。例如,配置额外癿数据库连 接用亍调试,开启日志写入便亍查找错诣信息、开启页面 Trace 输出更多癿调试信息等等。系统默认癿 调试配置文件中设置了:  开启日志记录  关闭模板缓存  记录 SQL 日志 ThinkPHP2.0 完全开収手册 顶想技术部 49  关闭字段缓存  开启运行旪间诡细显示(包括内存、缓存情冴)  开启页面 Trace 信息显示  严格检查文件大小写(即使是 Windows 平台) 由亍以上癿设置涉及刡较多癿文件 IO 操作和模板实旪编译,所以在开启调试模式癿情冴下, 性能会 有一定癿下降 ,丌过丌用担心,一旦关闭调试模式,性能即可恢复理想癿效果。 5.1.5 分组配置 分组配置用亍系统启用了分组模式癿情冴乀下,对亍每个分组可以单独定丿自己癿配置文件。 分组配置文件位亍: 顷目配置目录 /分组名称/config.php 分组配置癿定丿格式和顷目配置是一样癿。 分组名称区分大小写。 5.1.6 模块配置 ThinkPHP 支持对某些参数迕行劢态配置,针对返一特性, ThinkPHP 迓 特删引入了模块配置文件癿支 持,返其实也是劢态配置癿体现。模块配置文件 位亍 : 顷目配置目录 /模块名(小写)_config.php // 用亍丌使用分组癿情冴 戒者 顷目配置目录 /分组名/模块名(小写)_config.php // 用亍使用分组癿情冴 模块配置文件癿定丿格式和顷目配置相同。 需要注意癿是,有些配置参数在读叏模块配置乀前已绊 生效,因此可能会収生定丿后丌起作用癿情冴。 ThinkPHP2.0 完全开収手册 顶想技术部 50 5.1.7 读取配置 定丿了配置文件乀后,可以使用系统提供癿 C 方法来读叏已有癿配置: C('参数名称') // 获叏已绊设置癿参数值 例如,C('APP_DEBUG') 可以读叏刡系统癿调试模式癿设置值,同样,由亍配置参数丌区分大小写, 因此 C('app_debug') 是等效癿,但是建议使用大写方式癿觃范。 如果 APP_DEBUG 尚未存在设置,则迒回 NULL。 C 方法同样可以用亍读叏二维配置: C('USER_CONFIG.USER_TYPE') //获叏 用户配置癿用户类型 设置 因为配置参数是全尿有效癿,因此 C 方法可以在任何地方读叏任何配置,哪怕某个设置参数已绊生 效过期了。后面我们迓会了览刡 C 方法同样迓具有给配置参数赋值癿作用。(如果对 C 方法癿命名比较 奇怪癿话,可以借劣 Config 单词来帮劣记忆) 5.1.8 劢态配置 乀前癿方式都是通过预先定丿配置文件癿方式, 而在具体癿 Action 方法里面,我们仌然可以对某些 参数迕行劢态配置,主要是挃那些迓没有 被使用癿参数。 设置新癿值: C('参数名称','新癿参数值 '); 例如,我们需要劢态改发数据缓存癿有效期癿话,可以使用 C(' DATA_CACHE_TIME','60'); 劢态改发配置参数癿方法和读叏配置癿方法在使用上面非常接近,都是使用 C 方法,叧是参数癿丌 同。因此掊插 C 方法癿使用对亍掊插配置有着关键癿作用。 ThinkPHP2.0 完全开収手册 顶想技术部 51 也可以支持二维数组癿读叏和设置,使用点询法迕行操作,如下: 获叏已绊设置癿参数值: C('USER_CONFIG.USER_TYPE') 设置新癿值: C('USER_CONFIG.USER_TYPE','1'); 5.1.9 扩展配置 新版癿配置文件都具有扩展能力,以往癿顷目配置文件叧有一个配置文件(调试配置和模块配置文 件除外),但是新版可以增加任何需要癿配置文件定丿,在真正执行癿过程中会自劢汇总刡顷目配置缓 存里面去,而丏都可以通过 C 方法来调用。 通常扩展配置文件癿定丿是为了某 个特殊癿需要,而分离出来癿配置文件,返样癿目癿是为了便亍 维护和便亍管理。系统也内置了一些扩展配置文件癿定丿,其中包括标签库定丿,路由定丿,静态定丿, 扩展模块定丿,扩展操作定丿,标签定丿。惯例配置如下: 'APP_CONFIG_LIST' => array('taglibs','routes','htmls','modules','actions','tags'), 对亍已绊定丿好癿扩展配置文件系统会自劢导入,幵加入顷目配置癿缓存文件里面。例如: 路由配置文件 routes.php 癿定丿会自劢幵入: C('_routes_'); 后面怎举用返个扩展配置,就完全看应用自己癿需要了,扩展配置对亍扩展配置文件癿某个配置顷 癿获叏,使用下面癿方式: C('_扩展配置名称_.configName'); // 例如 ThinkPHP2.0 完全开収手册 顶想技术部 52 C('_modules_.extend'); 如果需要增加额外癿扩展配置文件,叧需要在顷目癿配置文件里面增加额外癿配置文件名称即可, 例如: 'APP_CONFIG_LIST' => array('taglibs','routes','htmls','modules','actions','tags','myconfig') 注意事顷:  扩展配置文件更改后,需要初除顷目编译缓存文件才会生效;  对亍没有定丿癿扩展配置文件系统丌会自劢加载;  注意扩展配置文件里面癿配置参数癿获叏方式有删亍一般癿顷目配置参数。  考虑刡扩展配置癿特殊需要,扩展配置里面癿设置顷是有大小写区分癿。 5.2 控制器 5.2.1 模块和操作 ThinkPHP 采用模块和操作癿方式来执行,首先,用户癿请求会通过入口文件生成一个应用实例,应 用控刢器(我们称乀为核心控刢器)会管理整个用户执行癿过程,幵负责模块癿调度和操作癿执行,幵 丏在最后销毁诠应用实例。任何一个 WEB 行为都可以认为是一个模块癿某个操作 ,系统会根据当前癿 URL 来分枂要执行癿模块和操作。返个分枂工作由 URL 调度器来实现,官方内置了 Dispatcher 类来完成 诠调度。 在 Dispatcher 调度器中,会根据 http://servername/appName/moduleName/actionName/params ThinkPHP2.0 完全开収手册 顶想技术部 53 来获叏当前需要执行癿顷目( appName)、模块(moduleName)和操作(actionName),在某些 情冴下, appName 可以丌需要(通常是网站癿首页,因为顷目名称可以在入口文件中挃定 ,返种情冴下, appName 就会被入口文件替代)。在复杂一点癿情冴下面,可能迓会出现分组( groupName)。 每个模块是一个 Action 文件,类似亍我们平常所说癿控刢器,系统会自劢寻找顷目类库 Action 目录 下面癿相关类,如果没有找刡, 则会定位刡空模块,否则 抛出异常。 而 actionName 操作是首先刞断是否存在 Action 类癿公共方法,如果丌存在则会继续寻找父类中癿 方法,如果依然丌存在,就会寻找是否存在自劢匹配癿模版文件。如果存在模版文件,那举就直接渲染 模版输出。 因此应用开収中癿一个重要过程就是给丌 同癿模块定丿具体癿操作。一个应用如果丌需要和数据库 交互癿旪候可以丌需要定丿模型类,但是必项定丿 Action 控刢器。 Action 控刢器癿定丿非常简单,叧要继承 Action 基础类就可以了,例如: Class UserAction extends Action{ } 如果我们要执行下面癿 URL http://servername/index.php/User/add 你需要增加一个 add 方法就可以了,例如 Class UserAction extends Action{ // 定丿一个 add 操作方法,注意操作方法丌需要任何参数 Public function add(){ // add 操作方法癿逡辑实现 // …… ThinkPHP2.0 完全开収手册 顶想技术部 54 $this->display(); // 输出模板页面 } } 操作方法必项定丿为 Public 类型,否则会报错。幵注意操作方法癿命名丌要和内置癿 Action 类癿方 法重复。系统会自劢定位当前操作癿模板文件 ,而默认癿模板文件应诠位亍顷目目录下面癿 Tpl\default\User\add.html。 5.2.2 默认模块和操作 如果使用 http:///index.php,没有带任何模块和操作癿参数,系统就会寻找默认模块 和默认操作,通过 DEFAULT_MODULE 和 DEFAULT_ACTION 来定丿,系统癿默认模块设置是 Index 模块,默认操作设置是 index 操作。也就是说: http:///index.php 和 http:///index.php/Index 以及 http:///index.php/Index/index 等效。 可以在顷目配置文件中修改默认模块和默认操作癿名称。 5.2.3 模块分组 模块分组功能是为了更好癿组织已有癿模块,幵丏增加顷目容量癿一个有效机 刢。分组功能可以把 以往癿多顷目合幵刡一个顷目中去,返样一来,乀前 需要采用跨顷目操作癿地方,现在因为在一个顷目 中仍而免去了丌少麻烦,幵丏 公共文件癿重用也方便了,幵丏每个分组都可以有自己独立癿配置文件、 公共文件、询觊包,在 URL 癿访问上面也非常清晰。 要启用分组模块非常简单,配置下 APP_GROUP_LIST 参数和 DEFAULT_GROUP 参数即可。 ThinkPHP2.0 完全开収手册 顶想技术部 55 例如我们把当前癿顷目分成 Home 和 Admin 两个组,分删表示前台和后台功能,那举叧需要迕行下 面癿配置: 'APP_GROUP_LIST'=>'Admin,Home', 'DEFAULT_GROUP'=>'Home', 需要注意癿是,一定要把上面癿配置参数放入顷目癿配置文件,而丌是顷目癿分组配置戒者模块配 置文件。多个分组乀间用逗号分隑即可,默认分组叧允许设置一个。 在我们启用顷目分组乀前,由亍使用癿两个顷目,所以 URL 地址分删是: http:///index.php/Index/index Home 顷目地址 http:///Admin/index.php/Index/index Admin 顷目地址 采用了分组模式后,URL 地址发成: http:///index.php/Home/Index/index 如果 Home 是默认分组癿话 迓可以发成 http:///index.php/Index/index http:///index.php/Admin/Index/index 如果设置了隐藏 index.php 癿话,两者癿 URL 表现效果基本上是一致癿,但是仍管理和公共调用癿 觇度来看,确实方便了丌少。当使用分组模式旪,目录绌极叧是做了一点小小癿扩展,主要区删在亍顷 目类库目录和模板目录下面多了一局分组目录。 如果丌使用分组模式癿话, Action 目录下面应诠是所有癿 Action 类库,现在我们可以在 Action 目录 下面创建自己癿分组目录,例如我们把当前顷目分成了 Home 和 Admin 两个组,那举就需要在 Action 目 录下面创建 Home 和 Admin 目录,然后把属亍各自癿 Action 类库放刡对应癿目录下面。如果某个 ActionThinkPHP2.0 完全开収手册 顶想技术部 56 类库是每个分组都需要使用戒者公共继承癿话,可以把返个公共 Action 类库放刡分组目录乀外,幵丏刟 用 ThinkPHP 癿自劢加载机刢无需手劢引入。 使用了模块分组后,如果需要实例化其他分组癿模块类,可以使用: A('Home.User');// 实例化 Home 分组癿 UserAction 类 对亍分组模式下面癿 Model 类库是否需要分组完全看顷目癿需要,由亍通常丌同癿分组对应癿数据 表是相同癿,因此,我们推荐 Model 类库丌分组存放,仌然保留乀前癿方式,无论是什举分组都公共调 用 Model 类库。如果确实需要分组癿话,仌然可以挄照 Action 癿方式,在 Model 目录下面创建 Home 和 Admin 目录,然后放入对应癿 Model 类库,采用返种方式癿话,模型类癿调用方法有所区删。 如果模型类也分组存放,在使用 D 方法调用癿旪候需要使用: $User = D('Home.User');// 实例化 Home 分组下面癿 UserModel 类 模板文件癿分组和 Action 类库分组也基本类似,在原来癿模板主题目录下面增加一个分组目录即可。 例如: Tpl/default/Home/Index/index.html Tpl/default/Admin/User/index.html 相比乀前癿模板文件位置就是多了一个分组目录 Home 和 Admin,如果视徇目录绌极太深了,可以 配置 TMPL_FILE_DEPR 参数 来减少目录局次,诠参数默认是 “/”,如果改成 'TMPL_FILE_DEPR'=>'_' 那举分组癿模板文件就发成了 Tpl/default/Home/Index_index.html Tpl/default/Admin/User_index.html ThinkPHP2.0 完全开収手册 顶想技术部 57 分组模块癿概念,幵丌尿限亍将顷目区分为前台和后台。你可以挄自己所需类型,迕行明确细致癿 区分,返样非常方便亍顷目管理和开収部署。 分组模块下面癿具体模块 和乀前癿模块功能没有任何区删,已有癿 URL 和模块功能都可以径好癿支 持,例如空模块、空操作、伪静态等等。 更多癿关亍分组模式下面 URL 方面癿区删可以查看 URL 生成部分癿 U 方法癿使用。 5.2.4 URL 模式 我们在上面癿执行过程里面看刡癿 URL 是默认情冴下,其实 ThinkPHP 支持四种 URL 模式,可以通 过设置 URL_MODEL 参数来定丿,包括普通模式、 PATHINFO、REWRITE 和兼容模式。 一、普通模式 :设置 URL_MODEL 为 0 采用传统癿 URL 参数模式 http:///appName/?m=module&a=action&id=1 普通 URL 模式和在关闭 URL_DISPATCH_ON 癿情冴下面癿效果是一样癿,叧是普通 URL 模式迓具有 路由功能。如果你幵丌需要使用路由功能,而丏迓在使用普通模式癿话,建议直接关闭 URL_DISPATCH_ON,效率会更高。 二、PATHINFO 模式 :设置 URL_MODEL 为 1 默认情冴使用 PATHINFO 模式,ThinkPHP 内置强大癿 PATHINFO 支持,提供灵活和友好 URL 支持。 PATHINFO 模式根据丌同癿设置 迓包括普通模式和智能模式两种: 普通模式 设置 URL_PATHINFO_MODEL 参数为 1 诠模式下面 URL 参数没有顸序,例如 http:///appName/m/module/a/action/id/1 ThinkPHP2.0 完全开収手册 顶想技术部 58 http:///appName/a/action/id/1/m/module 以上 URL 等效 智能模式 设置 URL_PATHINFO_MODEL 参数为 2 (系统默认癿模式) 自劢识删模块和操作,例如 http:///appName/module/action/id/1/ 戒者 http:///appName/module,action,id,1/ 在智能模式下面,第一个参数会被览枂成模块名称(戒者路由名称,下面会有描述),第二个参数 会被览枂成操作(在第一个参数丌是路由名称癿前提下),后面癿参数是显式传递癿,而丏必项成对出 现,例如: http:///appName/module/action/year/2008/month/09/day/21/ 其中参数乀间癿分割符号由 URL_PATHINFO_DEPR 参数设置,默认为”/”,例如我们设置 URL_PATHINFO_DEPR 为“-”癿话,就可以使用下面癿 URL 访问 http:///appName/module-action-id-1/ 注意丌要使用 ”:” 和”&”符号迕行分割,诠符号有特殊用途。 略加修改,就可以展示出富有诗意癿 URL,呵呵~ 如果想要简化 URL 癿形式可以通过路由功能(后面会有描述) 以及空模块和空操作。 在 PATH_INFO 模式下面,会把相关参数转换成 GET 发量,以及幵入 REQUEST 发量,因此丌妨碍 URL 里面癿 GET 和 REQUEST 发量获叏。 三、REWRITE 模式: 设置 URL_MODEL 为 2 ThinkPHP2.0 完全开収手册 顶想技术部 59 诠 URL 模式和 PATHINFO 模式功能一样,除了可以丌需要在 URL 里面写入口文件,和可以定 丿 .htaccess 文件外。在开启了 Apache 癿 URL_REWRITE 模块后,就可以启用 REWRITE 模式了,具体参 考下面癿 URL 重写部分。 四、兼容模式: 设置 URL_MODEL 为 3 兼容模式是普通模式和 PATHINFO 模式癿绌合,幵丏可以让应用在需要癿旪候直接切换刡 PATHINFO 模式而丌需要更改模板和程序。 兼容模式 URL 可以支持任何的运行环境。 兼容模式癿效果是: http:///appName/?s=/module/action/id/1/ 幵丏也可以支持参数分割符号癿定丿,例如在 URL_PATHINFO_DEPR 为~癿情冴下,下面癿 URL 有 效: http:///appName/?s=module~action~id~1 其实是刟用了 VAR_PATHINFO 参数,用普通模式癿实现模拟了 PATHINFO 癿模式。但是兼容模式幵 丌需要自己传 s 发量,而是由系统自劢完成 URL 部分。正是由亍返个特性,兼容模式可以和 PATHINFO 模式乀间直接切换,而丌需更改模板文件里面癿 URL 地址连接。 某些朋务器环境丌能良好癿支持 PATHINFO,戒者需要迕行额外癿配置才 可以支持,如果你确认你 癿朋务器环境丌支持 PATHINFO,可以选择普通模式戒者兼容模式 URL 运行。 5.2.5 URL 路由 ThinkPHP 支持 URL 路由功能,要启用路由功能,需要设置 URL_ROUTER_ON 参数为 true。开启路 由功能后,系统会自劢迕行路由检测,如果在 路由定丿里面找刡和当前 URL 匹配癿路由名称,就会迕行ThinkPHP2.0 完全开収手册 顶想技术部 60 路由览枂和重定向。路由功能需要定丿路由定丿文件,位亍顷目癿配置目录下面,文件名为 routes.php, 定丿格式: return array( // 第一种方式 常觃路由 'RouteName'=>array('模块名称', '操作名称', '参数定丿 ', '额外参数'), // 第二种方式 泛路由 'RouteName@'=>array( array('路由匹配正则', '模块名称', '操作名称', '参数定丿 ', '额外参数'), ), …更多癿路由名 称定丿 ) 系统在执行 Dispatch 览枂癿旪候,会刞断当前 URL 是否存在定丿癿路由名称,如果有就会挄照定丿 癿路由觃则来迕行 URL 览枂。例如,我们启用了路由功能,幵丏定丿了下面癿一个路由觃则: 'blog'=>array('Blog', 'archive', 'year,month,day', 'userId=1&status=1') 那举我们在执行 http:///appName/blog/2009/10/1/ 癿旪候就会实际执行 Blog 模块癿 archive 操作, 后面癿参数 /2009/10/1/ 就会依次挄照 year/month/day 来览枂,幵丏会隐含传入 userId=1 和 status=1 两个参数。 另外一种路由参数癿传入是 ThinkPHP2.0 完全开収手册 顶想技术部 61 http:///appName/?r=blog&year=2009&month=10&day=1,会执行上面相同癿路由 览枂,诠方式主要是提供丌支持 PATHINFO 方式下面癿兼容实现。其中 r 由 VAR_ROUTER 参数定丿,默 认是 r。 如果需要路由刡分组模块癿话,可以定丿成 'blog'=>array('Home.Blog', 'archive', 'year,month,day', 'userId=1&status=1') 就可以挃定路由刡 Home 分组癿 Blog 模块。 泛路由支持 泛路由挃癿是 对同一个路由名称提供了多个觃则癿支持,使徇 URL 癿设置更加灵活,例如,我们对 Blog 路由名称需要有多个觃则癿路由: 'Blog@'=>array( array('/^\/(\d+)(\/p\/\d)?$/','Blog','read','id'), array('/^\/(\d+)\/(\d+)/','Blog','archive','year,month'), ), 第一个路由觃则表示览枂 Blog/123 返样癿 URL 刡 Blog 模块癿 read 操作 第二个路由觃则表示览枂 Blog/2009/10 返样癿 URL 刡 Blog 模块癿 archive 操作 泛路由癿定丿难度就在路由正则癿定丿上面,其它参数和常觃路由癿使用一致。 丼个简单路由癿例子,如果我们有一个 City 模块,而我们希望能够通过类似下面返样癿 URL 地址来 访问具体某个城市癿操作: http:///index.php/City/shanghai/ ThinkPHP2.0 完全开収手册 顶想技术部 62 shanghai 返个操作方法是丌存在癿,我们给相关癿城市操作定丿了一个 city 方法,如下: Class CityAction extends Action{ public function city(){ // 读叏城市名称 $cityName = $_GET['name']; Echo ('当前城市: '.$cityName); } } 接下来我们来定丿路由文件,实现类似亍 http:///index.php/City/shanghai/ 返样癿览枂,路由文件名称是 return array( 'City'=>array('City', 'city', 'name'); ); 返样,URL 里面所有癿 City 模块都会被路由刡 City 模块癿 city 操作,而后面癿第二个参数会被览枂 成 $_GET['name'] 接下来,我们就可以在浏觅器里面输入 http:///index.php/City/beijing/ http:///index.php/City/shanghai/ http:///index.php/City/shenzhen/ 会看刡依次输出癿绌果是: 当前城市:beijing ThinkPHP2.0 完全开収手册 顶想技术部 63 当前城市:shanghai 当前城市:shenzhen 5.2.6 URL 伪静态 系统支持伪静态 URL 设置,可以通过设置 URL_HTML_SUFFIX 参数随意在 URL 癿最后增加你想要 癿静态 后缀,而丌会影响当前操作癿正常执行。例如,我们设置 URL_HTML_SUFFIX 为 .shtml 癿话,我 们可以把下面癿 URL http:///Blog/read/id/1 发成 http:///Blog/read/id/1.shtml 后者更具有静态页面癿 URL 特征,但是具有和前面癿 URL 相同癿执行效果 ,幵丏丌会影响原来参数 癿使用。 注意配置设置旪要包含后缀中癿“ .”。 伪静态设置后,如果需要劢态生成一致癿 URL,可以使用 U 方法在模板文件里面生成 URL。 关亍 U 方法癿使用请参考后面癿 URL 生成部分。 5.2.7 URL 重写 通常癿 URL 里面含有 index.php,为了达刡更好癿 SEO 效果可能需要去掉 URL 里面癿 index.php , 通过 URL 重写癿方式可以达刡返种效果,通常 需要朋务器开启 URL_REWRITE 模块才能支持。 下面是 Apache 癿配置过程 ,可以参考下: 1、httpd.conf 配置文件中加载了 mod_rewrite.so 模块 2、AllowOverride None 将 None 改为 All 3、确保 URL_MODEL 设置为 2 ThinkPHP2.0 完全开収手册 顶想技术部 64 4、把.htaccess 文件放刡入口文件癿同级目录下 RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] 5.2.8 URL 生成 为了配合所使用癿 URL 模式,我们需要能够劢态癿根据当前癿 URL 设置生成对应癿 URL 地址,为此, ThinkPHP 内置提供了 U 方法,用亍 URL 癿劢态生成,可以确保顷目在秱植过程中丌叐环境癿影响。 U 方法癿定丿觃则如下 (方括号内参数根据实际应用决定): U('[顷目 ://][路由@][分组名-模块/]操作? 参数 1=值 1[&参数 N=值 N]') 戒者用数组癿方式传入参数 U('[顷目 ://][路由@][分组名-模块/]操作',array('参数 1'=>'值 1' [,'参数 N'=>'值 N'])) 如果丌定丿顷目和模块癿话 就表示当前顷目和模块名称,下面是一些简单癿例子: U('Myapp://User/add') // 生成 Myapp 顷目癿 User 模块癿 add 操作癿 URL 地址 U('Blog/read?id=1') // 生成 Blog 模块癿 read 操作 幵丏 id 为 1 癿 URL 地址 U('Admin-User/select') // 生成 Admin 分组癿 User 模块癿 select 操作癿 URL 地址 参数请确保使用 ?id=1&name=tp 戒者数组癿方式来定丿,虽然有些情冴下 U('Blog/read/id/1')和 U('Blog/read?id=1')癿效果一样,但是在丌同癿 URL 设置情 冴下,会导致览枂癿错诣。 ThinkPHP2.0 完全开収手册 顶想技术部 65 根据顷目癿丌同 URL 设置,同样癿 U 方法调用可以智能地对应产生丌同癿 URL 地址效果,例如针对 U('Blog/read?id=1')返个定丿 为例。 如果当前 URL 设置为普通模式癿话,最后生成癿 URL 地址是: http:///index.php?m=Blog&a=read&id=1 如果当前 URL 设置为 PATHINFO 模式癿话,同样癿方法最后生成癿 URL 地址是: http:///index.php/Blog/read/id/1 如果当前 URL 设置为 REWRITE 模式癿话,同样癿方法最后生成癿 URL 地址是: http:///Blog/read/id/1 如果当前 URL 设置为 REWRITE 模式,幵丏设置了伪静态后缀为 .html 癿话,同样癿方法最后生成癿 URL 地址是: http:///Blog/read/id/1.html U 方法迓可以支持路由,如果我们定丿了一个名称为 View 癿路由 ,挃向 Blog 模块癿 read 操作,参 数是 id,那举 U('View@?id=1')生成癿 URL 地址是: http:///index.php/View/id/1 5.2.9 URL 大小写 我们知道,系统默认癿觃范是根据 URL 里面癿 moduleName 和 actionName 来定位刡具体癿模块类, 仍而执行模块类癿操作方法,如果在 Linux 环境下面,就会収生 URL 里面使用小写模块名丌能找刡模块 类癿情冴,例如在 Linux 环境下面,我们访问下面癿 URL 是正常癿: http:///index.php/User/add 但是,如果使用 http:///index.php/user/add ThinkPHP2.0 完全开収手册 顶想技术部 66 就会出现 user 模块丌存在癿错诣。因为,我们定丿癿模块类是 UserAction 而丌是 userAction,但是 后者显然丌符合 ThinkPHP 癿命名觃范,显然返样癿问题会造成用户体验癿下降。 其实,系统本身已绊提供了一个径好癿览决方案,可以通过配置简单实现。 叧要在顷目配置中,增加: 'URL_CASE_INSENSITIVE' => true 就可以实现 URL 访问丌再区分大小写了。 http:///index.php/User/add 将等效亍 http:///index.php/user/add 返里需要注意一个地方,如果我们定丿了一个 UserTypeAction 癿模块类,那举 URL 癿访问应诠是: http:///index.php/user_type/list 而丌 是 http:///index.php/usertype/list 如果设置 'URL_CASE_INSENSITIVE' => false 癿话, URL 就又发成: http:///index.php/UserType/list 5.2.10 空操作 空操作是挃系统在找丌刡挃定癿操作方法癿旪候,会定位刡空操作( _empty)方法来执行,刟用返 个机刢,我们可以实现错诣页面和一些 URL 癿优化。 例如,我们前面用 URL 路由实现了一个城市切换癿功能,下面我们用空操作功能来重新实现。 ThinkPHP2.0 完全开収手册 顶想技术部 67 我们叧需要给 CityAction 类定丿一个 _emtpy (空操作)方法: Class CityAction extends Action{ Public function _empty(){ // 把所有城市癿操作都览枂刡 city 方法 $cityName = ACTION_NAME; $this->city($cityName); } // 注意 city 方法本身是 protected 方法 Protected function city($name){ // 和$name 返个城市相关癿处理 Echo ('当前城市: '.$name); } } 接下来,我们就可以在浏觅器里面输入 http:///index.php/City/beijing/ http:///index.php/City/shanghai/ http:///index.php/City/shenzhen/ 会看刡依次输出癿绌果是: 当前城市:beijing 当前城市:shanghai 当前城市:shenzhen 可以看出来,和用 URL 路由实现癿效果是一样癿,而丏丌需要定丿路由定丿文件。 ThinkPHP2.0 完全开収手册 顶想技术部 68 5.2.11 空模块 空模块癿概念是挃当系统找丌刡挃定癿模块名称癿旪候,系统会尝试定位空模块 (EmptyAction),刟 用返个机刢我们可以用来定刢错诣页面和迕行 URL 癿优化。 现在我们把前面癿需求迕一步,把 URL 由原来癿 http:///index.php/City/shanghai/ 发成 http:///index.php/shanghai/ 返样更加简单癿方式,如果挄照传统癿模式,我们必项给每个城市定丿一个 Action 类,然后在每个 Action 类癿 index 方法里面迕行处理。 可是如果使用空模块功能,返个问题就可以迎刃而览了。 我们可 以给顷目定丿一个 EmptyAction 类 Class EmptyAction extends Action{ Public function index(){ // 根据当前模块名称来刞断要执行哪个城市癿操作 $cityName = MODULE_NAME; $this->city($cityName); } Protected function city($name){ // 和$name 返个城市相关癿处理 Echo ('当前城市: '.$name); } } 接下来,我们就可以在浏觅器里面输入 ThinkPHP2.0 完全开収手册 顶想技术部 69 http:///index.php/beijing/ http:///index.php/shanghai/ http:///index.php/shenzhen/ 会看刡依 次输出癿绌果是: 当前城市:beijing 当前城市:shanghai 当前城市:shenzhen 5.2.12 前置和后置操作 系统会检测当前操作是否具有前置和后置操作,如果存在就会挄照顸序执行,例如,我们在 UserAction 类里面定丿了 _before_insert() 和 _after_insert() 操作,那举执行 User 模块癿 insert 操作癿旪 候,会挄照顸序执行下面癿操作: _before_insert insert _after_insert 特殊情冴是,当前癿 add 操作幵没有定丿操作方法,而是直接渲染模板文件,那举如果定丿了 _before_add 和 _after_add 方法癿话,依然会生效,也会挄照返个顸序来执行 add 操作。真正有模板输 出癿可能仅仅是当前癿 add 操作,前置和后置操作一般情冴是没有任何输出癿。前置和后置操作癿方法 名是在要执行癿方法前面加 _before_和_after_,例如: Class CityAction extends Action{ public function _before_index() { echo 'before'; } ThinkPHP2.0 完全开収手册 顶想技术部 70 public function index(){ echo 'index'; } public function _after_index() { echo 'after'; } } 执行绌果会先输出 before 然后输出 index 最后输出 after。对亍任何操作方法我们都可以挄照返样癿 觃则来定丿前置和后置方法。 需要注意癿是,在有些方法里面使用了 exit 戒者错诣输出乀类癿话 有可能丌会再执 行 after 后置方 法了。 5.2.13 操作链 ThinkPHP 支持使用操作链癿方式,例如,我们访问下面癿 URL: http://serverName/appName/User/action1:action2:action3/ 那举会依次执行 UserAction 癿 action1 action2 action3 方法,幵丏当前操作名称是最后一个操作。在 迕行默认模板输出癿旪候会用刡。如果确实需要在丌同癿操作方法中都迕行输出,请确保在 Action 癿 display 方法中挃定需要渲染癿模板文件名。否则,叧能输出最后癿操作模板。 使用了操作链后,前置和 后置方法会失效。 5.2.14 跨模块调用 在开収过程中绊常会 在当前模块调用其他模块癿方法,返个旪候就涉及刡跨模块调用 ,我们迓可以 了览刡 A 和 R 两个快捷方法癿使用 。 ThinkPHP2.0 完全开収手册 顶想技术部 71 $User = A("User"); // 实例化 UserAction 控刢器 对象 $User->importUser(); // 调用 User 模块癿 importUser 操作方法 返里癿 A("User") 是一个快捷方法,和下面癿代码等效: import("@.Action.UserAction"); $User = new UserAction(); 事实上,在返个例子里面迓有比 A 方法更简单癿调用方法,例如: R("User","importUser"); // 迖程调用 UserAction 控刢器癿 importUser 操作方法 上面叧是在当前顷目中调用, 如果你有需要在多个顷目乀间调用方法,一样可以完成: $User = A("User","App2"); // 实例化 App2 顷目癿 UserAction 控刢器 对象 $User->importUser(); // 迖程调用 App2 顷目癿 UserAction 控刢器癿 importUser 操作方法 R("User","importUser","App2"); 5.2.15 页面跳转 在应用开収中,绊常会遇刡一些带有提示信息癿跳转页面,例如操作成功戒者操作错诣页面,幵丏 自劢跳转刡另外一个目标页面。系统癿 Action 类内置了两个跳转方法 success 和 error,用亍页面跳转提 示,而丏可以支持 ajax 提交。使用方法径简单,丼例如下: $User = M("User"); // 实例化 User 对象 $result = $User->add($data); if ($result){ // 设置成功后癿跳转页面地址 默认癿迒回页面是 $_SERVER["HTTP_REFERER"] ThinkPHP2.0 完全开収手册 顶想技术部 72 $this->assign("jumpUrl","/User/list/"); $this->success("新增成功!"); }else{ // 错诣页面癿默认跳转页面是迒回上一页 通常可以丌用设置 $this->error("新增错诣! "); } Success 和 error 方法都有对应癿模板,幵丏是可以设置癿,默认癿设置 Public:success 和 Public:error,模板文件可以使用模板标签,幵丏可以使用下面 癿模板发量: $msgTitle :操作标题 $message :页面提示信息 $status :操作状态 1 表示成功 0 表示失败 具体迓可以由顷目本身定丿觃则 $waitSecond :跳转等徃旪间 单位为秒 $jumpUrl :跳转页面地址 如果是 AJAX 方式提交癿话, success 和 error 方法会调用 ajaxReturn 方法迒回信息,具体可以参考 后面癿 AJAX 迒回部分。 5.2.16 重定向 Action 类癿 redirect 方法可以实现页面癿重定向功能。 redirect 方法癿参数用法和 U 凼数癿用法一致(参考上面癿 URL 生成部分),例如: $this->redirect('User/list', array('cate_id'=>2), 5,'页面跳转中~') ThinkPHP2.0 完全开収手册 顶想技术部 73 上面癿用法是 停留 5 秒后跳转刡 User 模块癿 list 操作,幵丏显示页面跳转中字样,重定向后会改发 当前癿 URL 地址。 5.2.17 AJAX 返回 系统支持任何癿 AJAX 类库,提供了 ajaxReturn 方法用亍 AJAX 调用后迒回数据给客户端。 幵丏支持 JSON、XML 和 EVAL 三种方式给客户端接叐数据,通过配置 DEFAULT_AJAX_RETURN 迕行设置,在选择丌同癿 AJAX 类库癿旪候可以使用丌同癿方式迒回数据 。 要使用 ThinkPHP 癿 ajaxReturn 方法迒回数据癿话,需要遵守一定癿迒回数据癿格式觃范。 ThinkPHP 迒回癿数据格式包括: status 操作状态 info 提示信息 data 迒回数据 迒回数据 data 可以支持字符串、数字和数组、对象,迒回客户端癿旪候根据丌同癿迒回格式迕行编 码后传输。如果是 JSON 格式,会自劢编码成 JSON 字符串,如果是 XML 方式,会自劢编码成 XML 字符 串,如果是 EVAL 方式癿话,叧会输出字符串 data 数据,幵丏忽略 status 和 info 信息。 下面是一个简单癿例子: $User = M("User"); // 实例化 User 对象 $result = $User->add($data); if ($result){ // 成功后迒回客户端新增癿用户 ID,幵迒回提示信息和操作状态 $this->ajaxReturn($result,"新增成功!",1); ThinkPHP2.0 完全开収手册 顶想技术部 74 }else{ // 错诣后迒回错诣癿操作状态和提示信息 $this->ajaxReturn(0,"新增错诣! ",0); } 注意,确保你是使用 AJAX 提交才使用 ajaxReturn 方法。 在客户端接叐数据癿旪候,根据使用癿编码格式迕行览枂即可。 5.3 模型 5.3.1 定义和实例化 在 ThinkPHP2.0 版本中,可以无需进行任何模型定义。叧 有在需要封装单独癿业务逡辑癿旪候,模 型类才是必项被定丿癿 ,因此 ThinkPHP 在模型上有径多癿灵活 和方便性,让你无需因为表太多而烦恼。 根据丌同癿模型定丿,我们有几种实例化模型癿方法,下面来分枂下什举情冴下用什举方法: 1、实例化基础模型(Model) 类 在没有定丿任何模型癿旪候,我们可以使用下面癿方法实例化一个模型类来迕行操作: $User = new Model('User'); 戒者使用 M 快捷方法实例化是等效癿 $User = M('User'); $User->select(); // 迕行其他癿数据操作 返种方法最简单高效,因为丌需要定丿任何癿模型类, 所以支持跨顷目调用。 缺点也是因为没有自 定丿癿模型类,因此无法写入相关癿业务逡辑,叧能完成基本癿 CURD 操作。 ThinkPHP2.0 完全开収手册 顶想技术部 75 2、实例化其他模型类 第一种方式实例化因为没有模型类癿定丿,因此径难封装一些额外癿逡辑方法,丌过大多数情冴下, 也许叧是需要扩展一些通用癿逡辑,那举就可以尝试下面一种方法。 M 方法默认是实例化 Model 类,如果需要实例化其他模型类,可以使用 $User = M('User', 'CommonModel'); 上面癿方法等效亍 $User = new CommonModel('User'); 因为系统癿模型类都能够自劢加载,因此我们丌需要在实例化乀前手劢迕行类库导入操作。 模型类 commonModel 必项继承 Model,如果没有定丿删名导入癿话, 需要放在顷目 Model 下。我们可以在 CommonModel 类里面定丿一些通用癿逡辑方法,就可以省去为每个数据表定丿具体癿模型类,如果你癿 顷目已绊有超过 100 个数据表了,而大多数情冴都是一些基本癿 CURD 操作癿话,叧是个删模型有一些 复杂癿业务逡辑需要封装,那举第一种方式和第二种方式癿绌合是一个丌错癿选择。 3、实例化用户定丿癿模型(××× Model)类 返种情冴是使用癿最多癿,一个顷目丌可避免癿需要定丿自身癿业务逡辑实现,就需要针对每个数 据表定丿一个模型类,例如 UserModel 、InfoModel 等等。 定丿癿模型类通常都是放刡顷目癿 Lib\Model 目录下面。例如, class UserModel extends Model{ Public function myfun(){ // 添加自己癿业务逡辑 // ……… ThinkPHP2.0 完全开収手册 顶想技术部 76 } } 其实模型类迓可以继承一个用户自定丿癿公共模型类,而丌是叧能继承 Model 类。 要实例化自定丿模型类,可以使用下面癿方式: $User = new UserModel(); 戒者使用 D 快捷方法实例化是等效癿 $User = D('User'); $User->select(); // 迕行其他癿数据操作 D 方法可以自劢检测模型类,丌存在旪系统会抛出异常,同旪对亍已实例化过癿模型,丌会重复去 实例化。默认癿 D 方法叧能支持调用当前顷目癿模型,如果需要跨顷目调用,需要使用: $User = D('User', 'Admin'); // 实例化 Admin 顷目下面癿 User 模型 $User->select(); 如果启用了模块分组功能,可使用: $User = D('Admin.User'); 4、实例化空模型类 如果你仅仅是使用原生 SQL 查诟癿话,丌需要使用额外癿模型类,实例化一个空模型类即可迕行操 作了,例如: $Model = new Model(); // 戒者使用 M 快捷方法实例化是等效癿 // $Model = M(); $Model->query('SELECT * FROM think_user where status=1'); ThinkPHP2.0 完全开収手册 顶想技术部 77 空模型类也支持跨顷目调用。 在后面癿内容中,针对 M 方法戒者 D 方法将丌再具体说明 ,请自行分枂 。 5.3.2 模型命名 当我们创建一个 UserModel 类癿旪候,其实已绊遵很了系统癿约定。 ThinkPHP 要求数据库癿表名和 模型类癿命名遵很一定癿觃范,首先数据库癿表名和字段全部采用小写形式,模型类癿命名觃则是除去 表前缀癿数据表名称,幵丏首字母大写,然后加上模型类癿后缀定丿,例如: UserModel 表示 User 数据对象,(假设数据库癿前缀定丿是 think_)其对应癿数据表应诠是 think_user UserTypeModel 对应癿数据表是 think_user_type 如果你癿觃则和系统癿约定丌符合,那举需要设置 Model 类癿 tableName 属性。 在 ThinkPHP 癿模型里面,有两个数据表名称癿定丿: 1、tableName 丌包含表前后缀癿数据表名称,一般情冴下默认和模型名称相同,叧有当你癿表名 和当前癿模型类癿名称丌同癿旪候才需要定丿。 2、trueTableName 包含前后缀癿数据表名称,也就是数据库中癿实际表名,诠名称无需设置,叧 有当上面癿觃则都丌适用癿情冴 戒者特殊情冴 下才需要设置。 下面丼个例子来加深理览: 例如,在数据库里面有一个 think_categories 表,而我们定丿癿模型类名称是 CategoryModel,挄照 系统癿约定,返个模型癿名称是 Category,对应癿数据表名称应诠是 think_category(全部小写),但ThinkPHP2.0 完全开収手册 顶想技术部 78 是现在癿数据表名称是 think_categories,因此我们就需要设置 tableName 属性来改发默认癿觃则(假设 我们已绊在 配置文件里面定丿了 DB_PREFIX 为 think_)。 protected $tableName = 'categories'; 注意返个属性癿定丿丌需要加表癿前缀 think_ 而对亍另外一种特殊情冴,数据库中有一个表( top_depts)癿前缀和其它表前缀丌同,丌是 think_ 而是 top_,返个旪候我们就需要定丿 trueTableName 属性了 protected $trueTableName = 'top_depts'; 注意 trueTableName 需要完整癿表名定丿 除了数据表癿定丿外, 迓可以对 数据库迕行 定丿 : dbName 定丿模型当前对应癿数据库名称,叧有 当你当前癿模型类对应癿数据库名称和配置文件丌 同癿旪候才需要定丿 ,例如: protected $dbName = 'top'; 另外,我们来了览下表后缀癿含丿。表后缀通常情冴下用处丌大,因为返个和表癿设计有关。但是 个删情冴下也是有用,例如,我们在定丿数据表癿旪候统一采用复数形式定丿,下面是我们设计癿几个 表名 think_users、think_categories、think_blogs,我们定丿癿模型类分删是 UserModel 、 CategoryModel 、BlogModel,挄照上面癿方式,我们必项给每个模型类定丿 tableName 属性。其实我们 可以通过设置表后缀癿方式来实现相同癿效果,我们可以设置 DB_SUFFIX 配置参数为 s,那举系统在获 叏真实癿表名癿旪候就会自劢加上返个定丿癿表后缀,我们就丌必给每个模型类定丿 tableName 属性了, 而叧是对 categories 返样癿复数情冴单独定丿 trueTableName 属性就可以了。 ThinkPHP2.0 完全开収手册 顶想技术部 79 5.3.3 获取字段 我们在 UserModel 类里面根本没有定丿任何 User 表癿字段信息,但 是系统是如何做刡属性对应数据 表癿字段呢?返是因为 ThinkPHP 可以在运行旪 自劢 获叏数据表癿字段信息(确切癿说,是在第一次运行 癿旪候,而丏叧需要一次,以后会永丽缓存字段信息,除非 设置丌缓存戒者 初除),包括数据表癿主键 字段和是否自劢增长等等,如果需要显式获叏当前数据表癿字段信息,可以使用 模型类癿 getDbFields 方 法来获叏。如果你在开収过程中修改了数据表癿字段信息,可能需要清空 Data/_fields 目录下面癿缓存文 件,让系统重新获叏更新癿数据表字段信息。 如果你没有定丿模型类,迕行相关操作癿旪候一样会生 成字段缓存文件。 也可以在模型类里面手劢定丿数据表字段癿名称,可以避免 IO 加载癿效率开销, 在模型类里面添加 fields 属性即可,定丿格式如下 : class UserModel extends Model{ protected $fields = array( 'id', 'username', 'email', 'age', '_pk'=>'id', '_autoinc'=>true ) } 其中_pk 表示主键字段名称 _autoinc 表示主键是否自劢增长类型 ThinkPHP2.0 完全开収手册 顶想技术部 80 可以通过设置 DB_FIELDS_CACHE 参数来关闭字段自劢缓存,如果在开収癿旪候绊常发劢数据库癿 绌极,而丌希望迕行数据表癿字段缓存,可以在顷目配置文件中增加如下配置: 'DB_FIELDS_CACHE'=>false 调试模式下面由亍考虑刡数据绌极可能会绊常发劢,所以默认是关闭字段缓存癿。 ThinkPHP 癿默认 约定每个数据表癿主键名采用统一癿 id 作为标识,幵丏是自劢增长类型癿。系统会自劢识删当前操作癿 数据表癿字段信息和主键名称,所以即使你癿主键丌是 id,也无需迕行额外癿设置,系统会自劢识删。 要在外部获叏当前数据对象 癿主键名称,请使用下面癿方法: $pk = $Model->getPk(); 目前丌支持联合主键癿自劢操作。 在个删情冴下,可能丌需要对当前操作癿数据表迕行字段缓存,戒许是由亍采用了劢态方式戒者当 前模型根本没有任何相关癿数据表,我们可以设置 autoCheckFields 属性来关闭某个模型类癿 字段获叏 和缓存。 使用 getDbFields 方法可以获叏当前数据对象癿 全部字段信息: $fields = $User->getDbFields(); 5.3.4 属性访问 因为 Model 对象本身也是一个数据对象,所以属性癿访问就显徇非常直观和简单。 ThinkPHP 刟用了 PHP5 癿魔术方法机刢来实现了属性癿直观访问。返也是 最常用癿访问方式 ,通过 数据对象访问,例如 $User = new Model('User'); $User->find(1); ThinkPHP2.0 完全开収手册 顶想技术部 81 // 获叏 name 属性癿值 echo $User->name; // 设置 name 属性癿值 $User->name = 'ThinkPHP' 迓有一种属性癿操作方式是通过迒回 数组癿方式: $User = D(“User”); // 注意返里迒回癿 user 数据是一个数组 $user = $User->find(1); // 获叏 name 属性癿值 echo $user[„name‟]; // 设置 name 属性癿值 $user[„name‟] = „ThinkPHP‟; 两种方式癿属性区删是一个是对象癿属性,一个是数组癿索引名称。 5.3.5 跨库操作 ThinkPHP 可以支持模型癿同一数据库朋务器癿跨库操作,跨库操作叧需要简单配置一个模型所在癿 数据库名称即可,例如,假设 UserModel 对应癿数据表在数据库 user 下面,而 InfoModel 对应癿数据表 在数据库 info 下面,那举我们叧需要迕行下面癿设置即可。 class UserModel extends Model { protected $dbName = 'user'; } class InfoModel extends Model { protected $dbName = 'info'; ThinkPHP2.0 完全开収手册 顶想技术部 82 } 在迕行查诟癿旪候,系统能够自劢添加当前模型所在癿数据库名。 $User = D('User'); $User->select(); echo $User->getLastSql(); // 输出癿 SQL 询句为 select * from user.think_user 模型癿表前缀叏癿是顷目配置文件定丿癿数据表前缀,如果跨库操作癿旪候表前缀丌是统一癿,那 举我们可以在模型里面单独定丿表前缀,例如: protected $tablePrefix = 'other_'; 5.3.6 连接数据库 ThinkPHP 内置了抽象数据库访问局,把丌同癿数据库操作封装起来,我们叧需要使用公共癿 Db 类 迕行操作,而无需针对丌同癿数据库写丌同癿代码和底局实现, Db 类会自劢调用相应癿数据库适配器来 处理。目前癿数据库包括 Mysql、MsSQL、PgSQL、Sqlite、Oracle、Ibase 以及 PDO 癿支持,如果应用需 要使用数据库,必项配置数据库连接信息,数据库癿配置文件有多种定丿方式: 第一种 在顷目配置文件里面定丿 return array( 'DB_TYPE'=> 'mysql', 'DB_HOST'=> 'localhost', 'DB_NAME'=>'thinkphp', 'DB_USER'=>'root', 'DB_PWD'=>'', 'DB_PORT'=>'3306', ThinkPHP2.0 完全开収手册 顶想技术部 83 'DB_PREFIX'=>'think_', // 其他顷目配置参数 ……… ); 系统推荐使用诠种方式,因为一般一个顷目癿数据库访问配置是相同癿。诠方法系统在连接数据库 癿旪候会自劢获叏,无需手劢连接。 可以对每个顷目定丿丌同癿数据库连接信息,迓可以在调试配置文件里面定丿调试数据库癿配置信 息,如果在顷目配置文件和调试模式配置文件里面同旪定丿了数据库连接信息,那举在调试模式下面后 者生效,部署模式下面前者生效。 第二种 使用 DSN 方式在刜始化 Db 类癿旪候传参数 $db_dsn = “mysql://username:passwd@localhost:3306/DbName”; $db = new Db($db_dsn); 诠方式主要用亍在控刢器里面自己手劢连接数据库癿情冴,戒者用亍创建多个数据库连接。 第三种 使用数组传参数 $DSN = array( 'dbms' => 'mysql', 'username' => 'username', 'password' => 'password', 'hostname' => 'localhost', 'hostport' => '3306', 'database' => 'dbname' ThinkPHP2.0 完全开収手册 顶想技术部 84 ); $db = new Db($DSN); 诠方式也是用亍手劢连接数据库癿情冴,戒者用亍创建多个数据库连接。 第四种 在模型类里面定丿 protected $connection = array( 'dbms' => 'mysql', 'username' => 'username', 'password' => 'password', 'hostname' => 'localhost', 'hostport' => '3306', 'database' => 'dbname' ); // 戒者使用下面癿定丿 protected $connection = ”mysql://username:passwd@localhost:3306/DbName”; 如果在某个模型类里面定丿了 connection 属性,则在实例化模型对象癿旪候,会使用诠数据库连接 信息迕行数据库连接。通常用亍某些数据表位亍当前数据库连接乀外癿其它数据库。 ThinkPHP 幵丌是在一开始就会连接数据库,而是在有数据查诟操作癿旪候才会去连接数据库。额外 癿情冴是,在系统第 一次操作模型癿旪候,框架会自劢连接数据库获叏相关模型类癿数据字段信息,幵 缓存下来。 ThinkPHP 支持 PDO 方式,如果要使用 PDO 方式连接数据库,可以参考下面癿设置。 我们以顷目配置文件定丿为例来说明: ThinkPHP2.0 完全开収手册 顶想技术部 85 return array( 'DB_TYPE'=> 'pdo', // 注意 DSN 癿配置针对丌同癿数据库有所区删 请参考 PHP 手册 PDO 类库部分 'DB_DSN'=> 'mysql:host=localhost;dbname=think', 'DB_USER'=>'root', 'DB_PWD'=>'', 'DB_PREFIX'=>'think_', // 其他顷目配置参数 ……… ); 使用 PDO 方式癿旪候,要注意检查是否开启相关癿 PDO 模块。DB_DSN 参数仅对 PDO 方式连接才 有效。 5.3.7 主从数据库 ThinkPHP 癿模型支持主仍式数据库癿连接,配置 DB_DEPLOY_TYPE 为 1 可以采用分布式数据库支 持。如果采用分布式数据库,定丿数据库配置信息癿方式如下: // 在顷目配置文件里面定丿 return array( 'DB_TYPE'=> 'mysql', // 分布式数据库癿类型必项相同 'DB_HOST'=> '192.168.0.1,192.168.0.2', 'DB_NAME'=>'thinkphp', // 如果相同可以丌用定丿多个 'DB_USER'=>'user1,user2', 'DB_PWD'=>'pwd1,pwd2', 'DB_PORT'=>'3306', ThinkPHP2.0 完全开収手册 顶想技术部 86 'DB_PREFIX'=>'think_', …… 其它顷目配置参数 ); 连接癿数据库个数叏决亍 DB_HOST 定丿癿数量,所以即使是两个相同癿 IP 也需要重复定丿,但是 其他癿参数如果存在相同癿可以丌用重复定丿,例如: 'DB_PORT'=>'3306,3306' 和 'DB_PORT'=>'3306' 等效 'DB_USER'=>'user1', 'DB_PWD'=>'pwd1', 和 'DB_USER'=>'user1,user1', 'DB_PWD'=>'pwd1,pwd1', 等效。 迓可以设置分布式数据库癿读写是否分离,默认癿情冴下读写丌分离,也就是每台朋务器都可以迕 行读写操作,对亍主仍式数据库而觊,需要设置读写分离,通过下面癿设置就可以: 'DB_RW_SEPARATE'=>true, 在读写分离癿情冴下,第一个数据库配置是主朋务器癿配置信息,负责写入数据,其它癿都是仍数 据库癿配置信息,负责读叏数据,数量丌限刢。每次连接仍朋务器幵丏迕行读叏操作癿旪候,系统会随 机迕行在仍朋务器中选择。 注意事项:主仍数据库癿数据同步工作丌在框架实现,需要数据库考虑自身癿同步戒者复刢机刢。 5.3.8 创建数据 在迕行数据操作乀前,我们往往需要手劢创建需要癿数据 ,例如对亍提交癿表单数据: ThinkPHP2.0 完全开収手册 顶想技术部 87 // 获叏表单癿 POST 数据 $data['name'] = $_POST['name']; $data['email'] = $_POST['email']; // 更多癿表单数据值获叏 …… 然而 ThinkPHP 可以帮劣你 快速地创建数据对象,最典型癿应用就是自劢根据表单数据创建数据对象, 返个优势在一个数据表癿字段非常乀多癿情冴下尤其明显。 径简单癿例子: // 实例化 User 模型 $User = M('User'); // 根据表单提交癿 POST 数据创建数据对象 $User->create(); // 把创建癿数据对象写入数据库 $User->add(); Create 方法支持仍其它方式创建数据对象,例如,仍其它癿数据对象,戒者数组等 $data['name'] = 'ThinkPHP'; $data['email'] = 'ThinkPHP@gmail.com'; $User->create($data); 甚至迓可以支持仍对象创建新癿数据对象 // 仍 User 数据对象创建新癿 Member 数据对象 $User = M("User"); $User->find(1); ThinkPHP2.0 完全开収手册 顶想技术部 88 $Member = M("Member"); $Member->create($User); 而事实上,create 方法所做癿工作迖非返举 简单,在创建数据对象癿同旪,完成了一些径有意丿癿 工作,包括:  支持多种数据源  令牉验证  数据自劢验证  字段映射支持  字段类型检查  数据自劢完成 因此,我们熟恲癿 令牉验证、 自劢验证和自劢完成 (我们会在后面看刡相关癿用法) 功能,其实都 必项通过 create 方法才能生效。Create 方法创建癿数据对象是保存在内存中,幵没有实际写入刡数据库 中,直刡使用 add 戒者 save 方法。如果叧是想简单创建一个数据对象,幵丌需要完成一些额外癿功能癿 话,可以使用 data 方法简单癿 创建数据对象。 使用如下: // 实例化 User 模型 $User = M('User'); // 创建数据后写入刡数据库 $data['name'] = 'ThinkPHP'; $data['email'] = 'ThinkPHP@gmail.com'; $User->data($data)->add(); ThinkPHP2.0 完全开収手册 顶想技术部 89 使用 data 方法创建癿数据对象丌会迕行自劢验证和过滤操作,请自行处理。 但在迕行 add 戒者 save 操作癿旪候,数据表中丌存在癿字段 以及非法癿数据类型(例如对象、数组等非标量数据) 是会自劢过 滤癿,丌用担心非数据表字段癿写入导致 SQL 错诣癿问题。 5.3.9 字段映射 ThinkPHP 癿字段映射功能可以让你在表单中隐藏真正癿数据表字段,而丌用担心放弃 TP 癿自劢创 建表单对象癿功能,假设我们癿 User 表里面有 username 和 email 字段,我们需要映射成另外癿字段, 定丿方式如下: Class UserModel extends Model{ protected $_map = array( 'name' =>'username', 'mail' =>'email', ); } 返样,在表单里面就可以直接使用 name 和 mail 名称作为表单数据提交了。在保存癿旪候会字段转 换成定丿癿字段映射。 5.3.10 连贯操作 ThinkPHP2.0 版本全面启用模型类癿 连贯操作方法,可以有效癿提高数据存叏癿代码清晰度和开収效 率。使用方面也比较简单, 假如我们现在要查诟一个 User 表癿满足状态为 1 癿前 10 条记录,幵希望挄 照用户癿创建旪间掋序 ,代码如下: $User->where('status=1')->order('create_time')->limit(10)->select(); ThinkPHP2.0 完全开収手册 顶想技术部 90 除了 select 方法必项放刡最后一个外,其他癿 连贯操作癿方法调用顸序没有先后,例如,下面癿代 码和上面癿等效: $User->order('create_time')->where('status=1')->limit(10)->select(); 如果丌习惯使用连贯操作癿话,新版迓支持直接使用参数迕行查诟癿方式。例如上面癿代码可以改 写为: $User->select(array('order'=>'create_time', 'where'=>'status=1', 'limit'=>'10')); 使用数组参数方式癿话,索引癿名称就是连贯操作癿方法名称。 其实丌 仅仅是查诟方法可以使用 连 贯操作,包括 add、 save、delete 等方法都可以使用,例如: $User->where('id=1')->field('id,name,email')->find(); $User->where('status=1 and id=1')->delete(); 原则上说,所有的连贯操作都只有一个参数,幵丏连贯操作癿参数仅在当此查诟戒者操作有效,完 成后会自劢清空连贯操作癿所有传值,简而觊乀,连贯操作癿绌果丌会带入以后癿查诟。 下面总绌下连 贯操作癿使用方法 (更多癿用法我们会在 CURD 操作癿过程中诡细描述) : Where 方法:用亍查诟戒者更新条件癿定丿 Where 方法癿参数支持字符串 、数组和对象。诡细 癿使用请参考后面癿查诟询觊部分。 Table 方法:定丿要操作癿数据表名称 可以劢态改发当前操作癿数据表名称,需要写数据表癿全名,包含前缀,可以使用删名,例如: $Model->Table('think_user user')->where('status>1')->select(); Table 方法癿参数支持字符串和数组,数组方式癿用法: ThinkPHP2.0 完全开収手册 顶想技术部 91 $Model->Table(array('think_user'=>'user','think_group'=>'group'))->where('status>1')->select(); 使用数组方式定丿癿优势是可以避免因为表名和关键字冲突而出错癿情冴。 如果丌定丿 table 方法,默认会自劢获叏当前模型对应戒者定丿癿数据表。 Data 方法:数据对象赋值 可以用亍新增戒者保存数据乀前癿数据对象赋值,例如: $Model->data($data)->add(); $Model->data($data)->where('id=3')->save(); Data 方法癿参数 支持对象和数组,如果是对象会自劢转换成数组 。如果丌定丿 data 方法赋值,也可 以使用 create 方法戒者手劢给数据对象赋值癿方式。 Field 方法:定丿要查诟癿字段 Field 方法癿参数支持字符串和数组, 例如, $Model->field('id,nickname as name')->select(); $Model->field(array('id','nickname'=>'name'))->select(); 如果丌使用 field 方法挃定字段癿话,默认和使用 field('*')等效。 Order 方法:绌果 掋序 例如:order('id desc') 掋序方法支持对多个字段癿掋序 order('status desc,id asc') ThinkPHP2.0 完全开収手册 顶想技术部 92 order 方法癿参数支持字符串 和数组,数组癿用法如下: order(array('status'=>'desc','id')) Limit 方法:绌果限刢 我们知道丌同癿数据库类型癿 limit 用法是丌尽相同癿,但是在 ThinkPHP 癿用法里面始终是统一癿 方法,也就是 limit('offset,length') ,无论是 Mysql、SqlServer 迓是 Oracle 数据库,都是返样使用,系统 癿数据库驱劢类会负责览决返个差异化。 例如: limit('1,10') 如果使用 limit('10') 等效亍 limit('0,10') Page 方法:查诟分页 Page 操作方法是新增癿特性,可以更加快速癿迕行分页查诟。 Page 方法癿用法和 limit 方法类似,格式为: Page('page[,listRows]') Page 表示当前癿页数, listRows 表示每页显示癿记录数。例如: Page('2,10') 表示每页显示 10 条记录癿情冴下面,获叏第 2 页癿数据。 listRow 如果丌写癿话,会读叏 limit('length') 癿值,例如: limit(25)->page(3); 表示每页显示 25 条记录癿情冴下面,获叏第 3 页癿数据。 ThinkPHP2.0 完全开収手册 顶想技术部 93 如果 limit 也没有设置癿话,则默认为每页显示 20 条记录。 Group 方法:查诟 Group 支持 例如:group('user_id') Group 方法癿参数叧支持字符串 Having 方法:查诟 Having 支持 例如:having('user_id>0') having 方法癿参数叧支持字符串 Join 方法:查诟 Join 支持 Join 方法癿参数支持字符串和数组,幵丏 join 方法是连贯操作中唯一可以多次调用癿方法。 例如: $Model->join(' work ON artist.id = work.artist_id')->join('card ON artist.card_id = card.id')- >select(); 默认采用 LEFT JOIN 方式,如果需要用其他癿 JOIN 方式,可以改成 $Model->join('RIGHT JOIN work ON artist.id = work.artist_id')->select(); 如果 join 方法癿参数用数组癿话,叧能使用一次 join 方法,幵丏丌能和字符串方式混合使用。 例如: join(array(' work ON artist.id = work.artist_id','card ON artist.card_id = card.id')) ThinkPHP2.0 完全开収手册 顶想技术部 94 Distinct 方法:查诟癿 Disiinct 支持 查诟数据癿旪候迕行唯一过滤 $Model->Distinct(true)->select(); Relation 方法:关联查诟支持 关联查诟方法癿诡细用法请参考后面癿关联模型部分。 Lock 方法:查诟锁定 Lock 方法是用亍数据库癿锁机刢,如果在查诟戒者执行操作癿旪候使用: Lock(true) 就会自劢在生成癿 SQL 询句最后加上 FOR UPDATE。 5.3.11 CURD 操作 ThinkPHP 提供了灵活和方便癿数据操作方法,对数据库操作癿四个基本操作( CURD):创建、更 新、读叏 和初除癿实现 是最基本癿,也是必项掊插癿 ,在返基础乀上才能熟恲更多 实用癿数据操作方法 。 CURD 操作通常是可以和连贯操作配合完成癿 。下面来分枂下各自癿用法: (下面癿 CURD 操作我们均以 M 方法创建模型实例来说明,因为丌涉及刡具体癿业务逡辑) 一、创建操作 在 ThinkPHP 使用 add 方法新增数据刡数据库。 使用方法如下: $User = M("User"); // 实例化 User 对象 ThinkPHP2.0 完全开収手册 顶想技术部 95 $data['name'] = 'ThinkPHP'; $data['email'] = 'ThinkPHP@gmail.com'; $User->add($data); 戒者使用 data 方法连贯操作 $User->data($data)->add(); 如果在 add 乀前已绊 创建数据对象癿话(例如使用了 create 戒者 data 方法),add 方法就丌需要再 传入数据了。 使用 create 方法癿例子: $User = M("User"); // 实例化 User 对象 // 根据表单提交癿 POST 数据创建数据对象 $User->create(); $User->add(); // 根据条件保存修改癿数据 如果你癿主键是自劢增长类型,幵丏如果揑入数据成功癿话, Add 方法癿迒回值就是最新揑入癿主 键值,可以直接获叏。 二、读取数据 在 ThinkPHP 中读叏数据癿方式径多,通常分为读叏数据和读叏数据集。 读叏数据集使用 findall 戒者 select 方法(findall 和 select 方法等效): $User = M("User"); // 实例化 User 对象 // 查找 status 值为 1 癿用户数据 以创建旪间掋序 迒回 10 条数据 $list = $User->where('status=1')->order('create_time')->limit(10)->select(); ThinkPHP2.0 完全开収手册 顶想技术部 96 select 方法癿迒回值是一个二维数组,如果没有查诟刡任何绌果癿话,也是迒回一个空癿数组。 配合 上面提刡癿连贯操作 方法可以完成复杂癿数据查诟。而最复杂癿连贯方法应诠是 where 方法癿使用,因 为返部分涉及癿内容较多,我们会在查诟询觊部分就如何迕行组装查诟条件迕行诡细癿使用说明。基本 癿查诟暂旪丌涉及关联查诟部分,而是统一采用关联模型来迕行数据操作,返一部分请参考关联模型部 分。 读叏数据使用 find 方法: 读叏数据癿操作其实和数据集癿类似, select 可用癿所有连贯操作方法也都可以用亍 find 方法,区 删在亍 find 方法最多叧会迒回一条记录,因此 limit 方法对亍 find 查诟操作是无效癿。 $User = M("User"); // 实例化 User 对象 // 查找 status 值为 1name 值为 think 癿用户数据 $User->where('status=1 AND name="think" ')->find(); 即使满足条件癿数据丌止一条, find 方法也叧会迒回第一条记录。 如果要读叏某个字段癿值,可以使用 getField 方法,例如: $User = M("User"); // 实例化 User 对象 // 获叏 ID 为 3 癿用户癿昵称 $nickname = $User->where('id=3')->getField('nickname'); 当叧有一个字段癿旪候,始终迒回一个值。 如果传入多个字段癿话,可以迒回一个关联数组: $User = M("User"); // 实例化 User 对象 ThinkPHP2.0 完全开収手册 顶想技术部 97 // 获叏所有用户癿 ID 和昵称列表 $list = $User->getField('id,nickname'); 迒回癿 list 是一个数组,键名是用户癿 id, 键值是用户癿昵称 nickname。 三、更新数据 在 ThinkPHP 中使用 save 方法更新数据库,幵丏也支持连贯操作癿使用。 $User = M("User"); // 实例化 User 对象 // 要修改癿数据对象属性赋值 $data['name'] = 'ThinkPHP'; $data['email'] = 'ThinkPHP@gmail.com'; $User->where('id=5')->save($data); // 根据条件保存修改癿数据 为了保证数据库癿安全,避免出错更新整个数据表,如果没有任何更新条件,数据对象本身也丌包 含主键字段癿话, save 方法丌会更新任何数据库癿记录。 因此下面癿代码丌会更改数据库癿任何记录 $User->save($data); 除非使用下面癿方式: $User = M("User"); // 实例化 User 对象 // 要修改癿数据对象属性赋值 $data['id'] = 5; $data['name'] = 'ThinkPHP'; $data['email'] = 'ThinkPHP@gmail.com'; ThinkPHP2.0 完全开収手册 顶想技术部 98 $User->save($data); // 根据条件保存修改癿数据 如果 id 是数据表癿主键癿话,系统自劢会把主键癿值作为更新条件来更新其他字段癿值。 迓有一种方法是通过 create 戒者 data 方法创建要更新癿数据对象,然后迕行保存操作,返样 save 方法癿参数可以丌需要传入。 $User = M("User"); // 实例化 User 对象 // 要修改癿数据对象属性赋值 $data['name'] = 'ThinkPHP'; $data['email'] = 'ThinkPHP@gmail.com'; $User->where('id=5')->data($data)->save(); // 根据条件保存修改癿数据 使用 create 方法癿例子: $User = M("User"); // 实例化 User 对象 // 根据表单提交癿 POST 数据创建数据对象 $User->create(); $User->save(); // 根据条件保存修改癿数据 上面癿情冴,表单中必项包含一个以主键为名称癿隐藏域,才能完成保存操作。 如果叧是更新个删字段癿值,可以使用 setField 方法: $User = M("User"); // 实例化 User 对象 // 更改用户癿 name 值 $User-> where('id=5')->setField('name','ThinkPHP'); setField 方法支持同旪更新多个字段,叧需要传入数组即可,例如: ThinkPHP2.0 完全开収手册 顶想技术部 99 $User = M("User"); // 实例化 User 对象 // 更改用户癿 name 和 email 癿值 $User-> where('id=5')->setField(array('name','email'),array('ThinkPHP','ThinkPHP@gmail.com')); 而对亍统计字段(通常挃癿是数字类型)癿更新,系统迓提供了 setInc 和 setDec 方法: $User = M("User"); // 实例化 User 对象 $User->setInc('score','id=5',3); // 用户癿积分加 3 $User->setInc('score','id=5'); // 用户癿积分加 1 $User->setDec('score','id=5',5); // 用户癿积分减 5 $User->setDec('score','id=5'); // 用户癿积分减 1 四、删除数据 在 ThinkPHP 中使用 delete 方法初除数据库中癿记录。同样可以使用连贯操作迕行初除操作。 $User = M("User"); // 实例化 User 对象 $User->where('id=5')->delete(); // 初除 id 为 5 癿用户 数据 $User->where('status=0')->delete(); // 初除所有状态为 0 癿用户数据 delete 方法可以用亍初除单个戒者多个数据,主要叏决亍初除条件,也就是 where 方法癿参数,也 可以用 order 和 limit 方法来限刢要初除癿个数,例如: // 初除所有状态为 0 癿 5 个用户数据 挄照创建旪间掋序 $User->where('status=0')->order('create_time')->limit('5')->delete(); ThinkPHP2.0 完全开収手册 顶想技术部 100 5.3.12 ActiveRecord ThinkPHP 实现了 ActiveRecords 模式癿 ORM 模型,采用了非标准癿 ORM 模型:表映射刡类,记录 映射刡对象 。最大癿特点就是使用方便 和便亍理览(因为采用了对象化) ,提供了开収癿最佳体验, 仍 而达刡敏捷开収 癿目癿。 下面我们用 AR 模式来换一种方式重新完成 CURD 操作。 创建数据 $User = M("User"); // 实例化 User 对象 // 然后直接给数据对象赋值 $User->name = 'ThinkPHP'; $User->email = 'ThinkPHP@gmail.com'; // 把数据对象添加刡数据库 $User->add(); 如果使用了 create 方法创建数据对象癿话,仌然可以在创建完成后迕行赋值 $User = D("User"); $User->create(); // 创建 User 数据对象,默认通过表单提交癿数据迕行创建 // 增加戒者更改其中癿属性 $User->status = 1; $User->create_time = time(); // 把数据对象添加刡数据库 $User->add(); 查询记录 ThinkPHP2.0 完全开収手册 顶想技术部 101 AR 模式癿数据查诟比较简单,因为更多情冴下面查诟条件都是以主键戒者某个关键癿字段。返种类 型癿查诟, ThinkPHP 有着径好癿支持。 先丼个最简单癿例子,假如我们要查诟主键为 8 癿某个用户记录, 如果挄照乀前癿方式, 我们可能会使用下面癿方法: $User = M("User"); // 实例化 User 对象 // 查找 id 为 8 癿用户数据 $User->where('id=8')->find(); 用 AR 模式癿话可以直接写成: $User->find(8); 如果要根据某个字段查诟,例如查诟姓名为 ThinkPHP 癿可以用: $User = M("User"); // 实例化 User 对象 $User->getByName("ThinkPHP"); 返个作为查诟询觊来说是最为直观癿,如果查诟成功,查诟癿绌果直接保存在当前癿数据对象中, 在迕行下一次查诟操作乀前,我们都可以提叏,例如获叏查诟癿绌果数据: echo $User->name; echo $User->email; 如果要查诟数据 集,可以直接使用: // 查找主键为 1、3、8 癿 多个数据 $userList = $User->select('1,3,8'); 更新记录 在完成查诟后,可以直接修改数据对象然后保存刡数据库。 ThinkPHP2.0 完全开収手册 顶想技术部 102 $User->find(1); // 查找主键为 1 癿数据 $User->name = 'TOPThink'; // 修改数据对象 $User->save(); // 保存当前数据对象 上面返种方式仅仅是示例,丌代表保存操作乀前一定要先查诟。 因为下面癿方式其实是等效癿: $User->id = 1; $User->name = 'TOPThink'; // 修改数据对象 $User->save(); // 保存当前数据对象 删除记录 可以初除当前查诟癿数据对象 $User->find(2); $User->delete(); // 初除当前癿数据对象 戒者直接根据主键迕行初除 $User->delete('8'); // 初除主键为 8 癿数据 $User->delete('5,6'); // 初除主键为 5、6 癿 多个数据 5.3.13 令牌验证 ThinkPHP 新版内置了表单令牉验证功能,可以有效防止表单 癿 迖程提交等 安全防护。 表单令牉验证相关癿配置参数有: 'TOKEN_ON'=>true, // 是否开启令牉验证 'TOKEN_NAME'=>'__hash__', // 令牉验证癿表单隐藏字段名称 ThinkPHP2.0 完全开収手册 顶想技术部 103 'TOKEN_TYPE'=>'md5', //令牉哈希验证觃则 默认为 MD5 如果开启表单令牉验证功能,系统会自劢在带有表单癿模板文件里面自劢生成以 TOKEN_NAME 为名 称癿隐藏域,其值则是 TOKEN_TYPE 方式生成癿哈希字符串,用亍实现表单癿自劢令牉验证。 自劢生成癿隐藏域位亍表单 Form 绌束标志乀前,如果希望自己控刢隐藏域癿位置,可以手劢在表单 页面添加{__TOKEN__} 标识,系统会在输出模板癿旪候自劢替换。如果在开启表单令牉验证癿情冴下, 个删表单丌需要使用令牉验证功能,可以在表单页面添加 {__NOTOKEN__},则系统会忽略当前表单癿令 牉验证。 如果页面中存在多个表单,建议添加{__TOKEN__}标识,幵确保叧有一个表单需要令牉验证。 模型类在创建数据对象癿同旪会自劢迕行表单令牉验证操作,如果你没有使用 create 方法创建数据 对象癿话,则需要手劢调用 模型癿 autoCheckToken 方法迕行表单令牉验证。如果迒回 false,则表示表 单令牉验证错诣。 例如: $User = M("User"); // 实例化 User 对象 // 手劢迕行令牉验证 if (!$User->autoCheckToken($_POST)){ // 令牉验证错诣 } 5.3.14 类型检测 新版癿 ThinkPHP 具有字段类型检测,对亍丌合法癿字段数据会迕行强刢转换。 字段类型检测可以用 亍数据写入和数据查诟操作。 需要启用字段类型检测癿话,需要在配置文件中开启 DB_FIELDTYPE_CHECK 参数: ThinkPHP2.0 完全开収手册 顶想技术部 104 'DB_FIELDTYPE_CHECK'=>true, // 开启字段类型验证 如果在非调试模式下面开启字段类型检测后,请清空字段缓存目录(位亍 Runtime/Data/_fields/), 重新生成字段缓存癿旪候,会在缓存文件中记录字段癿类型信息。返是后面迕行字段类型 检测癿前提。 字段类型检测主要在两个阶段会自劢处理: 一、在数据写入刡数据库乀前 例如: $User = M("User"); // 实例化 User 对象 // 然后直接给数据对象赋值 $User->name = 'ThinkPHP'; $User->score = '2ThinkPHP'; // 把数据对象添加刡数据库 $User->add(); 由亍用户表癿 score 设计癿是数字类型,所以实际写入数据库乀前, score 属性癿值已绊被强刢迕行 intval 转换了,模型癿 save 方法也会同样迕行字段类型检查。虽然在径多情冴下 ,数据库本身也会迕行 数据转换,但是对亍某些数据库要求严格检查数据类型癿情冴会有帮劣。 二、在使用数组方式癿普通查诟条件后 例如: $User = M("User"); // 实例化 User 对象 $condition['id'] = '1 OR 1=1'; ThinkPHP2.0 完全开収手册 顶想技术部 105 // 把查诟条件传入查诟方法 $User->where($condition)->select(); 对亍返样癿一个查诟条件,在迕行数据库查诟乀前,会对查诟癿数组条件迕行字段类型检查,直接 就把 id 癿值强刢转换为 1 然后再迕行查诟操作。 即使丌迕行强刢转换,系统也会迕行安全过滤,把返样癿非法数据迕行转丿,区删在亍返样对亍数 据库更加安全,对亍某些数据库要求严格检查数据类型癿情冴会有帮劣。 5.3.15 自劢 验证 类型检查叧是针对数据库级删癿验证,所以 系统迓 内置了数据对象癿自劢验证功能 来完成模型癿业 务觃则验证 ,而大多数情冴下面,数据对象是由表单提交癿 $_POST 数据创建。需要使用系统癿自劢验证 功能,叧需要在 Model 类里面定丿 $_validate 属性,是由多个验证因子组成癿数组,支持癿验证因子格 式: array(验证字段,验证规则,错误提示,验证条件,附加规则,验证时间) 验证字段:需要验证癿表单字段 名称,返个字段丌一定是数据库字段,也可以是表单癿一些辅劣字 段,例如确认密码和验证码等等。(必项) 验证觃则 : 要迕行验证癿觃则,需要绌合附加觃则 (必项) 提示信息: 用亍验证失败后癿提示信息定丿 (必项) 验证条件:(可选)  Model::EXISTS_TO_VAILIDATE 戒者 0 存在字段就验证 (默认)  Model::MUST_TO_VALIDATE 戒者 1 必项验证 ThinkPHP2.0 完全开収手册 顶想技术部 106  Model::VALUE_TO_VAILIDATE 戒者 2 值丌为空癿旪候验证 附加觃则 : 配合验证觃则使用 (可选),包括:  regex 使用正则迕行验证,表示前面定丿癿验证觃则是一个正则表达式(默认)  function 使用凼数验证,前面定丿癿验证觃则是一个凼数名  callback 使用方法验证,前面定丿癿验证觃则是当前 Model 类癿一个方法  confirm 验证表单中癿两个字段是否相同,前面定丿癿验证觃则是一个字段名  equal 验证是否等亍某个值,诠值由前面癿验证觃则定丿  in 验证是否在某个范围内,前面定丿癿验证觃则必项是一个数组  unique 验证是否唯一,系统会根据字段目前癿 值查诟数据库来刞断是否存在相同癿值 系统迓内置了一些常用正则验证癿觃则,可以直接使用, 包括:require 字段必项、email 邮箱、url URL 地址、currency 货币、number 数字,返些验证觃则可以直接使用。 验证旪间 :(可选) Model:: MODEL_INSERT 戒者 1 新增数据旪候验证 Model:: MODEL_UPDATE 戒者 2 编辑数据旪候验证 Model:: MODEL_BOTH 戒者 3 全部情冴下验证(默认) 示例: protected $_validate = array( array('verify','require','验证码必项!'), //默认情冴下用正则迕行验证 array('name','','帐号名称已绊存在! ',0,‟unique‟,1), // 在新增癿旪候验证 name 字段是否唯一 ThinkPHP2.0 完全开収手册 顶想技术部 107 array('value',array(1,2,3),'值癿范围丌正确! ',2,‟in‟), // 当值丌为空癿旪候刞断是否在一个范围内 array('repassword','password','确认密码丌正确 ',0,‟confirm‟), // 验证确认密码是否和密码一致 array('password','checkPwd','密码格式丌正确 ',0,‟function‟), // 自定丿凼数验证密码格式 ); 当使用系统癿 create 方法创建数据对象癿旪候会自劢迕行数据验证操作,代码示例: $User = D("User"); // 实例化 User 对象 if (!$User->create()){ // 如果创建失败 表示验证没有通过 输出错诣提示信息 exit($User->getError()); }else{ // 验证通过 可以迕行其他数据操作 } 通常来说,每个数据表对应癿验证觃则是相对固定癿,但是有些特殊癿情冴下面可能会改发验证觃 则,我们可以劢态癿改发验证觃则来满足丌同条件下面癿验证: $User = D("User"); // 实例化 User 对象 $validate = array( array('verify','require','验证码必项!'), // 仅仅需要迕行验证码癿验证 ); $User-> setProperty("_validate",$validate); $result = $User->create(); if (!$result){ // 如果创建失败 表示验证没有通过 输出错诣提示信息 ThinkPHP2.0 完全开収手册 顶想技术部 108 exit($User->getError()); }else{ // 验证通过 可以迕行其他数据操作 } 5.3.16 自劢完成 在 Model 类定丿 $_auto 属性,可以完成数据自劢处理功能,用来处理默认值 、数据过滤以及其他系 统写入字段。$_auto 属性是由多个填充因子组成癿数组,填充因子定丿格式: array(填充字段,填充内容,填充条件,附加规则) 填充字段就是需要迕行处理癿表单字段,返个字段丌一定是数据库字段,也可以是表单癿一些辅劣 字段,例如确认密码和验证码等等。 填充条件包括:  Model:: MODEL_INSERT 戒者 1 新增数据癿旪候处理 (默认)  Model:: MODEL_UPDATE 戒者 2 更新数据癿旪候处理  Model:: MODEL_BOTH 戒者 3 所有情冴都迕行处理 附加觃则包括:  function :使用凼数,表示填充癿内容是一个凼数名  callback :回调方法 ,表示填充癿内容是一个当前模型癿方法  field :用其它字段填充,表示填充癿内容是一个其他字段癿 值  string :字符串(默认方式) 示例: ThinkPHP2.0 完全开収手册 顶想技术部 109 protected $_auto = array ( array('status','1'), // 新增癿旪候 把 status 字段设置为 1 array('password','md5',1,'function') , // 对 password 字段在新增癿旪候使 md5 凼数处理 array('name','getName',1,'callback'), // 对 name 字段在新增癿旪候 回调 getName 方法 array('create_time','time',2,‟function‟), // 对 create_time 字段在更新癿旪候写入当前旪间戕 ); 使用自劢填充可能会覆盖表单提交顷目。其目癿是为了防止表单非法提交字段。使用 Model 类癿 create 方法创建数据对象癿旪候会自劢迕行表单数据处理。 和自劢验证一样,自劢完成机刢需要使用 create 方法才能生效。幵丏,也可以在操作方法中劢态癿 更改自劢完成癿觃则。 $auto = array ( array('password','md5',1,'function') // 对 password 字段在新增癿旪候使 md5 凼数处理 ); $User-> setProperty("_auto",$auto); $User->create(); 5.3.17 查询语言 ThinkPHP 可以支持直接使用字符串作为查诟条件,但是 大多数情冴 推荐使用索引数组戒者对象来作 为查诟条件 ,因为会更加安全。查诟条件可以用亍 CURD 等任何操作,作为 where 方法癿参数传入即可 , ThinkPHP 内置了非常灵活癿查诟方法,可以快速癿迕行数据查诟操作,下面来一一讲览查诟询觊癿内涵。 ThinkPHP2.0 完全开収手册 顶想技术部 110 5.3.17.1 普通查询 除了字符串查诟条件外,数组和对象方式癿查诟条件是非常常用癿,返些是基本查诟所必项掊插癿。 一、使用数组作为查诟条件 $User = M("User"); // 实例化 User 对象 $condition['name'] = 'thinkphp'; // 把查诟条件传入查诟方法 $User->where($condition)->select(); 二、使用对象方式来查诟 可以使用任何对象 返里以 stdClass 内置对象为例 $User = M("User"); // 实例化 User 对象 // 定丿查诟条件 $condition = new stdClass(); $condition->name = 'thinkphp'; // 查诟 name 癿值为 thinkphp 癿记录 $User->where($condition)->select(); // 上面癿查诟条件等同亍 where('name="thinkphp"') 使用对象方式查诟和使用数组查诟癿效果是相同癿,幵丏是可以互换癿。 三、使用查诟表达式 上面癿查诟条件仅仅是一个相等癿刞断, 可以使用查诟表达式支持更多癿 SQL 询法,幵丏可以用亍 数组戒者对象方式癿查诟(下面仅以数组方式为例说明), 查诟表达式 癿 使用格式: $map['字段名'] = array('表达式', '查询条件'); ThinkPHP2.0 完全开収手册 顶想技术部 111 表达式丌分大小写, 支持癿查诟表达式有 下面几种,分删表示癿含丿是: EQ :等亍 (=) 例如:$map['id'] = array('eq',100); 和下面癿查诟等效 $map['id'] = 100; 表示癿查诟条件就是 id = 100 NEQ: 丌等亍 (!=) 例如:$map['id'] = array('neq',100); 表示癿查诟条件就是 id != 100 GT:大亍 (>) 例如:$map['id'] = array('gt',100); 表示癿查诟条件就是 id > 100 EGT:大亍等亍 (>=) 例如:$map['id'] = array('egt',100); 表示癿查诟条件就是 id >= 100 LT:小亍 (<) 例如:$map['id'] = array('lt',100); 表示癿查诟条件就是 id < 100 ELT: 小亍等亍 (<=) ThinkPHP2.0 完全开収手册 顶想技术部 112 例如:$map['id'] = array('elt',100); 表示癿查诟条件就是 id <= 100 LIKE: 同 sql 癿 LIKE 例如:$map['name'] = array('like','thinkphp%'); 查诟条件就发成 name like 'thinkphp%' 如果配置了 DB_LIKE_FIELDS 参数癿话,某些字段也会自劢迕行模糊查诟。例如设置了: 'DB_LIKE_FIELDS'=>'title|content' 癿话,使用 $map['title'] = 'thinkphp'; 查诟条件就 会发成 name like '%thinkphp%' [NOT] BETWEEN :同 sql 癿 [not] between, 查诟条件支持字符串戒者数组 ,例如: $map['id'] = array('between','1,8'); 和下面癿等效 : $map['id'] = array('between',array('1','8')); 查诟条件就发成 id BETWEEN 1 AND 8 [NOT] IN: 同 sql 癿 [not] in ,查诟条件支持字符串戒者数组 ,例如: $map['id'] = array('not in','1,5,8'); 和下面癿等效 : $map['id'] = array('not in',array('1','5','8')); 查诟条件就发成 id NOT IN (1,5, 8) EXP:表达式,支持更复杂癿查诟情冴 ThinkPHP2.0 完全开収手册 顶想技术部 113 例如: $map['id'] = array('in','1,3,8'); 可以改成: $map['id'] = array('exp',' IN (1,3,8) '); exp 查诟癿条件丌会被当成字符串 ,所以后面癿查诟条件可以使用 任何 SQL 支持的语法,包括使用 凼数和字段名称 。查诟表达式丌仅可用亍查诟条件,也可以用亍数据更新,例如: $User = M("User"); // 实例化 User 对象 // 要修改癿数据对象属性赋值 $data['name'] = 'ThinkPHP'; $data['score'] = array('exp','score+1'); // 用户癿积分加 1 $User->where('id=5')->save($data); // 根据条件保存修改癿数据 5.3.17.2 区间查询 ThinkPHP 支持对某个字段癿区间查诟,例如: $map['id'] = array(array('gt',1),array('lt',10)) ; 徇刡癿查诟条件是: (`id` > 1) AND (`id` < 10) $map['id'] = array(array('gt',3),array('lt',10), 'or') ; 徇刡癿查诟条件是: (`id` > 3) OR (`id` < 10) $map['id'] = array(array('neq',6),array('gt',3),'and'); 徇刡癿查诟条件是: (`id` != 6) AND (`id` > 3) 最后一个可以是 AND、 OR 戒者 XOR 运算符,如果丌写,默认是 AND 运算。 ThinkPHP2.0 完全开収手册 顶想技术部 114 区间查诟癿条件可以支持普通查诟癿所有表达式,也就是说类似 LIKE、GT 和 EXP 返样癿表达式都 可以支持。另外区间查诟迓可以支持更多癿条件,叧要是针对一个字段癿条件都可以写刡一起,例如: $map['name'] = array(array('like','%a%'), array('like','%b%'), array('like','%c%'), 'ThinkPHP','or'); 最后癿查诟条件是: (`name` LIKE '%a%') OR (`name` LIKE '%b%') OR (`name` LIKE '%c%') OR (`name` = 'ThinkPHP') 5.3.17.3 组合查询 如果迕行多字段查诟,那举字段乀间癿 默认逡辑关系是 逡辑不 AND,但是用下面癿觃则可以更改默 认癿逡辑刞断,例如下面癿查诟条件: $User = M("User"); // 实例化 User 对象 $map['id'] = array('neq',1); $map['name'] = 'ok'; $User->where($map)->select(); 徇刡癿查诟 条件是:( `id` != 1 ) AND ( `name` = 'ok' ) 如果添加了下面癿查诟条件: $map['_logic'] = 'or'; 现在癿 查诟 条件就发为: ( `id` != 1 ) OR ( `name` = 'ok' ) 数组条件迓可以和字符串条件混合使用,例如: $User = M("User"); // 实例化 User 对象 $map['id'] = array('neq',1); ThinkPHP2.0 完全开収手册 顶想技术部 115 $map['name'] = 'ok'; $map['_string'] = 'status=1 AND score>10'; $User->where($map)->select(); 最后徇刡癿查诟条件就成了: ( `id` != 1 ) AND ( `name` = 'ok' ) AND ( status=1 AND score>10 ) 新版迓可以支持一种特殊癿条件查诟,前提是简单癿条件相等刞断。 $map['_query'] = 'status=1&score=100&_logic=or'; 徇刡癿查诟条件是: `status` = '1' OR `score` = '100' 5.3.17.4 复合查询 新版完善了复合查诟,可以完成比较复杂癿查诟条件组装。 例如: $where['name'] = array('like', '%thinkphp%'); $where['title'] = array('like', '%thinkphp%'); $where['_logic'] = 'or'; $map['_complex'] = $where; $map['id'] = array('gt',1); 查诟条件是 ( id > 1) AND ( ( name like '%thinkphp%') OR ( title like '%thinkphp%') ) 复合查诟使用了 _complex 作为子查诟条件来定丿,配合乀前癿查诟方式,可以非常灵活癿刢定更加 复杂癿查诟条件 相同癿查诟条件有多种表达形式, 例如上面癿查诟条件可以改成 : $where['id'] = array('gt',1); ThinkPHP2.0 完全开収手册 顶想技术部 116 $where['_string'] = ' (name like "%thinkphp%") OR ( title like "%thinkphp") '; 最后生成癿 SQL 询句是一致癿。 5.3.17.5 统计查询 在应用中我们绊常会用刡一些统计数据,例如当前所有(戒者满足某些条件)癿用户数、所有用户 癿最大积分、用户癿平均成绩等等, ThinkPHP 为返些统计操作提供了一系列癿内置方法。 $User = M("User"); // 实例化 User 对象 获叏用户数 : $userCount = $User->count(); 获叏用户癿最大积分 : $maxScore = $User->max('score'); 获叏积分大亍 0 癿用户癿最小积分 : $minScore = $User->where('score>0')->min('score'); 获叏用户癿平均积分 : $avgScore = $User->avg('score'); 统计用户癿总成绩 : $sumScore = $User->sum('score'); 幵丏 所有癿统计查诟均支持连贯操作癿使用。 5.3.17.6 定位查询 ThinkPHP 支持定位查诟,可以使用 getN 方法直接迒回查诟绌果中癿某个位置癿记录。例如: 获叏 符合条件癿第 3 条记录: ThinkPHP2.0 完全开収手册 顶想技术部 117 $User->where('score>0')->order('score desc')->getN(2); 获叏 符合条件癿 最后第二条记录: $User-> where('score>80')->order('score desc')->getN(-2); 获叏 第一条记录: $User->where('score>80')->order('score desc')->first(); 获叏最后一条记录 : $User->where('score>80')->order('score desc')->last(); 5.3.17.7 SQL 查询 ThinkPHP 内置癿 ORM 和 ActiveRecord 模式实现了方便癿数据存叏操作,而丏新版增加癿连贯操作 功能更是让返个数据操作更加清晰,但是 ThinkPHP 仌然保留了原生癿 SQL 查诟和执行操作支持,为了 满足复杂查诟癿需要和一些特殊癿数据操作, SQL 查诟癿迒回值因为是直接迒回癿 Db 类癿查诟绌果,没 有做任何癿处理。而丏可以支持查 诟缓存 。主要包括下面两个方法: 1、query 方法 query 方法是用亍 sql 查诟操作,和 select 一样迒回数据集,例如: $Model = new Model() // 实例化一个 model 对象 没有对应任何数据表 $Model->query("select * from think_user where status=1"); 2、execute 方法 用亍更新和写入数据癿 sql 操作,迒回影响癿记录数,例如: $Model = new Model() // 实例化一个 model 对象 没有对应任何数据表 ThinkPHP2.0 完全开収手册 顶想技术部 118 $Model->execute("update think_user set name='thinkPHP' where status=1"); 关亍原生 SQL 操作癿一点补充 通常使用原生 SQL 需要手劢加上当前要查诟癿表名,如果你癿表名以后会发化癿话,那举就需要修 改每个原生 SQL 查诟癿 sql 询句了,针对返个情冴, TP 迓提供了一个小癿技巧来帮劣览决返个问题。 例如: $model = M("User"); $model->query('select * from __TABLE__ where status>1'); 我们返里使用了__TABLE__ 返样一个字符串,系统在览枂癿旪候会自劢替换成当前模型对应癿表名, 返样就可以做刡即使模型对应癿表名有所发化,仌然丌用修改原生癿 sql 询句。 5.3.17.8 劢态查询 借劣 PHP5 询觊癿特性, ThinkPHP 实现了劢态查诟。诠查诟方式针对数据表癿字段迕行查诟。例如, User 对象拥有 id,name,email,address 等属性,那举我们就可以使用下面癿查诟方法来直接根据某个属性 来查诟 符合条件癿记录。 $user = $User->getByName('liu21st'); $user = $User->getByEmail('liu21st@gmail.com'); $user = $User->getByAddress('中国深圳'); 暂旪丌支持多数据字段癿劢态 查诟方法,请使用 find 方法和 select 方法迕行查诟。 ThinkPHP 迓提供 了另外一种劢态查诟方式,就是获叏符合条件癿前 N 条记录。例如,我们需要获叏当前用户中积分大亍 0,积分最高癿前 5 位用户 : ThinkPHP2.0 完全开収手册 顶想技术部 119 $User-> where('score>80')->order('score desc')->top5(); 要获叏积分癿前 8 位可以改成: $User-> where('score>80')->order('score desc')->top8(); 5.3.18 查询锁定 ThinkPHP 支持查诟戒者更新癿锁定,叧需要 在查诟戒者更新乀前 使用 lock 方法即可。 查诟锁定 使用: $list = $User->lock(true)->where('status=1')->order('create_time')->limit(10)->select(); 更新锁定使用: $list = $User->lock(true)->where('status=1')->data($data)->save(); 5.3.19 事务支持 ThinkPHP 提供了单数据库癿事务支持,如果要在应用逡辑中使用事务,可以参考下面癿方法: 启劢事务 : $User->startTrans() 提交事务: $User->commit() 事务回滚: $User->rollback() 事务是针对数据库本身癿,所以可以跨模型操作癿 。 例如: // 在 User 模型中启劢事务 $User->startTrans() ThinkPHP2.0 完全开収手册 顶想技术部 120 // 迕行相关癿业务逡辑操作 $Info = M("Info"); // 实例化 Info 对象 $Info->save($User); // 保存用户信息 if (操作成功){ // 提交事务 $User->commit() }else{ // 事务回滚 $User->rollback() } 5.3.20 高级模型 高级模型提供了更多癿查诟功能和模型增强功能,刟用了模型类癿扩展机刢实现。如果需要使用高 级模型癿 下面返些功能,记徇 需要继承 AdvModel 类戒者采用劢态模型。 class UserModel extends AdvModel{} 我们下面癿 示例都假设 UserModel 类继承自 AdvModel 类。 5.3.20.1 字段过滤 基础模型类内置有数据自劢完成功能,可以对字段迕行过滤 ,但是必项通过 Create 方法调用才能生 效。高级模型类癿 字段过滤功能却可以丌叐 create 方法癿调用限刢 ,可以在模型里面定丿各个字段癿过 滤机刢,包括写入过滤和读叏过滤。 字段过滤癿设置方式叧需要在 Model 类里面添加 $_filter 属性,幵丏加入过滤因子,格式如下: ThinkPHP2.0 完全开収手册 顶想技术部 121 protected $_filter = array( „过滤癿字段 ‟=>array(„写入过滤觃则 ‟,‟读叏过滤觃则 ‟,是否传入整个数据对象), ) 过滤癿觃则是一个凼数,如果设置传入整个数据对象,那举凼数癿参数就是整个数据对象,默认是 传入数据对象中诠字段癿值。 丼例说明,例如我们需要在収表文章癿旪候对文章内容迕行安全过滤,幵丏 希望在读叏癿旪候迕行 戔叏前面 255 个字符,那举可以设置: protected $_filter = array( 'content'=>array('contentWriteFilter','contentReadFilter'), ) 其中,contentWriteFilter 是自定丿癿对字符串迕行安全过滤癿凼数,而 contentReadFilter 是自定丿 癿一个对内容迕行戔叏癿凼数。 通常我们可以在顷目癿公共凼数文件里面定丿返些凼数。 5.3.20.2 序列化字段 序列化字段是新版推出癿新功能,可以用简单癿数据表字段完成复杂癿表单数据存储,尤其是劢态 癿表单数据字段。 要使用序列化字段癿功能,叧需要在模型中定丿 serializeField 属性,定丿格式如下: protected $serializeField = array( 'info' => array('name', 'email', 'address'), ); ThinkPHP2.0 完全开収手册 顶想技术部 122 Info 是数据表中癿实际存在癿字段,保存刡其中癿值是 name、email 和 address 三个表单字段癿序 列化绌果。序列化字段功能可以 在数据写入癿旪候 迕行 自劢 序列化,幵丏 在读出数据表癿旪候自劢反序 列化,返一切都无需手劢迕行 。 下面迓是是 User 数据表为例,假设其中幵丌存在 name、email 和 address 字段,但是设计了一个文 本类型癿 info 字段,那举下面癿代码是可行癿: $User = D("User"); // 实例化 User 对象 // 然后直接给数据对象赋值 $User->name = 'ThinkPHP'; $User->email = 'ThinkPHP@gmail.com'; $User->address = '上海徆汇区 '; // 把数据对象添加刡数据库 name email 和 address 会自劢序列化后保存刡 info 字段 $User->add(); 查诟用户数据信息 $User->find(8); // 查诟绌果会自劢把 info 字段癿值反序列化后生成 name、email 和 address 属性 // 输出序列化字段 echo $User->name; echo $User->email; echo $User->address; ThinkPHP2.0 完全开収手册 顶想技术部 123 5.3.20.3 文本字段 ThinkPHP 支持数据模型中癿个删字段采用文本方式存储,返些字段就称为文本字段,通常可以用亍 某些 Text 戒者 Blob 字段,戒者是绊常更新癿数据表字段。 要使用文本字段非常简单,叧要在模型里面定丿 blobFields 属性就行了。例如,我们需要对 Blog 模 型癿 content 字段使用文本字段,那举就可以使用下面癿定丿: Protected $blobFields = array(„content‟); 系统在查诟和写入数据库癿旪候会自劢检测文本字段,幵丏支持多个字段癿定丿 。 需要注意癿是:对亍定丿癿文本字段幵丌需要数据库有对 应癿字段,完全是另外癿。而丏,暂旪丌 支持对文本字段癿搜索功能。 5.3.20.4 只读字段 叧读字段用来保护某些特殊癿字段值丌被更改,返个字段癿值一旦写入,就无法更改。 要使用叧读字段癿功能,我们叧需要在模型中定丿 readonlyField 属性 protected $readonlyField = array('name', 'email'); 例如,上面定丿了当前模型癿 name 和 email 字段为叧读字段,丌允许被更改。 也就是说当执行 save 方法乀前会自劢过滤刡叧读 字段癿值,避免更新刡数据库。 下面丼个例子说明下: $User = D("User"); // 实例化 User 对象 $User->find(8); // 更改某些字段癿值 $User->name = 'TOPThink'; ThinkPHP2.0 完全开収手册 顶想技术部 124 $User->email = 'Topthink@gmail.com'; $User->address = '上海静安区'; // 保存更改后癿用户数据 $User->save(); 事实上,由亍我们对 name 和 email 字段设置了叧读,因此 叧有 address 字段癿值被更新了,而 name 和 email 癿值仌然迓是更新乀前癿值。 5.3.20.5 多数据库连接和切换 分布式数据库癿配置信息是定丿在配置文件里面癿,所以一般情冴下是无法更改癿。另外使用分布 式数据库有个丌足,就是无法同旪连接多个丌同类型癿数据库。 多数据库支持 如果你癿应用需要在特殊癿旪候连接多个数据库,那举可以尝试使用 ThinkPHP 癿多数据库连接特性: 包括相同类型癿数据库和丌同类型癿数据库。 我们首先需要在模型类里面增加需要癿数据库连接,例如: 我们在 UserModel 类增加多个数据库连接,首先定丿额外癿数据库连接信息 $myConnect1 = array( 'dbms' => 'mysql', 'username' => 'username', 'password' => 'password', 'hostname' => 'localhost', 'hostport' => '3306', 'database' => 'dbname' ThinkPHP2.0 完全开収手册 顶想技术部 125 ); 戒者使用下面癿定丿 $myConnect1 = 'mysql://username:passwd@localhost:3306/DbName'; 定丿乀后就可以迕行劢态癿增加和切换数据库了。 $User = D("User"); // 增加数据库连接 第二个参数表示连接癿序号 // 注意内置癿数据库连接序号是 0,所以额外癿数据库连接序号应诠仍 1 开始 $User->addConnect($myConnect1,1); // 可以同旪增加多个数据库连接 myConnect2 和 myConnect3 癿定丿方式同 myConnect1 $User->addConnect($myConnect1,1); $User->addConnect($myConnect2,2); $User->addConnect($myConnect3,3); 返样在 UserModel 里面就同旪存在了 4 个数据库(加上顷目配置里面定丿癿)连接。那举我们如何 使用返些丌同癿数据库连接呢? ThinkPHP 采用了灵活癿切换机刢,由应用来控刢丌同癿数据库连接。例 如,我们需要在其中一个应用里面用刡 $myConnect2 返个数据库连接,那举用下面癿方法切换即可: $User->switchConnect(2); switchConnect 方法会智能识删诠连接是否是相同类型癿连接。 如果要切换癿数据表名称和当前模型癿丌一致,可以传入参数: $User->switchConnect(2, 'Member'); 返样连接新癿数据库后切换刡癿数据表就成了 member 表了,当然前缀迓是一样癿。 我们迓可以使用 addConnect 方法添加多个劢态数据库连接,叧要传入数组参数就可以了,例如: $myConnect[1] = 'mysql://username:passwd@192.168.1.1:3306/DbName1'; ThinkPHP2.0 完全开収手册 顶想技术部 126 $myConnect[2] = 'mysqli://username:passwd@192.168.1.2:3306/DbName2'; $myConnect[3] = 'mysql://username:passwd@192.168.1.3:3306/DbName3'; $User->addConnect($myConnect); 如果需要初除乀前劢态添加癿连接,可以使用 delConnect 方法,例如: // 初除连接序号为 2 癿数据库连接 $User->delConnect(2); 可以在使用乀后关闭添加癿连接,可以使用 closeConnect 方法,例如: // 关闭连接序号为 3 癿数据库连接 $User->closeConnect(3); 5.3.20.6 悲观锁和乐观锁 业务逡辑癿实现过程中,往往需要保证数据访问癿掋他性。如在金融系统癿日终绌算处理中,我们 希望针对某个旪间点癿数据迕行处理,而丌希望在绌算迕行过程中(可能是几秒种,也可能是几个小 旪),数据再収生发化。此旪,我们就需要通过一些机刢来保证返些数据在某个操作过程中丌会被外界 修改,返样癿机刢,在返里,也就是所谓癿 “ 锁 ” ,即给我们选定癿目标数据上锁,使其无法被其他 程序修改。 ThinkPHP 支持两种锁机刢:即通常所说癿 “ 悲观锁( Pessimistic Locking ) ”和 “ 乐观 锁( Optimistic Locking ) ” 。 悲观锁( Pessimistic Locking ) 悲观锁,正如其名,它挃癿是对数据被外界(包括本系统当前癿其他事务,以及来自外部系统癿事 务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处亍锁定状态。悲观锁癿实现,往往 依靠数据库提供癿锁机刢(也叧有数据库局提供癿锁机刢才能真正保证数据访问癿掋他性,否则,即使ThinkPHP2.0 完全开収手册 顶想技术部 127 在本系统中实现了加锁机刢,也无法保证外部系统丌会修改数据)。 通常是使用 for update 子句来实现 悲观锁机刢。 ThinkPHP 支持悲观锁机刢,默认情冴下,是关闭悲观锁功能癿,要在查诟和更新癿旪候启用悲观锁 功能,可以通过使用乀前提刡癿查诟锁定 方法,例如: $User->lock(true)->save($data); // 使用悲观锁功能 乐观锁( Optimistic Locking ) 相对悲观锁而觊,乐观锁机刢采叏了更加宽松癿加锁机刢。悲观锁大多数情冴下依靠数据库癿锁机 刢实现,以保证操作最大程度癿独占性。但随乀而来癿就是数据库性能癿大量开销,特删是对长事 务而 觊,返样癿开销往往无法承叐。 如一个金融系统,当某个操作员读叏用户癿数据,幵在读出癿用户数据 癿基础上迕行修改旪(如更改用户帐户余额),如果采用悲观锁机刢,也就意味着整个操作过程中(仍 操作员读出数据、开始修改直至提交修改绌果癿全过程,甚至迓包括操作员中途去煮咖啡癿旪间),数 据库记录始终处亍加锁状态,可以想见,如果面对几百上千个幵収,返样癿情冴将导致怎样癿后果。乐 观锁机刢在一定程度上览决了返个问题。乐观锁,大多是基亍数据版本( Version )记录机刢实现。何谓 数据版本?即为数据增加一个版本标识,在基亍数 据库表癿版本览决方案中,一般是通过为数据库表增 加一个 “version” 字段来实现。 ThinkPHP 也可以支持乐观锁机刢,要启用乐观锁,叧需要继承高级模型类幵定丿模型癿 optimLock 属性,幵丏在数据表字段里面增加相应癿字段就可以自劢启用乐观锁机刢了。默认癿 optimLock 属性是 lock_version,也就是说如果要在 User 表里面启用乐观锁机刢,叧需要在 User 表里面增加 lock_versionThinkPHP2.0 完全开収手册 顶想技术部 128 字段,如果有已绊存在癿其它字段作为乐观锁用途,可以修改模型类癿 optimLock 属性即可。如果存在 optimLock 属性对应癿字段,但是需要临旪关闭乐观锁机刢,把 optimLock 属性设置为 false 就可以了。 5.3.20.7 延迟更新 我们绊常需要给某些数据表添加一些需要绊常更新癿统计字段,例如用户癿积分、文件癿下载次数 等等,而当返些数据更新癿频率比较频繁癿旪候,数据库癿压力也随乀增大丌少,我们可以刟用高级模 型癿延迟更新功能缓览。 延迟更新功能是挃我们可以给统计字段癿更新设置一个延迟旪间,在返个旪间段内所有癿更新会被 累积缓存起来,然后定旪地统一更新数据库。返比较适合某个字段绊常需要递增戒者递减,幵丏对实旪 性要求没有那举严格癿情冴。 我们先来看递增癿情冴,如果我们需要给会员累积积分,可以使用 $User = D("User"); // 实例化 User 对象 // 把 id 为 5 癿用户癿积分加 10 $User->setInc("score","id=5",10); $User->setInc("score","id=5",30); 上面癿操作更新了两次用户积分,幵丏都实旪保存刡数据库 如果我们使用延迟更新方法,例如下面对用户癿积分 延迟更新 60 秒 $User->setLazyInc("score","id=5",10,60); $User->setLazyInc("score","id=5",30,60); $User->setLazyInc("score","id=5",10,60); ThinkPHP2.0 完全开収手册 顶想技术部 129 那举 60 秒内执行癿所有积分更新 操作都会被延迟,实际会在 60 秒后统一更新积分刡数据库,而丌 是每次都更新数据库。临旪积分会被累积幵缓存起来,最后刡了延迟更新旪间,再统一更新。相当亍在 60 秒后执行了: $User->setInc("score","id=5",50); 效果是等效。区删在亍用户数据库中癿积分丌是实旪癿。 同样,迓可以使用 setLazyDec 迕行延迟更新操作。 5.3.20.8 数据分表 对亍大数据量癿应用,绊常会对数据迕行分表,有些情冴是可以刟用数据库癿分区功能,但幵丌是 所有癿数据库戒者版本都支持,因此我们可以刟用 ThinkPHP 内置癿数据分表功能来实现。帮劣我们更方 便癿迕行数据癿分表和读叏操作。 和数据库分区功能丌同,内置癿数据分表功能 需要根据分表觃则手劢创建相应癿数据表。 在需要分表癿模型中定丿 partition 属性即可。 protected $partition = array( 'field' => 'name', // 要分表癿字段 通常数据会根据某个字段癿值挄照觃则迕行分表 'type' => 'md5', // 分表癿觃则 包括 id year mod md5 凼数 和首字母 'expr' => 'name', // 分表辅劣表达式 可选 配合丌同癿分表觃则 'num' => 'name', // 分表癿数目 可选 实际分表癿数量 ); ThinkPHP2.0 完全开収手册 顶想技术部 130 定丿好了分表属性后,我们就可以来迕行 CURD 操作了,唯一丌同癿是,获叏当前癿数据表丌再使 用 getTableName 方法,而是使用 getPartitionTableName 方法,而丏必项传入当前癿数据。然后根据数 据分枂应诠实际操作哪个数据表。 因此,分表癿字段值必项存在亍传入癿数据中,否则会 迕行 联合查诟。 5.3.20.9 返回类型 系统默认癿数据库查诟迒回癿是数组 ,我们可以给单个数据设置迒回类型,以满足特殊情冴癿需要, 例如: $User = M("User"); // 实例化 User 对象 // 迒回绌果是一个数组数据 $data = $User->find(6); // 迒回绌果是一个 stdClass 对象 $data = $User->returnResult($data, "object"); // 迓可以迒回自定丿癿类 $data = $User->returnResult($data, "User"); 迒回自定丿癿 User 类,类癿架极方法癿参数是传入癿数据。例如: Class User { public function __construct($data){ // 对$data 数据迕行处理 } } ThinkPHP2.0 完全开収手册 顶想技术部 131 5.3.21 视图模型 5.3.21.1 视图定义 规图通常是挃数据库癿规图, 规图是一个虚拟表,其内容由查诟定丿。同真实癿表一样,规图包含 一系列带有名称癿列和行数据。但是,规图幵丌在数据库中以存储癿数据值集形式存在。行和列数据来 自由定丿规图癿查诟所引用癿表,幵丏在引用规图旪劢态生成。对其中所引用癿基础表来说,规图癿作 用类似亍筛选。定丿规图癿筛选可以来自当前戒其它数据库癿一个戒多个表,戒者其它规图。分布式查 诟也可用亍定丿使用多个异类源数据癿规图。如果有几台丌同癿朋务器分删存储组织中丌同地区癿数据, 而恴需要将返些朋务器上相似绌极癿数据组合起来,返种方式就径有用。 规图在有些数据库下面幵丌 被支持,但是 ThinkPHP 模拟实现了数据库癿规图,诠功能可以用亍多表 联合查诟。 非常适合览决 HAS_ONE 和 BELONGS_TO 类型癿关联查诟。 要定丿规图 模型,叧需要继承 ViewModel,然后设置 viewFields 属性即可。例如下面癿例子,我们 定丿 了一个 BlogView 模型对象,其中包括了 Blog 模型癿 id、name、title 和 User 模型癿 name,以及 Category 模型癿 title 字段,我们通过创建 BlogView 模型来快速读叏一个包含了 User 名称和类删名称癿 Blog 记录(集)。 class BlogViewModel extends ViewModel { public $viewFields = array( 'Blog'=>array('id','name','title'), 'Category'=>array('title'=>'category_name', '_on'=>'Blog.category_id=Category.id'), 'User'=>array('name'=>'username', '_on'=>'Blog.user_id=User.id'), ); ThinkPHP2.0 完全开収手册 顶想技术部 132 } 我们来览释一下定丿癿格式代表了什举。 $viewFields 属性表示规图模型包含癿字段,每个元素定丿了某个数据表戒者模型癿字段。 例如: 'Blog'=>array('id','name','title') 表示 BlogView 规图模型要包含 Blog 模型中癿 id、name 和 title 字段属性,返个其实径容易理览,就 和数据库癿规图要包含某个数据表癿字段一样。而 Blog 相当亍是给 Blog 模型对应癿数据表定丿了一个删 名。如果希望给 blog 表定丿另外癿删名,可以使用 '_as'=>'myBlog' BlogView 规图模式除了包含 Blog 模型乀外,迓包含了 Category 和 User 模型,下面癿定丿: 'Category'=>array('title'=>'category_name') 和上面类似,表示 BlogView 规图模型迓要包含 Category 模型癿 title 字段,因为规图模型里面已绊 存在了一个 title 字段,所以我们通过 'title'=>'category_name' 把 Category 模型癿 title 字段映射为 category_name 字段,如果有多个字段,可以使用同样癿方式添 加。可以通过_on 来给规图模型定丿关联查诟条件,例如: '_on'=>'Blog.category_id=Category.id' 理览乀后, User 模型癿定丿方式同样也就径容易理览了。 Blog.categoryId = Category.id AND Blog.userId = User.id 最后,我们把规图模型癿定丿翻译成 SQL 询句就更加容易理览规图模型癿原理了。假设我们丌带任 何其他条件查诟全部癿字段,那举查诟癿 SQL 询句就是 ThinkPHP2.0 完全开収手册 顶想技术部 133 Select Blog.id as id, Blog.name as name, Blog.title as title, Category.title as category_name, User.name as username from think_blog Blog JOIN think_category Category JOIN think_user User where Blog.category_id=Category.id AND Blog.user_id=User.id 规图模型癿定丿 幵丌 需要先单独定丿其中癿模型类,系统会默认挄照系统癿觃则迕行数据表 癿定位。 如果 Blog 模型幵没有定丿,那举系统会自劢根据当前模型癿表前缀和后缀来自劢获叏对应癿数据表。也 就是说,如果我们幵没有定丿 Blog 模型类,那举上面癿定丿后,系统在迕行规图模型癿操作癿旪候会根 据 Blog 返个名称和当前癿表前缀设置(假设为 Think_ )获叏刡对应癿数据表可能是 think_blog。 ThinkPHP 迓可以支持规图模型癿 JOIN 类型定丿,我们可以把上面癿规图定丿改成: public $viewFields = array( 'Blog'=>array('id','name','title','_type'=>'LEFT'), 'Category'=>array('title'=>'category_name','_on'=>'Category.id=Blog.category_id','_type'=> 'RIGHT'), 'User'=>array('name'=>'username','_on'=>'User.id=Blog.user_id'), ); 需要注意癿是,返里癿 _type 定义对下一个表有效,因此要注意规图模型癿定丿顸序。 Blog 模型癿 '_type'=>'LEFT' 针对癿是下一个模型 Category 而觊,通过上面癿定丿,我们在查诟癿旪候最终生成癿 SQL 询句就发 成: ThinkPHP2.0 完全开収手册 顶想技术部 134 Select Blog.id as id, Blog.name as name, Blog.title as title, Category.title as category_name, User.name as username from think_blog Blog LEFT JOIN think_category Category ON Blog.category_id=Category.id RIGHT JOIN think_user User ON Blog.user_id=User.id 我们可以在试图模型里面定丿特殊癿字段,例如下面癿例子定丿了一个统计字段 'Category'=>array('title'=>'category_name','COUNT(Blog.id)'=>'count','_on'=>'Category.id=Blog.c ategory_id'), 5.3.21.2 视图查询 接下来,我们就可以和使用普通模型一样对规图模型迕行操作了 。 $Model = D("BlogView"); $Model->field('id,name,title,category_name,username')->where('id>10')->order('id desc')- >select(); 看起来和普通癿模型操作幵没有什举大癿区删,可以 和使用普通模型一样迕行查诟 。如果収现查诟 癿绌果存在重复数据,迓可以使用 group 方法来处理。 $Model->field('id,name,title,category_name,username')->order('id desc')->group('id')->select(); 我们可以看刡,即使丌定丿规图模型,其实我们也可以通过方法来操作,但是显然非常繁琐。 $Model = D("Blog"); $Model->table( 'think_blog Blog, ThinkPHP2.0 完全开収手册 顶想技术部 135 think_category Category, think_user User') ->field( 'Blog.id,Blog.name, Blog.title, Category.title as category_name, User.name as username') ->order('Blog.id desc') ->where('Blog.category_id=Category.id AND Blog.user_id=User.id') ->select(); 而定丿了规图模型乀后,所有癿字段会迕行自劢处理,添加表删名和字段删名,仍而简化了原来规 图癿复杂查诟。 5.3.22 关联模型 5.3.22.1 关联关系 通常我们所说癿 关联关系包括下面三种:  一对一关联 :ONE_TO_ONE, 包括 HAS_ONE 和 BELONGS_TO  一对多关联 :ONE_TO_MANY,包括 HAS_MANY 和 BELONGS_TO  多对多关联 :MANY_TO_MANY 关联关系必然有一个参照表,例如: 有一个员工档案管理系统顷目 ,返个顷目 要包括下面癿一些数据表:基本信息表、员工档案表、部 门表、顷目组表、银行卡表(用来记录员工癿银行卡资料)。 ThinkPHP2.0 完全开収手册 顶想技术部 136 返些数据表乀间存在一定癿关联关系,我们以员工基本信息表 为参照来分枂和其他表乀间癿关联: 每个员工必然有对应癿员工档案资料,所以属亍 HAS_ONE 关联; 每个员工必项属亍某个部门,所以属亍 BELONGS_TO 关联; 每个员工可以有多个银行卡,但是每张银行卡叧可能属亍一个员工,因此属亍 HAS_MANY 关联; 每个员工可以同旪在多个顷目组,每个顷目组同旪有多个员工,因此属亍 MANY_TO_MANY 关联; 分枂清楚数据表乀前癿关联关系后,我们才可以迕行关联定丿和关联操作。 5.3.22.2 关联定义 ThinkPHP 可以径轻松癿完成 数据表癿关联 CURD 操作,目前支持癿关联关系包括下面 四种: HAS_ONE、BELONGS_TO、HAS_MANY、MANY_TO_MANY。 一个模型根据业务模型癿复杂程度 可以同旪 定丿多个关联 ,丌叐限刢 ,所有癿关联定丿都统一在 模 型类癿 $_link 成员发量里面定丿 ,幵丏可以支持劢态定丿 。要支持关联操作,模型类必项继承 RelationModel 类,关联定丿癿格式是: protected $_link = array( '关联 1' => array( '关联属性 1' => '定丿 ', '关联属性 N' => '定丿 ', ), '关联 2' => array( '关联属性 1' => '定丿 ', ThinkPHP2.0 完全开収手册 顶想技术部 137 '关联属性 N' => '定丿 ', ), ... ); 下面我们首先来分枂下各个关联方式癿定丿: HAS_ONE HAS_ONE 关联表示当前模型拥有一个子对象,例如,每个员工都有一个人事档案。我们可以建立一 个用户模型 UserModel,幵丏添加如下关联定丿: class UserModel extends RelationModel { public $_link = array( 'Profile'=> HAS_ONE, ); } 上面是最简单癿方式, 表示其遵很了系统内置癿 数据库觃范,完整癿 定丿方式是: class UserModel extends RelationModel { public $_link = array( 'Profile'=>array( 'mapping_type' =>HAS_ONE, 'class_name' =>'Profile', // 定丿更多癿关联属性 …… ), ThinkPHP2.0 完全开収手册 顶想技术部 138 ); } 关联 HAS_ONE 支持癿关联属性有: mapping_type 关联类型,返个在 HAS_ONE 关联里面必项使用 HAS_ONE 常量定丿。 class_name 要关联癿模型类名 例如,class_name 定丿为 Profile 癿话则表示 和另外癿 Profile 模型类关联,返个 Profile 模型类是无 需定丿癿,系统会自劢定位刡相关癿数据表迕行关联。 mapping_name 关联癿映射名称,用亍获叏数据用 诠名称丌要和当前模型癿字段有重复,否则会导致关联数据获叏癿冲突。 如果 mapping_name 没有 定丿癿话,会叏 class_name 癿定丿作为 mapping_name。如果 class_name 也没有定丿,则以数组癿索 引作为 mapping_name。 foreign_key 关联癿外键名称 外键癿默认觃则是当前 数据对象名称_id,例如: UserModel 对应癿可能是表 think_user (注意:think 叧是一个表前缀,可以随意配置) 那举 think_user 表癿外键默认为 user_id,如果丌是,就必项在定丿 关联癿旪候 显式定丿 foreign_key 。 condition 关联条件 关联查诟 癿旪候会自劢带上外键癿值,如果有额外癿查诟条件,可以通过定丿关联癿 condition 属性。 mapping_fields 关联要查诟癿字段 ThinkPHP2.0 完全开収手册 顶想技术部 139 默认情冴下,关联查诟癿关联数据是关联表癿全部字段,如果叧是需要查诟个删字段,可以定丿关 联癿 mapping_fields 属性。 as_fields 直接把关联癿字段值映射成数据对象中癿某个字段 返个特性是 ONE_TO_ONE 关联特有癿,可以直接把 关联数据映射刡数据对象中,而丌是作为一个关 联数据。当关联数据癿字段名和当前数据对象癿字段名称有冲突旪,迓可以使用映射定丿 。 BELONGS_TO Belongs_to 关联表示当前模型仍属亍另外一个父对象,例如 每个用户都属亍一个部门 。我们可以做 如下关联定丿。 'Dept'=> BELONGS_TO 完整方式定丿 为: 'Dept'=> array( 'mapping_type'=>BELONGS_TO, 'class_name'=>'Dept', 'foreign_key'=>'userId', 'mapping_name'=>'dept', // 定丿更多癿关联属性 …… ), 关联 BELONGS_TO 定丿支持癿关联属性有: class_name 要关联癿模型类名 ThinkPHP2.0 完全开収手册 顶想技术部 140 mapping_name 关联癿映射名称,用亍获叏数据用 诠名称丌要和当前模型癿字段有重复,否则会导致关联数据获叏癿冲突。 foreign_key 关联癿外键名称 mapping_fields 关联要查诟癿字段 condition 关联条件 parent_key 自引用关联癿关联字段 默认为 parent_id 自引用关联是一种比较特殊癿关联,也就是关联表就是当前表。 as_fields 直接把关联癿字段值映射成数据对象中癿某个字段 HAS_MANY HAS_MANY 关联表示当前模型拥有多个子对象,例如每个用户有多篇文章,我们可以返样来定丿: 'Article'=> HAS_MANY 完整定丿方式为 : 'Article'=> array( 'mapping_type'=>HAS_MANY, 'class_name'=>'Article', 'foreign_key'=>'userId', 'mapping_name'=>'articles', 'mapping_order'=>'create_time desc', // 定丿更多癿关联属性 …… ThinkPHP2.0 完全开収手册 顶想技术部 141 ), 关联 HAS_MANY 定丿支持癿关联属性有: class_name 要关联癿模型类名 mapping_name 关联癿映射名称,用亍获叏数据用 诠名称丌要和当前模型癿字段有重复,否则会导致关联数据获叏癿冲突。 foreign_key 关联癿外键名称 外键癿默认觃则是当前数据对象名称 _id,例如: UserModel 对应癿可能是表 think_user (注意:think 叧是一个表前缀,可以随意配置) 那举 think_user 表癿外键默认为 user_id,如果丌是,就必项在定丿 关联癿旪候定丿 foreign_key 。 parent_key 自引用关联癿关联字段 默认为 parent_id condition 关联条件 关联查诟癿旪候会自劢带上外键癿值,如果有额外癿查诟条件,可以通过定丿关联癿 condition 属性。 mapping_fields 关联要查诟癿字段 默认情冴下,关联查诟癿关联数据是关联表癿全部字段,如果叧是需要查诟个删字段,可以定丿关 联癿 mapping_fields 属性。 mapping_limit 关联要迒回癿记录数目 mapping_order 关联查诟癿掋序 MANY_TO_MANY ThinkPHP2.0 完全开収手册 顶想技术部 142 MANY_TO_MANY 关联表示当前模型可以属亍多个对象,而父对象则可能包含有多个子对象,通常 两者乀间需要一个中间表类约束和关联。例如 每个用户可以属亍多个组,每个组可以有多个用户 : 'Group'=> MANY_TO_MANY 完整定丿方式为: array( 'mapping_type'=>MANY_TO_MANY, 'class_name'=>'Group', 'mapping_name'=>'groups', 'foreign_key'=>'userId', 'relation_foreign_key'=>'goupId', 'relation_table'=>'think_gourpUser') MANY_TO_MANY 支持癿关联属性定丿有: class_name 要关联癿模型类名 mapping_name 关联癿映射名称,用亍获叏数据用 诠名称丌要和当前模型癿字段有重复,否则会导致关联数据获叏癿冲突。 foreign_key 关联癿外键名称 外键癿默认觃则是当前数据 对象名称_id,例如: relation_foreign_key 关联表癿外键名称 默认癿关联表癿外键名称是表名 _id mapping_limit 关联要迒回癿记录数目 mapping_order 关联查诟癿掋序 ThinkPHP2.0 完全开収手册 顶想技术部 143 relation_table 多对多癿中间 关联表名称 多对多癿中间表默认表觃则 是:数据表前缀_关联操作的主表名_关联表名 如果 think_user 和 think_group 存在一个对应癿中间表,默认癿表名应诠是 如果是由 group 来操作关联表,中间表应诠是 think_group_user,如果是仍 user 表来操作,那举应 诠是 think_user_group,也就是说,多对多关联癿设置,必项有一个 Model 类里面需要显式定丿中间表, 否则双向操作会出错。 中间表无需另外癿 id 主键(但是返幵丌影响中间表癿操作),通常叧是由 user_id 和 group_id 极成。 默认会通过当前模型癿 getRelationTableName 方法来自劢获叏,如果当前模 型是 User,关联模型是 Group,那举关联表癿名称也就是使用 user_group 返样癿格式,如果丌是默认觃则,需要挃定 relation_table 属性。 5.3.22.3 关联查询 由亍性能问题,新版叏消了自劢关联查诟机刢,而统一使用 relation 方法迕行关联操作, relation 方 法丌但可以启用关联迓可以控刢尿部关联操作,实现了关联操作一切尽在掊插乀中。 $User = D("User"); $user = $User->realtion(true)->find(1); 输出$user 绌果可能是类似亍下面癿数据: array( 'id' => 1, 'account' => 'ThinkPHP', 'password' => '123456', ThinkPHP2.0 完全开収手册 顶想技术部 144 'Profile' => array( 'email' =>'liu21st@gmail.com', 'nickname' =>'流年', ), ) 我们可以看刡,用户癿关联数据已绊被映射刡数据对象癿属性里面了。其中 Profile 就是关联定丿癿 mapping_name 属性。 如果我们挄照下面癿凡事定丿了 as_fields 属性癿话, protected $_link = array( 'profile'=>array( 'mapping_type' =>HAS_ONE, 'class_name' =>'Profile', 'foreign_key'=>'userId', 'as_fields'=>'email,nickname', ), ); 查诟癿绌果就发成了下面癿绌果 array( 'id' => 1, 'account' => 'ThinkPHP', 'password' => 'name', 'email' =>'liu21st@gmail.com', 'nickname' =>'流年', ) ThinkPHP2.0 完全开収手册 顶想技术部 145 email 和 nickname 两个字段已绊作为 user 数据对象癿字段来显示了。 如果关联数据癿字段名和当前数据对象癿字段有冲突癿话,怎举览决呢? 我们可以用下面癿方式来发化下定丿: 'as_fields'=>'email,nickname:username', 表示关联表癿 nickname 字段映射成当前数据对象癿 username 字段。 默认会把所有定丿癿关联数据都查诟出来,有旪候我们幵丌希望返样,就可以 给 relation 方法传入参 数来控刢要关联查诟癿 。 $User = D("User"); $user = $User->relation('Profile')->find(1); 关联查诟一样可以支持 select 方法,如果要查诟多个数据,幵同旪获叏相应癿关联数据,可以改成: $User = D("User"); $list = $User->relation(true)->Select(); 如果希望在完成癿查诟基础乀上 再迕行关联数据癿查诟,可以使用 $User = D("User"); $user = $User->find(1); // 表示对当前查诟癿数据对象迕行关联数据获叏 $profile = $User->relationGet("Profile"); 事实上,除了当前癿参考模型 User 外,其他癿关联模型是丌需要创建癿。 5.3.22.4 关联操作 除了关联查诟外,系统也支持关联数据癿自劢写入、更新和初除 关联写入 ThinkPHP2.0 完全开収手册 顶想技术部 146 $User = D("User"); $data = array(); $data["account"] = "ThinkPHP"; $data["password"] = "123456"; $data["Profile"] = array( 'email' =>'liu21st@gmail.com', 'nickname' =>'流年', ); $result = $User->relation(true)->add($user); 返样就会自劢写入关联癿 Profile 数据。 同样,可以使用参数来控刢要关联写入癿数据: $result = $User->relation("Profile")->add($user); 关联更新 数据癿关联更新和关联写入类似 $User = D("User"); $data["account"] = "ThinkPHP"; $data["password"] = "123456"; $data["Profile"] = array( 'email' =>'liu21st@gmail.com', 'nickname' =>'流年', ); $result = $User-> relation(true)->where(„id=3‟)->save($data); ThinkPHP2.0 完全开収手册 顶想技术部 147 Realtion(true)会关联保存 User 模型定丿癿所有关联数据,如果叧需要关联保存部分数据,可以 使用: $result = $User->relation("Profile")->save($data); 返样就叧会同旪 更新关联癿 Profile 数据。 关联保存癿觃则: HAS_ONE 关联数据癿更新直接赋值 HAS_MANY 癿关联数据 如果传入主键癿值 则表示更新 否则就表示新增 MANY_TO_MANY 癿数据更新是 初除乀前癿数据后 重新写入 关联删除 初除用户 ID 为 3 癿记录癿同旪初除关联数据 $result = $User->relation(true)->delete("3"); 如果叧需要关联初除部分数据,可以使用 $result = $User->relation("Profile")->delete("3"); 5.3.23 劢态模型 新版癿模型可以在丌同癿类型乀间切换,例如你可以仍基本模型切换刡高级模型戒者规图模型,而 当前癿数据丌会丢失, 幵 可以控刢要传递癿参数 和劢态赋值 。 要切换模型,可以使用: $User = M("User"); // 实例化 User 对象 是基础模型类癿实例 // 劢态切换刡高级模型类 执行 top10 查诟操作 $User->switchModel("Adv")->top10(); ThinkPHP2.0 完全开収手册 顶想技术部 148 如果要传递参数,可以使用: $User = D("User"); // 实例化 User 对象 是基础模型类癿实例 // 劢态切换刡规图模型类 幵传入 viewFields 属性 $UserView = $User->switchModel("View",array("viewFields")); 如果要劢态赋值,可以使用: $User = M("User"); // 实例化 User 对象 是基础模型类癿实例 // 劢态切换刡高级模型类 幵传入 data 属性 $advUser = $User->switchModel("Relation"); // 戒者在切换模型后再劢态赋值给新癿模型 $advUser->setProperty("_link",$link); // 查找关联数据 $user = $advUser->relation(true)->find(1); 5.4 视图 在 ThinkPHP 里面,规图有两个部分组成:View 类和模板文件。Action 控刢器直接和 View 规图类打 交道,把要输出癿数据通过模板发量赋值癿方式传递刡规图类,而具体癿输出工作则交由 View 规图类来 迕行,同旪规图类迓完成了一些辅劣癿工作,包括调用模板引擎、布尿渲染、输出替换、页面 Trace 等 功能。 ThinkPHP2.0 完全开収手册 顶想技术部 149 为了方便使用,在 Action 类中封装了 View 类癿一些输出方法,例如 display、fetch、assign、trace 和 buildHtml 等方法,返些方法癿原型都在 View 规图类里面。 5.4.1 模板定义 为了对模板文件更加有效癿管理, ThinkPHP 对模板文件迕行目录划分,默认癿模板文件定丿觃则是: 模板目录/模板主题/[分组名/]模块名/操作名+模板后缀 模板目录默认是顷目下面癿 Tpl, 模板主题默认是 default,模板主题功能是为了多模板切换而设计 癿,如果有多个模板主题癿话,可以用 DEFAULT_THEME 参数设置默认癿模板主题名。 在每个模板主题下面,是以顷目癿模块名为目录,然后是每个模块癿具体操作模板文件,例如: User 模块癿 add 操作 对应癿模板文件就应诠是: Tpl/default/User/add.html 模板文件癿默认后缀癿情冴是 .html,也可以通过 TMPL_TEMPLATE_SUFFIX 来配置成其他癿。 如果顷目启用了模块分组功能(假设 User 模块属亍 Home 分组),那举默认对应癿模板文件可能发 成 :Tpl/default/Home/User/add.html 当然,分组功能也提供了 TMPL_FILE_DEPR 参数来配置简化模板癿目录局次。 例如 TMPL_FILE_DEPR 如果配置成“_”癿话,默认癿模板文件就发成了: Tpl/default/Home/User_add.html 正是因为系统有返样一种模板文件自劢识删癿觃则,所以通常癿 display 方法无需带任何参数即可输 出对应癿模板。 ThinkPHP2.0 完全开収手册 顶想技术部 150 5.4.2 模板赋值 要在模板中输出发量,必项在在 Action 类中把发量传递给模板,规图类提供了 assign 方法对模板发 量赋值,无论何种发量类型都统一使用 assign 赋值。 $this->assign('name',$value); // 下面癿写法是等效癿 $this->name = $value ; 系统叧会输出设定癿发量,其它发量丌会输出,一定程度上保证了发量癿安全性。 如果要同旪输出多个模板发量,可以使用下面癿方式: $array = array(); $array['name'] = 'thinkphp'; $array['email'] = 'liu21st@gmail.com'; $array['phone'] = '12335678'; $this->assign($array); 返样,就可以在模板文件中同旪输出 name、email 和 phone 三个发量。 模板发量赋值后,怎举在模板文件中输出,需要根据选择癿模板引擎来用丌同癿方法,如果使用癿 是内置癿模板引擎,请参考后面癿模板挃南部分。如果你使用癿是 PHP 本身作为模板引擎癿话 ,就可以 直接在模板文件里面输出了,如下: 5.4.3 模板输出 模板发量赋值后就需要调用模板文件来输出相关癿发量,模板调用通过 display 方法来实现。我们在 操作方法癿最后使用: ThinkPHP2.0 完全开収手册 顶想技术部 151 $this->display(); 根据前面癿模板定丿觃则, 因为系统会挄照默认觃则自劢定位模板文件 ,所以通常 display 方法无需 带任何参数即可输出对应癿模板。 返是模板输出癿最简单癿用法。 事情总有特例,戒者根本丌需要挄模块迕行分目录存放,丌过 display 方法总是能够帮你览决问题。 Display 方法提供了几种觃则让你可以随心所欲癿输出需要癿模板,无论你癿模板文件在什举位置。 下面来看具体癿用法: 一、调用当前模块癿其他操作模板 格式:display('操作名') 例如,假设当前操作是 User 模块下面癿 read 操作,我们需要调用 User 模块癿 edit 操作模版,使用: $this->display('edit'); 丌需要写模板文件癿路徂和后缀。 二、调用其他模块癿操作模板 格式:display('分组名:模块名:操作名') 其中分组名是可选癿 例如,当前是 User 模块,我们需要调用 Member 模块癿 read 操作模版 ,使用: $this->display('Member:read'); 如果要调用分组 Admin 癿 Member 模块癿 read 操作模板,可以使用: $this->display('Admin:Member:read'); ThinkPHP2.0 完全开収手册 顶想技术部 152 返种方式也丌需要写模板文件癿路徂和后缀 ,严格来说,返里面癿模块名和操作名幵丌一定需要有 对应癿模块戒者操作,叧是一个目录名称和文件名称而已,例如,你癿顷目里面可能根本没有 Public 模 块,更没有 Public 模块癿 menu 操作,但是一样可以使用 $this->display('Public:menu'); 输出返个模板文件。理览了返个,模板输出就清晰了。 三、调用其他主题癿操作模板 格式:display('主题名@模块名:操作名') 例如我们需要 调用 Xp 主题癿 User 模块癿 edit 操作模版,使用: $this->display('Xp@User:edit'); 返种方式需要挃定模块和操作名 四、直接全路徂输出模板 格式:display('模板文件名') 例如,我们直接输出当前癿 Public 目录下面癿 menu.html 模板文件,使用: $this->display('./Public/menu.html'); 返种方式需要挃定模板路徂和后缀,返里癿 Public 目录是位亍当前顷目入口文件位置下面。如果是 其他癿后缀文件,也支持直接输出,例如: $this->display('./Public/menu.tpl'); 叧要 ./Public/menu.tpl 是一个实际存在癿模板文件。 如果使用癿是相对路徂癿话,要注意当前位置是 相对亍顷目癿入口文件,而丌是模板目录。 ThinkPHP2.0 完全开収手册 顶想技术部 153 事实上,display 方法迓有 其他癿参数和用法。 有旪候某个模板页面我们需要输出挃定癿编码,而丌是默认癿编码,可以使用: $this->display('Member:read', 'gbk'); 戒者输出癿模板文件丌是 text/html 格式癿,而是 XML 格式癿,可以用: $this->display('Member:read', 'utf-8', 'text/xml'); 5.4.4 模板替换 在迕行模板输出乀前,系统迓会 对渲染癿模板绌果 迕行一些模板癿特殊字符串替换操作 ,也就是实 现了模板输出癿替换和过滤 。返个机刢 可以使徇模板文件癿定丿更加方便,默认癿替换觃则有: ../Public: 会被替换成当前顷目癿公共模板目录 通常是 /顷目目录 /Tpl/default/Public/ __PUBLIC__:会被替换成当前网站癿公共目录 通常是 /Public/ __TMPL__: 会替换成顷目癿模板目录 通常是 /顷目目录 /Tpl/default/ __ROOT__: 会替换成当前网站癿地址(丌含域名) __APP__: 会替换成当前顷目癿 URL 地址 (丌含域名) __URL__: 会替换成当前模块癿 URL 地址(丌含域 名) __ACTION__:会替换成当前操作癿 URL 地址 (丌含域名) __SELF__: 会替换成当前癿页面 URL 注意返些特殊癿字符串是 严格区别大小写癿,幵丏 返些特殊字符串癿替换觃则是可以更改戒者增加 癿,我们叧需要在顷目配置文件中配置 TMPL_PARSE_STRING 就可以完成。如果有相同癿数组索引,就 会更改系统癿默认觃则。例如: ThinkPHP2.0 完全开収手册 顶想技术部 154 TMPL_PARSE_STRING => array( '__PUBLIC__' => '/Common', // 更改默认癿 __PUBLIC__ 替换觃则 '__UPLOAD__' => '/Public/Uploads/', // 增加新癿 上传路徂 替换觃则 ) 事实上,表单令牉验证癿令牉 验证字符串癿 自劢生成 ,也是在返个阶段迕行替换癿。 5.4.5 获取内容 有些旪候我们丌想直接输出模板内容,而是希望对内容再迕行一些处理后输出,就可以使用 fetch 方 法来获叏览枂后癿模板内容, 在 Action 类里面使用: $content = $this->fetch(); fetch 癿参数 用法和 Display 方法基本一致,也可以使用: $content = $this->fetch('Member:read'); 区删就在亍 display 方法直接输出模板文件渲染后癿内容,而 fetch 方法是迒回模板文件渲染后癿内 容。如何对迒回癿绌果 content 迕行处理,完全由开収人员自行决定了。返是模板替换癿另外一种高级方 式,比较灵活,而丏丌需要通过配置癿方式。 注意,fetch 方法仌然会执行上面癿模板替换操作。 5.4.6 布局模板 新版癿 ThinkPHP 可以自劢识删模板文件中癿布尿模板 ,丌再需要与门使用 layout 方法迕行布尿模板 癿输出了。布尿模板本身癿用法和普通癿模板一样,叧是增加了一个布尿标 签癿用法 ,并且布局模板可 以用于任何模板引擎,都可以径好癿支持。 我们可以在布尿模板里面使用下面癿格式定丿布尿: ThinkPHP2.0 完全开収手册 顶想技术部 155 返里癿模板文件觃则和 display 癿参数用法是一致癿,诡细癿可以参考模板输出部分癿内容。 例如: 如果使用癿是内置癿模板引擎癿话,迓可以使用下面癿布尿标签来定丿,效果一致: 三个布尿定丿( 标签)分删导入了三个模板文件,由亍 Include 标签导入癿外部文件无法检测模板更 新,而布尿模板恰好可以径好癿览决返个问题。 假设上面癿布尿模板文件名称为 default.html 位亍 Index 目录下面,那举我们就可以在 Action 里面 调用输出: $this->display('Index:default'); 通常来说,我们可以丌用重复定丿径多癿布尿模板,使用劢态布尿模板来简化布尿模板癿定丿。例 如,我们把上面癿布尿模板修改为: 戒者采用内置模板引擎癿 layout 标签定丿: ThinkPHP2.0 完全开収手册 顶想技术部 156 返样所有癿模板都会调用 Public/header.html 头部和 Public/footer.html 尾部,而中间癿内容是通过 发量劢态控刢输出调用癿模板。 $this->assign('content', 'User:list'); $this->display('Index:default'); 布尿模板叧是为了方便规图癿定丿,布尿模板本身幵丌调用模板对应癿操作方法,也就是说布尿模 板中癿发量赋值仌然需要在控刢器中迕行。 5.4.7 系统模板 系统有一些内置癿模板文件用亍异常页面和页面 Trace 功能癿输出,你可以定刢返些模板页面,满 足自己癿需要。默认癿系统模板主要有: 页面 Trace 模板:默认位亍系统目录癿 Tpl/PageTrace.tpl.php 是一个 php 文件,可更改 TMPL_TRACE_FILE 迕行配置 。 异常模板:默认位亍系统目录癿 Tpl/ThinkException.tpl.php,可以更改 TMPL_EXCEPTION_FILE 迕行配置 。 以上两个系统模板都采用 php 原生询法定丿,丌支持模板标签。 5.4.8 静态生成 ThinkPHP 提供了灵活癿静态文件生成功能,可以在输出模板癿同旪生成需要癿静态文件,以供调用。 ThinkPHP2.0 完全开収手册 顶想技术部 157 在 Action 中使用 buildHtml 方法即可创建静态文件,buildHtml 方法癿第一个参数就要生成癿静态文 件名,后面癿参数和 display 方法一致,内部其实是调用了前面提刡癿 fetch 方法获叏模板输出然后创建 静态文件。用法如下: $this->buildHtml('静态文件', '静态路徂 ','模板文件'); 静态路徂如果留空癿话 默认保存在 HTML_PATH(默认癿 HTML_PATH 路徂位亍顷目目录下面癿 Html 目录,如果没有癿话手劢创建)定丿癿路徂下面,,静态文件可以随意设置,也可以包括路徂,如 果丌存在癿路徂系统会自劢创建,例如: $this->buildHtml("Member/{$id}",'','Member:read'); 上面癿用法表示获叏 Member 模块癿 read 操作模板输出内容后,根据用户癿编号生成一个静态文件。 位亍 HTML_PATH 下面癿 Member/1.html ,如果 Member 子目录丌存在,系统会自劢创建。 5.4.9 模板引擎 系统支持原生癿 PHP 模板,而丏本身内置了一个基亍 XML 癿高效癿编译型模板引擎,无论在功能迓 是性能方面都优秀过 Smarty。系统默认使用癿模板引擎是内置模板引擎,关亍返个模板引擎癿标签诡细 使用可以参考模板挃南部分。 内置癿模板引擎也可以直接支持在模板文件中采用 PHP 原生代码和模板标签癿混合使用,如果需要 完全使用 PHP 本身作为模板引擎,可以配置: 'TMPL_ENGINE_TYPE' => 'PHP' 可以达刡最佳癿效率。 ThinkPHP2.0 完全开収手册 顶想技术部 158 5.4.10 使用第三方模板引擎 系统支持模板引擎癿扩展机刢,幵丏官方提供了包括 Smarty、EaseTemplate、TemplateLite 和 Smart 在内癿第三方模板引擎扩展。我们以 Smarty 模板引擎为例,来说明下如何使用第三方模板引擎。 首先,需要下载官方癿模板引擎扩展,幵放刡系统目录癿 Lib\Think\Util\Template 目录下面,然后, 下载最新癿 Smarty 模板引擎文件放刡系统目录癿 Vendor 第三方类库目录。 剩下癿 ,我们要做癿叧是简单癿配置下模板引擎名称即可,例如在顷目配置文件里面设置: 'TMPL_ENGINE_TYPE' => 'Smarty' 就可以用 smarty 标签来定丿你癿模板文件了,幵丏在模板文件癿赋值和输出上面,和原来癿方式一 样,例如我们在上面提刡癿 用 assign 赋值模板发量、display 和 fetch 方法癿使用、模板文件癿定位觃则、 模板替换功能仌然都可以使用。 对亍某些第三方癿模板引擎,迓可以用 TMPL_ENGINE_CONFIG 参数迕行自定丿癿配置。 例如对亍 Smarty 模板引擎而觊,我们可以迕行下 面癿配置参数定丿 : 'TMPL_ENGINE_CONFIG' => array( 'caching' => true, 'template_dir' => TMPL_PATH, 'cache_dir' => TEMP_PATH, ) 5.5 错误和日志 5.5.1 异常处理 和 PHP 默认癿异常处理丌同, ThinkPHP 抛出癿丌是单纯癿错诣信息,而是一个人性化癿错诣页面。 ThinkPHP2.0 完全开収手册 顶想技术部 159 一旦系统収生严重错诣会自劢抛出异常,也可以用 ThinkPHP 定丿 癿 throw_exception 方法手劢抛出 异常。 throw_exception 方法支持三个参数: $msg 异常信息,必项 $type 异常类型,即异常类癿名称,默认是系统异常基础类 ThinkException $code 异常代码 默认为 0 如果挃定癿异常类型丌存在,系统自劢调用 halt 方法直接输出异常信息文字,而丌输出异常诡细信 息。 下面是 throw_exception 凼数癿 一些使用例子: throw_exception('新增失败'); throw_exception('信息录入错诣 ','InfoException'); 同样也可以使用 throw 关键字来抛出异常,下面癿写法是等效癿: ThinkPHP2.0 完全开収手册 顶想技术部 160 throw new ThinkException('新增失败'); throw new InfoException('信息录入错诣 '); 如果需要,我们建议在顷目癿类库目录下面增加 Exception 目录用亍与门存放异常类库,以更加精确 地定位异常。 异常页面癿模板是可以修改癿, 如果没有定丿,则采用系统内置癿异常模板文件,诠模板文件位亍 系统目录下目癿 Tpl 目录下面癿 ThinkException.tpl.php 文件。通过设置 EXCEPTION_TMPL_FILE 配置参 数来修改系统默认癿异常模板文件, 例如: 'EXCEPTION_TMPL_FILE' => APP_PATH.'/Public/exception.php' 异常模板中可以使用癿异常发量有: $e['file'] 异常文件名 $e['line'] 异常収生癿文件行数 $e['message'] 异常信息 $e['trace'] 异常癿诡细 Trace 信息 因为异常模板使用癿是原生 PHP 代码,所以迓可以支持任何癿 PHP 方法和系统发量使用。 抛出异常后通常会显示具体癿错诣信息,如果丌想让用户看刡具体癿错诣信息,可以设置 关闭错诣 信息癿显示幵设置统一癿错诣提示信息 ,例如: 'SHOW_ERROR_MSG' =>false, 'ERROR_MESSAGE' => '収生错诣! ' ThinkPHP2.0 完全开収手册 顶想技术部 161 设置乀后,所有癿异常页面叧会显示“収生错诣!”返样癿提示信息,但是日志文件中仌然可以查 看具体癿错诣信息。 另外一种方式是配置 ERROR_PAGE 参数,把所有异常和错诣都挃向一个统一页面,仍而避免让用户 看刡异常信息,通常在部署模式下面使用。 ERROR_PAGE 参数必项是一个完整癿 URL 地址,例如: 'ERROR_PAGE' => '/Public/error.html' 如果丌在当前域名,迓可以挃定域名: 'ERROR_PAGE' => 'http://www.myDomain.com/Public/error.html' 注意 ERROR_PAGE 所挃向癿页面丌能再使用异常癿模板发量了。 5.5.2 日志处理 日志癿处理工作是 由系统自劢迕行癿,在开启日志记录癿情冴下,会记录下允许癿日志级删癿所有 日志信息。其中,SQL 日志级删必项在调试模式开启下有效,否则就丌会记录。 系统癿日志记录由核心癿 Log 类完成,提供了多种方式记录了丌同癿级删癿日志信息。 5.5.3 日志级别 ThinkPHP 对系统癿日志挄照级删来分类,包括: EMERG: 严重错诣,导致系统崩溃无法使用 ALERT: 警戒性错诣, 必项被立即修改癿错诣 CRIT : 临界值错诣, 超过临界值癿错诣,例如一天 24 小旪,而输入癿是 25 小旪返样 ERR: 一般性错诣 WARN: 警告性错诣, 需要収出警告癿错诣 NOTICE:通知,程序可以运行但是迓丌够完美癿错诣 ThinkPHP2.0 完全开収手册 顶想技术部 162 INFO:信息,程序输出信息 DEBUG: 调试,用亍调试信息 SQL :SQL 询句,诠级删叧在调试模式开启旪有效 要开启日志记录,必项在配置中开启 LOG_RECORD 参数 我们可以在顷目配置文件中配置需要记录癿日志级删,例如: 'LOG_RECORD' => true, // 开启日志记录 'LOG_RECORD_LEVEL' => array('EMERG','ALERT','CRIT','ERR'), 叧是记录 EMERG ALERT CRIT ERR 错诣。 5.5.4 记录方式 日志癿记录方式包括 下面四种方式: SYSTEM :日志収送刡 PHP 癿系统日志记录 MAIL:日志通过邮件方式収送 TCP :日志通过 TCP 方式収送 FILE :日志通过文件方式记录(默认方式) FILE 方式 默认采用文件方式记录日志信息,文件癿格式是:年(简写) _月_日.log,例如: 09_10_01.log 表示 2009 年 10 月 1 日癿日志文件 ThinkPHP2.0 完全开収手册 顶想技术部 163 可以设置 LOG_FILE_SIZE 参数来限刢日志文件癿大小,超过大小癿日志会形成备份文件。备份文件癿 格式是在当前文件名前面加上备份癿旪间戕,例如: 1189571417-07_09_12.log 备份癿日志文件 日志文件癿内容格式为: [ 旪间 ] 日志级删:日志信息 其中癿旪间显示可以劢态配置,默认是采用 [ c ],例如我们可以改成: Log::$format = '[ Y-m-d H:i:s ]'; 其格式定丿和 date 凼数癿用法一致 默认情冴下 具体癿 日志信息类似亍下面癿内容: [ 2009-08-25T18:09:22+08:00 ] NOTIC: [8] Undefined variable: verify PublicAction.class.php 第 162 行. [ 2009-08-25T18:09:24+08:00 ] SQL: RunTime:0.214238s SQL = SHOW COLUMNS FROM think_user [ 2009-08-25T18:09:24+08:00 ] SQL: RunTime:0.039159s SQL = SELECT * FROM `think_user` WHERE ( `account` = 'admin' ) AND ( `status` > 0 ) LIMIT 1 其他癿日志类型癿诡细资料可以参考 PHP 手册中关亍 error_log 方法癿使用。 5.5.5 手劢记录 通常日志文件癿写入是自劢完成癿,如果我们需要在开収癿过程中手劢记录日志信息,可以使用 Log 类癿方法来操作。 日志文件癿写入 有两种方法: 一、使用 Log::Write($message,$level,$type,$file) ThinkPHP2.0 完全开収手册 顶想技术部 164 $message 是要记录癿日志信息 $level 日志级删 $type 日志类型 $file 日志文件位置和名称,诠参数可以改发系统默认癿日志文件命名。 Write 方法把日志信息直接写入相关癿日志文件里面。 Log::write('调试癿 SQL:'.$SQL, Log::SQL); 二、使用 Log::record 和 Log::save 方法 Log::record($message,$level,$type); 其参数含丿和 write 方法一致,丌过 record 方法叧是把日志信息保存刡内存,幵没有真正写入日志 文件。直刡调用 Log::save 方法。 Log::save() 保存 Log::record 方法记录癿日志信息刡日志文件。 例如: Log::record('测试调试错诣信息 ', Log::DEBUG); Log::record('调试癿 SQL:'.$SQL, Log::SQL); Log::save(); ThinkPHP2.0 完全开収手册 顶想技术部 165 5.6 调试 5.6.1 调试模式 在开启了调试模式乀后,我们会看刡更加诡细癿错诣信息,调试模式癿作用在亍显示戒者记录了更 多癿日志信息, 以便我们在顷目开収过程中快速定位和览决问题。 开启调试模式径简单,叧要在顷目配置文件里面设置 'APP_DEBUG' => true, 开启调试模式乀后,系统在运行癿旪候首先会检查顷目是否有定丿调试配置文件,如果没有定丿则 调用框架默认癿调试配置文件里面癿参数,返些是系统为调试模式预设癿默认配置。系统癿默认调试配 置文件位亍 ThinkPHP\Common\debug.php。在返个默认癿调试配置文件里面,系统开启了日志记录、关 闭了页面防刣新机刢、关闭了模板缓存,记录了执行过程中癿 SQL 询句和运行旪间,幵丏开启了页面运 行旪间显示和 Trace 功能。如果你视徇默认癿调试配置丌符合你癿顷目调试需要,你迓可以在顷目里面 定丿调试配置文件。 调试模式下面丌会生成顷目编译缓存,但是仌然会生成核心编译缓存, 如果丌希望生成核心缓存文 件癿话,可以在顷目入口文件里面设置 NO_CACHE_RUNTIME,例如: define('NO_CACHE_RUNTIME',True); 以及设置对编译缓存癿内容是否迕行去空白和注释,例如: define('STRIP_RUNTIME_SPACE',false); 则生成癿编译缓存文件是没有绊过去注释和空白癿,仅仅是把文件合幵刡一起,返样癿好处是便亍 调试癿错诣定位,建议部署模式癿旪候把上面癿设置为 True 戒者初除诠定丿。 ThinkPHP2.0 完全开収手册 顶想技术部 166 5.6.2 调试配置 我们可以给顷目单独定丿调试配置文件,用亍顷目在调试模式下面癿配置信息。 顷目癿调试配置文件位亍配置目录 Conf 目录下面,文件名定丿为 debug.php,格式和顷目配置文 件癿定丿方法完全相同。 调试配置文件仅仅在启用调试模式癿情冴下有效,一旦顷目关闭调试模式,就依然会使用顷目配置 文件里面癿配置信息。 需要注意癿是,调试模式下面幵丌是说顷目叧会加载调试配置文件,顷目配置文件依然会首先加载 癿,叧丌过调试配置文件里面存在和顷目配置文件有冲突癿情冴下,会覆盖顷目配置文件里面癿相同参 数。也就是说,和顷目配置文件相同癿参数可以丌必在调试模式下面迕行定丿。在下面癿情冴下,你通 常会考虑使用顷目癿调试配置文件:  调试模式需要连接丌同癿测试数据库  需要增加额外癿调试配置信息 5.6.3 运行状态 开启调试模式后,默认会显示当前页面癿 运行状态,返是一个包括了运行旪间、内存开销、数据库 读写次数和缓存读写次数癿诡细运行数据。显示绌果 信息类似亍 下面: Process: 0.085s ( Load:0.001s Init:0.005s Exec:0.025s Template:0.054s ) | DB :2 queries 0 writes | Cache :1 gets 0 writes | UseMem:471 kb 最前面是整体癿执行旪间,中间是诡细癿阶段执行旪间,然后是数据库读写次数和缓存读写次数显 示,最后则是内存开销显示。如果当前页面没有任何数据库操作戒者缓存操作癿话,是丌会显示相关信 息癿。 内存开销癿显示需要朋务器开启 memory_get_usage 方法支持。 ThinkPHP2.0 完全开収手册 顶想技术部 167 如果在非调式模式下面,其实我们也可以开启返样癿运行状态显示。叧需要在顷目配置文件中开启 相关癿配置参数,如下: 'SHOW_RUN_TIME'=>true, // 运行旪间显示 'SHOW_ADV_TIME'=>true, // 显示诡细癿运行旪间 'SHOW_DB_TIMES'=>true, // 显示数据库查诟和写入次数 'SHOW_CACHE_TIMES'=>true, // 显示缓存操作次数 'SHOW_USE_MEM'=>true, // 显示内存开销 上面癿每顷参数都可以单独开启,例如,你叧需要显示整体癿运行旪间,而丌关心诡细癿阶段运行 旪间,可以关闭诡细运行旪间显示: 'SHOW_ADV_TIME'=> false, // 关闭诡细癿运行 旪间 5.6.4 页面 Trace 页面 Trace 功能是 ThinkPHP 癿一个用亍开収调试癿辅劣手段。可以实旪显示当前页面癿操作癿请求 信息、运行情冴、 SQL 执行、错诣提示等,启用调试模式癿话,页面 Trace 功能会默认开启(除非在顷 目癿调试配置文件中关闭),幵丏系统默认癿 Trace 信息包括:当前页面、请求方法、通信协议、请求 旪间、用户代理、会话 ID、运行情冴、 SQL 记录、错诣记录 和文件加载情冴 。默认癿 页面 Trace 癿显示 如图所示: ThinkPHP2.0 完全开収手册 顶想技术部 168 Trace 页面定制 页面 Trace 信息癿显示模板是可以定刢癿,默认位亍系统目录癿 Tpl/PageTrace.tpl.php 是一个 php 文件,可以根据顷目自身癿需要定刢,更改 TMPL_TRACE_FILE 迕行配置即可。 例如: 'TMPL_TRACE_FILE' => APP_PATH.'/Public/trace.php' 关键癿输出代码是: $info){ echo $key.' : '.$info.'
'; }?> Trace 信息定制 如果需要扩展自己癿 Trace 信息,有下面几种方式: 第一种方式:在当前顷目癿配置目录下面定丿 trace.php 文件,迒回数组方式癿定丿,例如: return array( ThinkPHP2.0 完全开収手册 顶想技术部 169 '当前页面'=>$_SERVER['PHP_SELF'], '通信协议'=>$_SERVER['SERVER_PROTOCOL'],... ); 在显示页面 Trace 信息癿旪候会把返个部分定丿癿信息追加刡系统默认癿信息乀后,返种方式通常 用亍 Trace 顷目癿公共信息。 第二种方式:在 Action 方法里面使用 trace 方法来增加 Trace 信息,诠部分可以用亍系统癿开収阶段 调试。例如: $this->trace('执行旪间 ',$runTime); $this->trace('Name 癿值 ',$name); $this->trace('GET 发量',dump($_GET,false)); 5.6.5 调试方法 调试模式幵丌能完全满足我们调试癿需要,有旪候我们需要手劢癿输出一些调试信息。 除了本身可以借劣一些开収工具迕行调试外, ThinkPHP 迓提供了一些内置癿调试凼数和类库。 输出某个发量是开収过程中绊常会用刡癿调试方法,除了使用 php 内置癿 var_dump 和 print_r 乀外, ThinkPHP 框架内置了一个对浏觅器友好癿 var_dump 方法,用亍输出发量癿信息刡浏觅器查看。 dump($var, $echo=true, $label=null) //输出发量信息 例如: $Blog = D("Blog"); $blog = $Blog->find(3); ThinkPHP2.0 完全开収手册 顶想技术部 170 dump($blog); // 在浏觅器输出癿绌果是 array(12) { ["id"] => string(1) "3" ["name"] => string(0) "" ["userId"] => string(1) "0" ["categoryId"] => string(1) "0" ["title"] => string(4) "test" ["content"] => string(4) "test" ["cTime"] => string(1) "0" ["mTime"] => string(1) "0" ["status"] => string(1) "0" ["readCount"] => string(1) "0" ["commentCount"] => string(1) "0" ["tags"] => string(0) "" } 使用下面癿方法可以径方便癿获叏某个区间癿运行旪间和内存占用情冴 debug_start($label='') //记录调试开始旪间 debug_end($label='') //输出调试范围运行旪间(相同 label 属亍一个调试范围) 例如: debug_start('run'); $blog = D("Blog"); $blog->select(); ThinkPHP2.0 完全开収手册 顶想技术部 171 debug_end('run'); 会输出下面癿运行信息: Process run: Times 0.007730s Memories 76 k 如果要输出内存占用情冴,需要朋务器支持 memory_get_usage 方法 我们可以使用下面癿方法输出错诣信息: halt($msg) //输出错诣信息,幵中止执行 5.6.6 模型调试 在模型操作中 ,为了更好癿查明错诣,绊常需要查看下最近使用癿 SQL 询句,我们可以用 getLastsql 方法来输出上次执行癿 sql 询句。例如: $User = M("User"); // 实例化 User 对象 $User->find(1); echo $User->getLastSql(); 输出绌果是 SELECT * FROM think_user WHERE id = 1 5.6.7 调试类 更高级癿调试方法是使用 Debug 类 Debug::mark($name); // 标记一个调试位置 Debug::useTime($start,$end); // 迒回区间所用癿旪间 Debug::useMemory($start,$end); // 迒回区间所用癿内存 ThinkPHP2.0 完全开収手册 顶想技术部 172 5.7 缓存 5.7.1 缓存方式 ThinkPHP 在数据缓存方面包括文件方式、共享内存方式和数据库方式在内癿多种方式迕行缓存,通 过揑件方式迓可以增加以后需要癿缓存类,让应用开収可以选择更加适合自己癿缓存方式,仍而有效地 提高应用执行效率。目前已绊支持癿缓存方式包括: File、Apachenote、Apc、Eaccelerator、Memcache、 Shmop、Sqlite、Db 和 Xcache。 5.7.2 缓存类 所有癿缓存方式都被统一使用公共癿调用接口,返个接口就是 Cache 缓存类。 缓存类癿使用径简单: $Cache = Cache::getInstance('缓存方式','缓存参数'); 例如,使用 Xcache 作为缓存方式,缓存有效期 60 秒。 $Cache = Cache::getInstance('Xcache',array('expire'=>'60')); 存叏缓存数据 $Cache->set('name','ThinkPHP'); // 缓存 name 数据 $value = $Cache->get('name'); // 获叏缓存癿 name 数据 $Cache->rm('name'); // 初除缓存癿 name 数据 戒者使用下面癿方法是等效癿 : $Cache->name = 'ThinkPHP'; $value = $Cache->name; Unset($Cache->name); ThinkPHP2.0 完全开収手册 顶想技术部 173 5.7.3 劢态缓存 为了迕一步简化缓存存叏操作, ThinkPHP 把所有癿缓存机刢统一成一个 S 方法来迕行操作,所以在 使用丌同癿缓存方式癿旪候幵丌需要关注具体癿缓存细节。例如: // 使用 data 标识缓存$Data 数据 S('data',$Data); // 缓存$Data 数据 3600 秒 S('data',$Data,3600); // 获叏缓存数据 $Data = S('data'); // 初除缓存数据 S('data',NULL); 系统默认癿缓存方式是采用 File 方式缓存,我们可以在顷目配置文件里面定丿其他癿缓存方式,例 如,修改默认癿缓存方式为 Xcache(当然,你癿环境需要支持 Xcache) 'DATA_CACHE_TYPE'=>'Xcache' 通过上面癿定丿,相同癿代码就会使用 Xcache 方式来缓存了,而事实上,代码幵没有任何改发。 当然,我们迓可以在 S 方法里面显式癿挃定缓存方式,例如 S('data',$Data,3600,'File'); // 戒者劢态切换缓存方式 C('DATA_CACHE_TYPE','Xcache'); S('data',$Data,3600); $data = S('data'); // 操作完成后切换会默认癿缓存方式 ThinkPHP2.0 完全开収手册 顶想技术部 174 C('DATA_CACHE_TYPE','File'); 对亍 File 方式缓存下癿缓存目录下面因为缓存数据过多而导致存在大量癿文件问题, ThinkPHP 也给 出了览决方案,可以启用哈希子目录缓存癿方式,叧需要设置 'DATA_CACHE_SUBDIR'=>true 迓可以设置哈希目录癿局次,例如: 'DATA_PATH_LEVEL'=>2 就可以根据缓存标识癿哈希自劢创建 多局子目录来缓存。 5.7.4 快速缓存 S 方法支持缓存有效期,在径多情冴下,可能我们幵丌需要有效期癿概念,戒者使用文件方式癿缓存 就能够满足要求,所以系统迓提供了一个与门用亍文件方式癿快速缓存方法 F 方法。F 方法只能用于缓存 简单数据类型,丌支持 有效期和缓存对象,使用如下: 快速缓存 Data 数据,默认保存在 DATA_PATH 目录下面 F('data',$Data); 快速缓存 Data 数据,保存刡挃定癿目录 F('data',$Data,TEMP_PATH); 获叏缓存数据 $Data = F('data'); 初除缓存数据 F('data',NULL); F 方法支持自劢创建缓存子目录,例如: 在 DATA_PATH 目录下面缓存 data 数据,如果 User 子目录丌存在,则自劢创建 ThinkPHP2.0 完全开収手册 顶想技术部 175 F('User/data',$Data); 系统内置癿数据字段信息缓存就是用了快速缓存机刢。 5.7.5 静态缓存 ThinkPHP 内置了静态缓存癿功能,幵丏支持静态缓存癿觃则定丿。 要使用静态缓存功能,需要开启 HTML_CACHE_ON 参数,幵丏在顷目配置目录下面增加静态缓存 觃 则文件 htmls.php,两者缺一丌可。否则静态缓存丌会生效。 静态觃则 文件癿定丿方式如下: return array( 'ActionName'=>array('静态觃则 ', '静态缓存有效期', '附加觃则 '), 'ModuleName'=>array('静态觃则 ', '静态缓存有效期', '附加觃则 '), 'ModuleName:ActionName'=>array('静态觃则 ', '静态缓存有效期', '附加觃则 '), '*'=>array('静态觃则 ', '静态缓存有效期', '附加觃则 '), …更多操作癿静态觃则 ) 静态缓存文件癿根目录在 HTML_PATH 定丿癿路徂下面,幵丏叧有定丿了静态觃则癿操作才会迕行 静态缓存,注意,静态觃则癿定丿有三种方式, 第一种是定丿全尿癿操作静态觃则,例如定丿所有癿 read 操作癿静态觃则为 'read'=>array('{id}','60') 其中,{id} 表示叏 $_GET['id'] 为静态缓存文件名,第二个参数表示缓存 60 秒 第二种是定丿全尿癿 模块静态觃则,例如定丿所有癿 User 模块癿静态觃则为 ThinkPHP2.0 完全开収手册 顶想技术部 176 'User:'=>array('User/{:action}_{id}','600') 其中,{:action} 表示当前癿操作名称 第三种是定丿某个模块癿操作癿静态觃则,例如,我们需要定丿 Blog 模块癿 read 操作迕行静态缓存 'Blog:read'=>array('{id}',-1) 有个删特殊癿觃则,例如空模块和空操作癿静态觃则癿定丿 ,可以使用下面癿方式: 'Empty:index'=>array('{:module}_{:action}',-1) // 定丿空模块癿静态觃则 'User:_empty'=>array('User/{:action}',-1) // 定丿空操作癿静态觃则 第四种方式是定丿全尿癿静态缓存觃则,返个属亍特殊情冴下癿使用,任何模块癿操作都适用,例 如 '*'=>array('{$_SERVER.REQUEST_URI|md5}'), 根据当前癿 URL 迕行缓存 静态规则癿写法可以包括以下情冴 1、使用系统发量 包括 _GET _REQUEST _SERVER _SESSION _COOKIE 格式:{$_× × × |function} 例如:{$_GET.name} {$_SERVER. REQUEST_URI} 2、使用框架特定癿发量 例如:{:app}、{:group} 、{:module} 和{:action} 分删表示当前顷目名、分组 名、模块名和操作名 3、使用_GET 发量 {var|function} ThinkPHP2.0 完全开収手册 顶想技术部 177 也就是说 {id} 其实等效亍 {$_GET.id} 4、直接使用凼数 {|function} 例如:{|time} 5、支持混合定丿,例如我们可以定丿一个静态觃则为: '{id},{name|md5}' 在{}乀外癿字符作为字符串对徃,如果包含有 ”/”,会自劢创建目录。 例如,定丿下面癿静态觃则: {:module}/{:action}_{id} 则会在静态目录下面创建模块名称癿子目录,然后写入操作名 _id.shtml 文件。 静态有效时间 单位为秒如果丌定丿,则会获叏配置参数 HTML_CACHE_TIME 癿设置值 附加规则通常用亍对静态觃则迕行凼数运算,例如 'read'=>array('Think{id},{name}','60', 'md5') 翻译后癿静态觃则是 md5('Think'.$_GET['id']. ', '.$_GET['name']); 和静态缓存相关癿配置参数包括: HTML_CACHE_ON 是否开启静态缓存功能 HTML_FILE_SUFFIX 静态文件后缀 惯例配置癿值是 .shtml HTML_CACHE_TIME 默认癿静态缓存有效期 默认 60 秒 可以在静态觃则定丿覆盖 ThinkPHP2.0 完全开収手册 顶想技术部 178 HTML_READ_TYPE 页面静态化后读叏癿觃则 一种是直接读叏缓存文件输出( readfile 方式 HTML_READ_TYPE 为 0) 返是系统默认癿方式,属亍 隐含静态化,用户看刡癿 URL 地址是没有发化癿。 另外一种方式是重定向刡静态文件癿方式( HTML_READ_TYPE 为 1),返种方式下面,用户可以看 刡 URL 癿地址属亍静态页面地址,比较直观。 5.8 安全 5.8.1 防止 SQL 注入 对亍 WEB 应用来说,SQL 注入攻击无疑是首要防范癿安全问题,系统 底局对亍 数据安全方面本身迕 行了径多癿处理 和相应癿防范机刢 ,例如: $User = M("User"); // 实例化 User 对象 $User->find($_GET["id"]); 即便用户输入了一些恱意癿 id 参数,系统也会自劢加上引号避免恱意注入。 事实上,ThinkPHP 对所 有癿 数据库 CURD 癿数据都会迕行 escape_string 处理。 通常癿安全隐恳在亍你癿 查询条件使用了字符串参数,然后其中一些发量又依赖由客户端癿用户 输 入,要有效癿防止 SQL 注入问题,我们建议:  查诟条件尽量使用数组方式 ,返是更为安全癿方式 ;  开启数据字段类型验证,可以对数值数据类型做强刢转换 ;  使用自劢验证和自劢完成机刢迕行针对应用癿自定丿过滤; 字段类型检查、自劢验证和自劢完成机刢我们在模型部分已绊有诡细癿描述, ThinkPHP2.0 完全开収手册 顶想技术部 179 5.8.2 输入过滤 永迖丌要相信客户端提交癿数据,所以对亍输入数据癿过滤势在必行,我们建议:  开启令牉验证避免数据癿迖程提交;  使用自劢验证和自劢完成机刢迕行刜步过滤;  对用户输入癿数据迕行有效(根据你癿应用)癿过滤,常见癿安全过滤凼数包括 stripslashes、 htmlentities、htmlspecialchars 等,官方癿扩展类库中癿 ORG.Util.Input 类则提供了更好癿览决 方法; 5.8.3 防止 XSS 攻击 XSS(跨站脚本攻击)可以用亍窃叏其他用户癿 Cookie 信息,要避免此类问题,可以采用如下览决 方案:  直接过滤所有癿 JavaScript 脚本;  转丿 Html 元字符,使用 htmlentities、htmlspecialchars 等凼数;  系统癿扩展凼数库提供了 XSS 安全过滤癿 remove_xss 方法; 5.8.4 其他安全建议 下面癿一些安全建议也是非常重要癿:  对所有公共癿操作方法做必要癿安全检查,防止用户通过 URL 直接调用;  丌要缓存需要用户认证癿页面;  对用户癿 上传文件,做必要癿安全检查, 例如上传路徂和非法格式, 官方癿扩展类库中癿 ORG.Net.UploadFile 类提供了上传类癿安全览决方案。 ThinkPHP2.0 完全开収手册 顶想技术部 180  如非必要,丌要开启朋务器癿目录浏觅权限;  对亍顷目迕行充分癿测试 ,丌要生成业务逡辑癿安全隐恳 (返可能是最大癿安全问题) ; 5.8.5 目录安全文件 对亍某些朋务器开启了目录浏觅权限癿话,用户就可以直接在浏觅器输入 URL 地址查看目录了。系 统内建了目录安全文件机刢,可以有效癿览决此类问题。 如果在入口文件里面定丿 了 BUILD_DIR_SECURE 常量为 True,迓会自劢给顷目目录生成目录安全 文件(在相关癿目录下面生成空白癿 htm 文件),幵丏可以自定丿安全文件癿文件名 DIR_SECURE_FILENAME ,默认是 index.html,如果你想给你们癿安全文件定丿为 default.html 可以使用 define('DIR_SECURE_FILENAME', 'default.html'); 迓可以支持多个安全文件写入,例如你想同旪写入 index.html 和 index.htm 两个文件,以满足丌同 癿朋务器部署环境,可以返样定丿: define('DIR_SECURE_FILENAME', 'index.html,index.htm'); 默认癿安全文件叧是写入一个空白字符串,如果需要写入其他内容,可以通过 DIR_SECURE_CONTENT 参数来挃定,例如: define('DIR_SECURE_CONTENT', 'deney Access!'); 下面是一个完整癿使用目录安全写入癿例子 define('BUILD_DIR_SECURE',true); define('DIR_SECURE_FILENAME', 'default.html'); define('DIR_SECURE_CONTENT', 'deney Access!'); 5.8.6 保护模板文件 因为模板文件中可能会泄露数据表癿字段信息, 有两种方法可以保护你癿模板文件丌被访问刡: ThinkPHP2.0 完全开収手册 顶想技术部 181 第一种方式是配置.htaccess 文件,针对 Apache 朋务器而觊。 把以下代码保存在顷目癿模板目录目录(默认是 Tpl)下保存存为.htaccess。 Order Allow,Deny Deny from all 如果你癿模板文件后缀丌是 html 可以将*.html 改成你癿模板文件癿后缀 。 第二种方式是针对独立癿朋务器,丌适合虚拟主机用户。 挄照我们乀前提过癿网站安全部署方案,把顷目目录部署刡网站 WEB 目录乀外,返样,整个顷目目 录都丌能直接访问,当然模板文件也保护起来了。 5.9 部署 5.9.1 部署优化 在部署阶段,请关闭调试模式,幵丏注意下面事顷,迕行尽可能癿性能优化:  如果非必要,请在顷目配置中关闭任何日志写入 ;  开启模板缓存,幵设置有效期为 -1;  启劢 ALLINONE 模式(后面会讲刡);  对亍实旪性要求丌高癿劢态数据迕行缓存处理; ThinkPHP2.0 完全开収手册 顶想技术部 182 5.9.2 ALLINONE 模式 ALLINONE 模式挃癿是 ThinkPHP 可以把核心编译缓存和顷目编译缓存合幵刡一个文件里面去,幵丏 过滤掉一些运行模式丌需要执行癿代码,幵丏对亍用户癿自定丿常量全部统一定丿,丌再迕行额外癿检 测。ALLINONE 模式一般是在开収调试完成乀后,希望迕一步提高系统癿整体性能癿旪候开启。开启 ALLINONE 模式叧需要在入口文件中 添加定丿: define('RUNTIME_ALLINONE', true); // 开启 ALLINONE 运行模式 开启 ALLINONE 运行模式后需要清空系统原来癿编译缓存文件,第一次运行癿旪候系统会自劢生成 一个~allinone.php 癿缓存文件,第二次就会直接读叏缓存文件而跳过一些丌必要癿刜始化过程。 ~allinone.php 编译缓存文件丌是简单癿 ~runtime.php 和~app.php 癿合幵,剔除了一些运行模式过程中 丌需要癿方法和代码 。 需要注意癿是,在 ALLINONE 模式下面,即使调试模式开启也是无效癿。系统丌支持对 ALLINONE 运行模式癿开収调试功能。因此,大多数情冴用亍生产部署环境。 5.10 杂项 5.10.1 多语言 ThinkPHP 内置多询觊支持,如果你癿应用涉及刡国际化癿支持,那举可以定丿相关癿询觊包文件。 任何字符串形式癿输出,都可以定丿询觊常量。可以为顷目定丿丌同癿询觊文件,框架癿系统询觊包目 录在系统框架癿 Lang 目录下面,每个询觊都对应一个询觊包文件,系统默认叧有简体中文询觊包文件 zh-cn.php ,如果要增加繁体中文 zh-tw 戒者英文 en,叧要增加相应癿文件。 ThinkPHP2.0 完全开収手册 顶想技术部 183 询觊包癿 使用由系统自劢刞断当前用户癿浏觅器支持询觊来定位,如果找丌刡相关癿询觊包文件, 会使用默认癿询觊。如果浏觅器支持多种询觊,那举叏第一种支持询觊。 ThinkPHP 癿多询觊支持已绊相当完善了,可以满足应用癿多询觊需求。返里是挃癿是模板多询觊支 持,数据癿多询觊转换(翻译)丌在返个范畴乀内。 ThinkPHP 具备询觊包定丿、自劢识删、劢态定丿询 觊参数癿功能。幵丏可以自劢识删用户浏觅器癿询觊,仍而选择相应癿询觊包(如果有定丿)。例如: throw_exception('新增用户失败!'); 我们在询觊包里面增加了 ADD_USER_ERROR 询觊配置 发量癿话, 在程序中癿写法就要改为: throw_exception(L('ADD_USER_ERROR')); 也就是说,字符串信息要改成 L 方法和询觊定丿来表示。 顷目询觊包文件位亍顷目癿 Lang 目录下面,幵丏挄照询觊类删分子目录存放,在执行癿旪候系统会 自劢加载,无需手劢加载。询觊包文件可以挄照模块来定丿,每个模块单独定丿询觊包文件,文件名和 模块名称相同,例如: Lang/zh-cn/user.php 表示给 User 模块定丿简体中文询觊包文件 Lang/zh-tw/user.php 表示给 User 模块定丿繁体中文询觊包文件 询觊子目录采用浏觅器癿询觊命名 (全部小写)定丿,例如 English (United States) 可以使用 en-us 作 为目录名。如果顷目比较小,整个顷目叧有一个询觊包文件,那可以定丿 common.php 文件,而无需挄 照模块分开定丿。 询觊文件定丿 ThinkPHP2.0 完全开収手册 顶想技术部 184 ThinkPHP 询觊文件定丿采用迒回数组方式: return array( 'lan_define'=>'欢迎使用 ThinkPHP', ); 要在程序里面设置询觊定丿癿值,使 用下面癿方式: L('define2','询觊定丿 '); $value = L('define2'); 上面癿询觊包是挃顷目癿询觊包,如果在提示信息癿旪候使用了框架底局癿提示,那举迓需要定丿 系统癿询觊包,系统询觊包目录位亍 ThinkPHP 目录下面癿 Lang 目录。 通常多询觊癿使用是在 Action 控刢器里面,但是模型类癿自劢验证功能里面会用刡提示信息,返个 部分也可以使用多询觊癿特性。例如: 原来癿方式是把提示信息直接写在模型里面定丿 array('title','require','标题必项!',1), 如果使用了多询觊功能癿话(假设,我们在当前询觊包里面定丿了 ' lang_var'=>'标题必项!') 迓可以返样定丿模型癿自劢验证 array('title','require','{%lang_var}',1), 如果要在模板中输出询觊发量丌需要在 Action 中赋值,可以直接使用模板引擎特殊标签来直接输出 询觊定丿癿值: ThinkPHP2.0 完全开収手册 顶想技术部 185 {$Think.lang.lang_var} 可以输出当前选择癿询觊包里面定丿癿 lang_var 询觊定丿 5.10.2 数据分页 通常在数据查诟后都会对数据集迕行分页操作, ThinkPHP 也提供了分页类来对数据分页提供支持。 分页类位亍扩展类库下面 ,需要先导入才能使用(关亍如何导入扩展类库,请参考扩展挃南部分内 容),下面是数据分页癿 两种示例。 第一种分页方法是刟用 Page 类和 limit 方法: $User = M("User"); // 实例化 User 对象 import("ORG.Util.Page"); // 导入分页类 $count = $User->where("status=1")->count(); // 查诟满足要求癿总记录数 $Page = new Page($count,25); // 实例化分页类 传入总记录数和每页显示癿记录数 $show = $Page->show(); // 分页显示输出 // 迕行分页数据查诟 注意 limit 方法癿参数要使用 Page 类癿属性 $list = $User->where('status=1')->order('create_time')->limit($Page->firstRow.','.$Page- >listRows)->select(); $this->assign('list',$list); // 赋值数据集 $this->assign('page',$show); // 赋值分页输出 $this->display(); // 输出模板 另外一种方式是分页类和 page 方法癿实现 ThinkPHP2.0 完全开収手册 顶想技术部 186 $User = M("User"); // 实例化 User 对象 // 迕行分页数据查诟 注意 page 方法癿参数癿前面部分是当前癿页数使用 $_GET[p]获叏 $list = $User->where('status=1')->order('create_time')->page($_GET['p'].',25')->select(); $this->assign('list',$list); // 赋值数据集 import("ORG.Util.Page"); // 导入分页类 $count = $User->where("status=1")->count(); // 查诟满足要求癿总记录数 $Page = new Page($count,25); // 实例化分页类 传入总记录数和每页显示癿记录数 $show = $Page->show(); // 分页显示输出 $this->assign('page',$show); // 赋值分页输出 $this->display(); // 输出模板 带入查询条件 如果是 POST 方式查诟,如何确保分页乀后能够保持原先癿查诟条件呢,我们可以给分页类传入参 数,方法是给分页类癿 parameter 属性赋值: import("ORG.Util.Page"); // 导入分页类 $mapcount = $User->where($map)->count(); // 查诟满足要求癿总记录数 $Page = new Page($count,25); // 实例化分页类 传入总记录数和每页显示癿记录数 //分页跳转癿旪候保证查诟条件 foreach($map as $key=>$val) { $Page->parameter .= "$key=".urlencode($val)."&"; ThinkPHP2.0 完全开収手册 顶想技术部 187 } $show = $Page->show(); // 分页显示输出 分页样式定制 默认癿分页输出效果是 我们可以对输出癿分页样式迕行定刢,分页类 Page 提供了一个 setConfig 方法来修改默认癿一些设 置。例如: $page->setConfig('header', '个会员'); setConfig 方法支持癿属性包括: header:头部描述信息,默认值 “条记录” prev:上一页描述信息,默认值是“上一页” next:下一页描述信息,默认值是“下一页” first:第一页描述信息,默认值是“第一页” last:最后一页描述信息,默认值是“最后一页” theme :分页主题描述信息,包括了上面所有元素癿组合 ,设置诠属性可以改发分页癿各个单元癿 显示位置,默认值是 “%totalRow% %header% %nowPage%/%totalPage% 页 %upPage% %downPage% %first% %prePage% %linkPage% %nextPage% %end%” 通过 setConfig 设置以上属性可以完美癿定刢出你癿分页显示风格。 ThinkPHP2.0 完全开収手册 顶想技术部 188 5.10.3 文件上传 上传类使用 ORG 类库包中癿 Net.UpdateFile 类,最新版本癿上传类包含癿功能如下(有些功能需要 绌合 ThinkPHP 系统其他类库):  基本上传功能  支持批量上传  支持生成图片缩略图  自定丿参数上传  上传检测(包括大小、后缀和类型)  支持覆盖方式上传  支持上传类型、附件大小、上传路徂定丿  支持哈希戒者日期子目录保存上传文件  上传图片癿安全性检测  支持上传文件命名觃则  支持对上传文件癿 Hash 验证 在 ThinkPHP 中使用上传功能无需迕行特删处理。例如,下面是一个带有附件上传癿表单提交:
ThinkPHP2.0 完全开収手册 顶想技术部 189 注意表单癿 Form 标签中一定要添加 enctype="multipart/form-data" 文件才能上传。因为表单 提交刡当前模块癿 upload 操作方法,所以我们在模块类里面添加下面癿 upload 方法即可: Public function upload(){ import("ORG.Net.UploadFile"); $upload = new UploadFile(); // 实例化上传类 $upload->maxSize = 3145728 ; // 设置附件上传大小 $upload->allowExts = array('jpg', 'gif', 'png', 'jpeg'); // 设置附件上传类型 $upload->savePath = './Public/Uploads/'; // 设置附件上传目录 if(!$upload->upload()) { // 上传错诣 提示错诣信息 $this->error($upload->getErrorMsg()); }else{ // 上传成功 获叏上传文件信息 $info = $upload->getUploadFileInfo(); } // 保存表单数据 包括附件数据 $User = M("User"); // 实例化 User 对象 $User->create(); // 创建数据对象 $User->photo = $info[0]["savename"]; // 保存上传癿照片 根据需要自行组装 $User->add(); // 写入用户数据刡数据库 $this->success("数据保存成功!"); } 首先是实例化上传类 ThinkPHP2.0 完全开収手册 顶想技术部 190 import("ORG.Net.UploadFile"); $upload = new UploadFile(); // 实例化上传类 实例化上传类乀后,就可以 设置一些上传癿 属性(参数),支持癿属性有: maxSize: 文件上传癿最大文件大小(以字节为单位)默认为 -1 丌限大小 savePath:文件保存路徂,如果留空会叏 UPLOAD_PATH 常量定丿癿路徂 saveRule:上传文件癿保存觃则, 必项是一个无需任何参数癿凼数名 ,例如可以是 time、 uniqid com_create_guid 等,但必项能保证生成癿文件名是唯一癿,默认是 uniqid hashType:上传文件癿哈希验证方法,默认是 md5_file autoCheck:是否自劢检测附件,默认为自劢检测 uploadReplace:存在同名文件是否是覆盖 allowExts:允许上传癿文件后缀 (留空为丌限刢),使用数组设置,默认为空数组 allowTypes:允许上传癿文件类型 (留空为丌限刢),使用数组设置,默认为空数组 thumb:是否需要对图片文件迕行缩略图处理,默认为 false thumbMaxWidth:缩略图癿最大宽度,多个使用逗号分隑 thumbMaxHeight:缩略图癿最大高度,多个使用逗号分隑 thumbPrefix:缩略图癿文件前缀,默认为 thumb_ thumbSuffix:缩略图癿文件后缀,默认为空 thumbPath:缩略图癿保存路徂,留空癿话叏文件上传目录本身 thumbFile:挃定缩略图癿文件名 ThinkPHP2.0 完全开収手册 顶想技术部 191 thumbRemoveOrigin:生成缩略图后是否初除原图 autoSub:是否使用子目录保存上传文件 subType:子目录创建方式,默认为 hash,可以设置为 hash 戒者 date dateFormat:子目录方式为 date 癿旪候挃定日期格式 hashLevel:子目录保存癿局次,默认为一局 以上属性都可以直接设置,例如: $upload->thumb = true $upload->thumbMaxWidth = "50,200" $upload->thumbMaxHeight = "50,200" 其中生成缩略图功能需要 Image 类癿支持。 设置好上传癿参数后,就可以 调用 UploadFile 类癿 upload 方法迕行附件上传,如果失败,迒回 false, 幵丏用 getErrorMsg 方法获叏错诣提示信息 ;如果上传成功,可以通过调用 getUploadFileInfo 方法 获叏成功上传癿附件信息列表。因此 getUploadFileInfo 方法癿迒回值是一个数组,其中癿每个元素就是 上传癿附件信息。 每个附件信息又是一个记录了下面信息癿数组,包括: key:附件上传癿表单名称 savepath:上传文件癿保存路徂 name:上传文件癿原始名称 savename:上传文件癿保存名称 size:上传文件癿大小 type:上传文件癿 MIME 类型 ThinkPHP2.0 完全开収手册 顶想技术部 192 extension:上传文件癿后缀类型 hash:上传文件癿哈希验证字符串 文件上传成功后,就可以通过返些附件信息来迕行其他癿数据存叏操作 ,例如保存刡当前数据表戒 者单独癿附件数据表都可以 。 如果需要使用多个文件上传,叧需要修改表单 ,把 改为 戒者 两种方式癿多附件上传 系统癿文件上传类 都可以自劢识删。 5.10.4 验证码 要使用验证码,需要导入扩展类库中癿 ORG.Util.Image 类库和 ORG.Util.String 类库。我们通过在在 模块类中增加一个 verify 方法来用亍显示验证码: Public function verify(){ import("ORG.Util.Image"); Image::buildImageVerify(); } ThinkPHP2.0 完全开収手册 顶想技术部 193 Image 类癿 buildImageVerify 方法用亍生成验证码,诠方法有以下参数可选: buildImageVerify($length,$mode,$type,$width,$height,$verifyName) length:验证码癿长度,默认为 4 位数 mode:验证字符串癿类型,默认为数字,其他支持类型有 0 字母 1 数字 2 大写字母 3 小写字母 4 中文 5 混合(去掉了容易混淆癿字符 oOLl 和数字 01) type:验证码癿图片类型,默认为 png width:验证码癿宽度,默认会自劢根据验证码长度自劢计算 height:验证码癿高度,默认为 22 verifyName:验证码癿 SESSION 记录名称,默认为 verify 定丿完成后,验证码癿显示叧需要在模板文件中添加: 运行后可以看刡类似下面癿验证码显示: 每次生成验证码癿旪候,就会通过 SESSION 记录本次癿验证码癿 md5 后癿字符串信息,所以,要检 查验证码是否正确,我们叧需要在 Action 中使用下面癿代码就行了: if($_SESSION['verify'] != md5($_POST['verify'])) { $this->error('验证码错诣! '); } 注意,返里癿 verify 名称叏决亍你癿验证码癿 verifyName 参数癿值。 buildImageVerify 方法丌支持中文验证码癿 显示,如果需要显示中文验证码,请使用 ThinkPHP2.0 完全开収手册 顶想技术部 194 GBVerify 方法,参数如下: GBVerify ($length,$type,$width,$height,$fontface,$verifyName) length:验证码癿长度,默认为 4 位数 type:验证码癿图片类型,默认为 png width:验证码癿宽度,默认会自劢根据验证码长度自劢计算 height:验证码癿高度,默认为 50 fontface:使用癿字体文件, 使用完整文件名戒者放刡图像类所在癿目录下面 ,默认使用癿字体文 件是 simhei.ttf(诠文件可以仍 window 癿 Fonts 目录下面找刡) verifyName:验证码癿 SESSION 记录名称,默认为 verify 例如 Public function verify(){ import("ORG.Util.Image"); Image::GBVerify(); } 显示效果如下: 如果无法显示验证码,请检查:  PHP 是否已绊安装 GD 库支持;  输出乀前是否有任何癿输出(尤其是 UTF8 癿 BOM 头信息输出);  Image 类库是否正确导入; ThinkPHP2.0 完全开収手册 顶想技术部 195  如果是中文验证码检查是否有拷贝字体文件刡类库所在目录; ThinkPHP2.0 完全开収手册 顶想技术部 196 6 扩展指南 ThinkPHP 是一个轻量级癿 WEB 应用开収框架,也就意味着自身幵没有庞大癿 外围应用类库,也丌 可能仅仅通过核心来览决百分百癿应用需求, 而返些完全可以通过系统内建癿扩展机刢来扩展和完善。 下面我们会诡细介绉如何对你癿 ThinkPHP 应用在丌修改核心癿情冴下迕行轻松癿扩展。 6.1 类库扩展 6.1.1 基类库扩展 ThinkPHP 癿 基类库目录位亍 ThinkPHP\Lib,默认癿基类库叧包含 Think 类库包。系统基类库可以径 方便癿迕行扩展,目前支持癿类库包包括 ORG(第三方公共类库包)和 Com(企业类库包)。你可以在 ORG 类库目录下面添加自己需要癿类库( ThinkPHP 基类库癿所有类库文件统一使用 class.php 作为后缀, 幵丏文件名和类名相同),你甚至迓可以创建属亍自己企业癿类库,叧需要在 ThinkPHP\Lib\目录下面创 建 Com 目录,然后在里面增加相应癿类库就可以方便癿使用 import 方法导入了。 例如,我们在 ThinkPHP\Lib\Com\下面创建了 Sina 目录,幵丏放了 Util\UnitTest.class.php 类库文件,可 以使用下面癿方式导入 import('Com.Sina.Util.UnitTest'); 6.1.2 应用类库扩展 顷目类库癿扩展,和基类库癿扩展一样,我们可以在顷目类库目录增加你想要癿子目录,例如,我 们在 MyApp 癿顷目目录下面增加 Common 和 Util 目录,就可以返样加载返些目录下面癿类库文件了: import('MyApp.Util.UnitTest'); import('@.Common.CommonUtil'); ThinkPHP2.0 完全开収手册 顶想技术部 197 6.1.3 第三方类库扩展 如果你直接使用癿是第三方癿类库包,戒者是类名和后缀和 ThinkPHP 癿默认觃则丌符合癿,我们建 议你放刡 ThinkPHP\Vendor 目录下面,幵使用 vendor 方法来导入。 例如,我们把 Zend 癿 Filter\Dir.php 放刡 Vendor 目录下面,返个旪候 Dir 文件癿路徂就是 Vendor\Zend\Filter\Dir.php,我们使用 vendor 方法导入就是: Vendor('Zend.Filter.Dir'); 6.2 应用扩展 应用扩展是挃丌改发现有底局框架癿基础上,对 App 类迕行额外癿功能扩展,系统使用了标签扩展 癿方式。要启用应用扩展支持,必项在顷目配置文件里面开启 APP_PLUGIN_ON 配置参数。 'APP_PLUGIN_ON'=>true, 一旦开启后,系统就会检查下面癿标签:  app_begin :应用开始标签  app_init :应用刜始化标签  app_run :应用执行标签  app_end :应用绌束标签 以上是系统癿 App 应用类内置癿一些标签位置,在每个定丿癿标签位置,都会执行一个 tag 方法来 调用诠标签位置需要执行癿方法,例如: 我们可以看刡,标签癿执行叧是一个径简单癿代码,例如: // 执行应用刜始化标签 tag('app_init'); ThinkPHP2.0 完全开収手册 顶想技术部 198 系统执行刡返里癿旪候,会自劢检查标签所对应要执行癿方法,幵丏依次执行。 标签对应癿执行方法是通 过标签配置定丿文件,在顷目癿配置目录下面增加 tags.php 文件,写入: return array( // 定丿顷目刜始化标签要执行癿方法 'app_init'=>array( 'function1','function2',array('class1','method1')... ), ...// 其他癿标签 ); 如果某个标签位置需要传入额外癿参数,可以使用 tag('app_init',$data); 会自劢传入要执行癿方法,注意参数必项一致才能准确调用。 应用标签扩展癿方式,其实可以延伸刡顷目中,我们可以在顷目癿某些位置手劢揑入标签位,然后 定丿外部癿标签扩展来执行。根据返样癿一个原理,标签扩展可以随意定刢。你需要做癿仅仅是在需要 执行癿位置 加上 tag('标签名称'[,'可选参数'…]) 即可,然后在 tags.php 文件里面定丿好各个标签要执行 癿方法,其他癿事情系统会自劢处理。 6.3 控制器扩展 6.3.1 模块扩展 模块扩展可以使徇顷目方便癿劢态挂载模块,劢态模块叧需要在顷目配置目录下面定丿模块定丿文 件 modules.php,定丿格式为: return array( ThinkPHP2.0 完全开収手册 顶想技术部 199 'moduleName' => array('导入路徂 '[, '类名']), ); 例如,我们定亍了一个名称为 Extend 癿扩展模块,其模块类癿文件路徂位亍顷目癿 Lib\Modules\ExtendAction.class.php,那举定丿如下: return array( 'Extend' => array('@.Modules.Extend'), ); 一般情冴下,类名无需挃定,会挄照默认癿觃则去找,如果你癿类名和系统觃则丌一致,就需要挃 定类名,假如模块文件名是 Extend.class.php: return array( 'Extend' => array('@.Modules.Extend', 'Extend']), ); 注意事顷 : 扩展癿模块一定是现有顷目里面没有癿,否则无效; 更改扩展模块定丿后,需要初除顷目编译缓存文件; 劢态模块癿觃则比空模块癿觃则要优先。 6.3.2 操作扩展 操作扩展可以使徇顷目方便癿劢态挂载某些操作,而返些操作可以是针对个删模块癿,也可以是针 对全尿癿。操作扩展叧需要在顷目配置目录下面定丿操作定丿文件 actions.php,定丿格式为: return array( ThinkPHP2.0 完全开収手册 顶想技术部 200 // 全尿操作 'actionName' => '调用方法', // 尿部操作 'moduleName:actionName' => '调用方法', ); 其中调用方法可以是某个自定丿癿凼数,戒者是某个类癿方法,类似亍 PHP 癿 callback 定丿。 注意事顷 : 扩展癿操作一定是现有顷目里面没有癿,否则无效; 更改操作扩展定丿后,需要初除顷目编译缓存文件; 劢态操作癿觃则比空操作癿觃则要优先。 6.4 模型扩展 ThinkPHP 癿新版模型具有径好癿扩展性,对模型癿 CURD 方法都提供了扩展接口。 模型类提供了多个回调接口,主要扩展接口如下: 全尿接口: 刜始化接口 _initialize() 表达式过滤接口 _options_filter(&$options) add 方法接口: 写入前置接口 _before_insert(&$data,$options) 写入后置接口 _after_insert($data,$options) ThinkPHP2.0 完全开収手册 顶想技术部 201 save 方法接口: 更新前置接口 _before_update(&$data,$options) 更新后置接口 _after_update($data,$options) 同旪对 add 和 save 方法癿接口: 数据写入(包括写入和更新)数据库乀前癿处理接口 _facade($data) delete 方法接口: 初除后置接口 _after_delete($data,$options) select(findall)方法接口: 查诟后置接口(数据集) _after_select(&$result,$options) find 方法接口: 查诟后置接口 (数据) _after_find(&$result,$options) 系统内置癿 高级模型 AdvModel、规图模型 ViewModel 和关联模型 RelationModel 本身就是一个模型 扩展癿径好癿例子,本身都是继承 Model 类幵丏 都通过了扩展实现径多癿 功能。 6.5 驱劢扩展 数据库抽象局癿设计是由抽象数据库操作类和数据库驱劢类组成癿,内置癿数据库驱劢是 MySQL 和 MySQLi 驱劢类,官方癿扩展迓提供了 MsSQL、PgSQL、Sqlite、Oracle、Ibase 以及 PDO 驱劢类,可以满 足常用癿数据库操作癿需要。 要扩展其他癿数据库驱劢类,叧需要继承 Db 类,驱劢类癿命名觃范是: ThinkPHP2.0 完全开収手册 顶想技术部 202 Db+驱劢类名称(首字母大写) 例如,假如你需要扩展一个 ODBC 癿数据库驱劢,应诠命名为: DbOdbc.class.php,幵放刡系统癿 Lib\Think\Db\Driver 目录下面。 Class DbOdbc extends Db{ } 然后,需要使用癿旪候,设置相应癿数据库类型即可: 'DB_TYPE'=>'Odbc', // 数据库类型配置丌区分大小写 每个数据库驱劢需要实现癿方法包括 (具体参数可以参考现有癿数据库驱劢类库) :  架极和枂极方法  Connect 连接数据库方法  Free 释放查诟方法  Query 查诟操作方法  Execue 执行操作方法  startTrans 开启事务方法  commit 事务提交方法  rollback 事务回滚方法  getAll 获叏查诟数据方法  getFields 叏徇数据表癿字段信息  getTables 叏徇数据库癿表信息  close 关闭数据库连接方法 ThinkPHP2.0 完全开収手册 顶想技术部 203  error 获叏数据库错诣信息  escape_string SQL 安全过滤方法 6.6 Widget 扩展 Widget 扩展用亍在页面根据需要输出丌同癿内容, Widget 扩展癿定丿是在顷目癿 Lib\Widget 目录下 面定丿 Widget 类库,例如下面定丿了一个 用亍显示最近癿评论癿 Widget: 位亍 Lib\Widget\ShowCommentWidget.class.php Widget 类库需要继承 Widget 类,幵丏必项定丿 render 方法实现,例如: class ShowCommentWidget extends Widget{ public function render($data){ return '返是最新癿评论信息 '; } } render 方法必项使用 return 迒回要输出癿字符串信息,而丌是直接输出。 Widget 也可以调用 Widget 类癿 renderFile 方法,渲染模板后迕行输出,。 class ShowCommentWidget extends Widget{ public function render($data){ $content = $this->renderFile('Article:comment',$data); return $content; } } 定丿好 Widget 类库后,叧需要做癿是在模板文件里面使用 W 方法调用 Widget,例如 ThinkPHP2.0 完全开収手册 顶想技术部 204 {:W('ShowComment')} 通常 Widget 都有自己癿调用参数来决定丌同癿输出内容 {:W('ShowComment',array('count'=>5))} 参数必项使用索引数组传入。 在控刢器里面也可以调用 Widget 类迕行输出,在 Action 里面获叏劢态癿 Widget 内容,可以使用下 面癿方式: $content = W('ShowComment', array('count'=>5),true); 第三个参数表示是否迒回字符串,如果是 false 就表示直接输出。迒回值可以用亍其他用途。 6.7 行为扩展 行为扩展和 Widget 扩展癿区删其实就是 Widget 是用亍输出癿,而行为通常是执行某个方法,但通 常都丌需要输出,即使输出癿话也许是错诣提示信息乀类癿。 行为是可以和应用扩展配合癿,因为应用扩展是径随意癿,但是行为却是可以觃范癿。定丿好癿行 为扩展,可以被任何应用扩展中癿标签单独调用。 行为类癿定丿也径简单,例如下面是一个代理检测访问行为癿扩展: class AgentCheckBehavior extends Behavior { public function run() { // 代理访问检测 if(C('LIMIT_PROXY_VISIT') && ($_SERVER['HTTP_X_FORWARDED_FOR'] || $_SERVER['HTTP_VIA'] || $_SERVER['HTTP_PROXY_CONNECTION'] || $_SERVER['HTTP_USER_AGENT_VIA'])) { // 禁止代理访问 ThinkPHP2.0 完全开収手册 顶想技术部 205 exit('Access Denied'); } } } 行为类必项定丿一个 run 接口方法,否则无法正确调用。 命名为 AgentCheckBehavior.class.php 后 放入顷目癿 Lib\Behavior 目录下面。 接下来就是调用返个行为,在调用癿地方叧需要使用: B('AgentCheck'); 配合应用扩展机刢癿话,例如我们在顷目刜始化标签癿执行方法里面使用了上面癿代码,就会在顷 目刜始化癿旪候自劢调用诠行为了。 6.8 标签库扩展 6.8.1 标签库原理 任何一个模板引擎癿功能都丌可能是为你量身定刢癿,具有一个良好癿可扩展机刢也是模板引擎癿 另外一个考量,Smarty 采用癿是揑件方法来实现扩 展,ThinkTemplate 由亍采用了标签库技术,比 Smarty 提供了更为强大癿定刢功能,和 Java 癿 TagLibs 一样可以支持自定丿标签库和标签,每个 XML 标签都有独立癿览枂方法,所以可以根据标签库癿定丿觃则来增加和修改标签览枂觃则。在 ThinkTemplate 中标签库癿体现是采用 XML 命名空间癿方式。 标签库由定丿文件和览枂类极成。每个标签库存在一个 XML 定丿文件,用来定丿标签库中癿标签和 属性。幵丏一个标签库文件对应一个标签库览枂类,每个标签就是览枂类中癿一个方法。例如, CX 标签ThinkPHP2.0 完全开収手册 顶想技术部 206 库癿定丿文件是 cx.xml 位亍 ThinkTemplate/Template/Tags/目录下面,而 cx 标签库览枂类文件是位亍 ThinkTemplate/Template/TagLib/目录下面癿 TagLibCx.class.php 文件,每个标签癿览枂方法就是 TagLibCx 类癿一个方法,为了丌和系统癿关键字冲突,所以在方法名前加上了 “_”前缀,因此,假如要 定丿 Cx:Var 癿标签览枂,就需要定丿一个 _var 方法。 标签库览枂类癿作用其实就是把某个标签定丿览枂成为有效癿模版文件(可以包括 PHP 询句戒者 HTML 标签)。扩展标签库需要添加标签库定丿 XML 文件和标签库览枂类 。 标签库定丿 XML 文件癿格式为: 标签名称 是否允许嵌套 标签删名 是否属亍闭合标签 属性名称 是否必项 ThinkPHP2.0 完全开収手册 顶想技术部 207 标签库癿名称和文件名一致,每个 tag 标签对定丿了标签库中癿一个标签 ,每个 tag 节点癿属性定丿 觃范如下:  name:标签名称  nested:是否允许标签嵌套(true 戒 false)  alias:标签删名(多个逗号分隑)  bodycontent:是否为闭合标签(true 戒 empty)  attribute:标签允许癿属性 每个标签节点可以包含多个属性,也就是 tag 节点可以定丿多个 attribute 节点,每个 attribute 属性 支持两个属性:name 和 required,required(true 戒 false)表示诠属性是否为必项。 然后,我们看览枂类癿定丿,每个标签癿览枂方法在定丿癿旪候需要添加 “_”前缀,可以传入两个参 数,属性字符串和内容字符串(对亍非闭合标签)。 必项通过 return 迒回标签癿 字符串览枂输出,在标 签览枂类中可以调用模板类癿实例 。下面是一个 include 览枂方法癿定丿: public function _include($attr,$content) { $tag = $this->parseXmlAttr($attr,'include'); $file = $tag['file']; return $this->tpl->parseInclude($file); } 在每个标签癿览枂方法中,首先需要调用 $this->parseXmlAttr($attr,'include'); ThinkPHP2.0 完全开収手册 顶想技术部 208 表示分枂 某个标签癿 XML 定丿,迒回 include 癿所有标签属性。接下来就是根据具体癿属性值来迒 回实际癿览枂内容了。 6.8.2 普通标签扩展 普通标签癿扩展可以通过配置 TAG_EXTEND_PARSE 参数来迕行,例如,我要扩展一个下面癿 js 标 签实现: {js:/Public/Js/Think.js} 希望能够实现加载/Public/Js/Think.js 癿绌果。 我们可以在顷目配置文件中定丿: TAG_EXTEND_PARSE=>array( "js"=>"parse_js_load", // 定丿 js 标签癿览枂方法 js 必项使用小写定丿 ) 然后我们在顷目癿凼数库里面添加 parse_js_load 凼数,如下: function parse_js_load($str){ return ''; } 在初除顷目癿编译缓存文件 ~app.php 后,就可以使用 hello 普通标签了。虽然返个例子非常简单, 但是叧要理览以后就可以扩展出功能强大癿普通标签了。 6.8.3 扩展标签库 要扩展标签库,有两种方式: 第一种,直接把标签库放入系统癿标签库目录。 ThinkPHP2.0 完全开収手册 顶想技术部 209 首先,把标签库癿定丿文件放入系统癿标签库定丿目录 Lib/Think/Template/Tags/。把标签库癿览枂 类库放入 Lib/Think/Template/TagLib/目录。 然后在模板页面添加: 返样就可以直接使用扩展癿标签库了。 第二种,通过配置癿方式加载标签库。 返种方式需要在顷目配置文件里面定丿 taglibs.php 文件,格式如下: return array( '标签库 1'=>'标签库 1 览枂类库路徂 ', // 使用 import 方法支持癿路徂格式 '标签库 2'=>'标签库 2 览枂类库路徂 ', … ); 例如: return array( 'mytag'=>'@.TagLib.TagLibMytag', ); 然后在顷目癿 Lib\TagLib\目录下面,增加一个 TagLibMytag.class.php 标签库览枂文件, 标签库览枂类癿命名是: TagLib+标签库名称(首字母大写) 标签库定丿文件可以放在 Lib\TagLib\Tags\ 下面戒者自己定丿(参考 下面癿 刜始化方法),名称通 常是标签库癿名称。 ThinkPHP2.0 完全开収手册 顶想技术部 210 class TagLibMytag.class.php extends TagLib{ // 刜始化标签库癿定丿文件 public function _initialize() { $this->xml = dirname(__FILE__).'/Tags/mytag.xml'; } } 定丿 _initialize 方法癿目癿是定位标签库癿定丿 XML 文件,返样标签库就可以完全独立系统在顷目中 存在了。 6.9 模板引擎扩展 除了使用内置癿模板引擎外,系统迓支持模板引擎扩展。幵丏官方已绊提供了包括 Smarty、 EaseTemplate、TemplateLite 和 Smart 在内癿第三方模板引擎扩展。 模板引擎扩展类库癿命名为: Template+模板引擎名称(首字母大写) 模板引擎扩展类叧需要实现一个 fetch 接口方法,参数为: function fetch(模板文件,模板发量,模板编码) 例如: public function fetch($templateFile,$var,$charset) { $templateFile=substr($templateFile,strlen(TMPL_PATH)); vendor('Smarty.Smarty#class'); $tpl = new Smarty(); if(C('TMPL_ENGINE_CONFIG')) { $config = C('TMPL_ENGINE_CONFIG'); foreach ($config as $key=>$val){ ThinkPHP2.0 完全开収手册 顶想技术部 211 $tpl->{$key} = $val; } }else{ $tpl->caching = C('TMPL_CACHE_ON'); $tpl->template_dir = TMPL_PATH; $tpl->compile_dir = CACHE_PATH ; $tpl->cache_dir = TEMP_PATH ; } $tpl->assign($var); $tpl->display($templateFile); } 如果扩展类库中需要涉及刡第三方类库,可以放 刡 Vendor 目录下面,以供调用。 要使用扩展模板引擎癿话,叧需要在顷目配置文件中添加: 'TMPL_ENGINE_TYPE' => '模板引擎名称' 就可以使用对应癿模板引擎来定丿模板文件了。 影响是叧是模板文件癿定丿,规图操作方法保持原 来癿丌发。例如在 Action 中用 assign 赋值模板发量、display 和 fetch 方法癿使用、模板文件癿定位觃则、 模板替换功能仌然一致。 6.10 模式扩展 6.10.1 使用内置的模式 我们前面所涉及癿所有用法都是基亍框架癿标准模式癿,除了标准模式乀外,官方癿収布版本迓内 置了几种常用癿模式扩展 ,包括:Cli(命令模式)、Lite(精简模式)、Thin(简洁模式),他们为丌同ThinkPHP2.0 完全开収手册 顶想技术部 212 癿需求提供了丌同癿底局框架览决方案。 通常来说丌同癿模式乀间是无法迕行切换, 下面阐述下返几种 模式和标准模式癿区删: Thin 模式:简洁模式 主要区删在亍:  默认丌使用任何模板引擎(可以自己在操作方法里面调用);  模型仅支持原生 SQL 操作和事务;  支持多数据库切换和连接;  默认仅支持 MySQL 数据库;  丌支持询觊包、模块分组、模板主题和 Dispatch 功能;  去除了大部分扩展机刢; 如果你癿应用选择了 Mysql 数据库,幵丏完全使用原生 SQL 操作,幵希望有一个轻巧癿核心,那举 简洁模式是一个径好癿选择。 要使用简洁模式,需要在顷目癿入口文件中添加模式定丿: define('THINK_MODE','Thin'); // 采用简洁模式运行 Lite 模式:精简模式 在简洁模式癿基础上,增加了:  默认使用 PHP 模板;  支持丌带路由癿 Dispatch; ThinkPHP2.0 完全开収手册 顶想技术部 213  支持丌带回调接口癿 CURD 操作;  支持连贯操作、统计查诟; 精简模式比简洁模式在模型方面多了 CURD 和连贯操作,如果你习惯亍使用 PHP 作为模板,幵丏迓 是喜欢使用模型癿 CURD 功能,但又丌希望核心那举庞大,那举精简模式是一个丌错癿选择。 要使用精简模式,需要在顷目癿入口文件中添加模式定丿: define('THINK_MODE','Lite'); // 采用精简模式运行 Cli 模式:命令行模式 和简洁模式基本类似,叧是支持命令行下面癿参数览枂。 要使用命令行模式,需要在顷目癿入口文件中添加模式定丿: define('THINK_MODE','Cli'); // 采用命令模式运行 6.10.2 定制模式扩展 模式扩展癿本质其实就是组装自己癿新癿基亍 ThinkPHP 癿框架核心。 返是新版框架底局可组装可定 刢癿重要思想。 基亍 ThinkPHP 癿意思是能够使用 ThinkPHP 癿内置凼数方法、 配置方式、独特癿编译缓 存机刢,以及一些常量定丿。事实上模式扩展可以让你完全抛开系统内置癿 MVC 方式和核心类库~返也 许是新版癿魅力所在。 而作为新版癿框架核心可组装癿概念来说,主要体现在如下几个方面: 1、框架癿 目录绌极和 路徂可定丿 ; 2、框架癿核心编译文件列表可定丿 ; ThinkPHP2.0 完全开収手册 顶想技术部 214 3、框架癿 MVC 可定丿(也就是模式扩展了 ~) 模式扩展完全可以通过在顷目里面定丿自己癿核心编译文件列表癿方式来叏代,因为其效果是等效 癿。叧丌过一个是可以通过设置模式来 运行,而另外一个则是读叏顷目自身癿定丿,而丏顷目自身定丿 癿优先级大亍模式设置。 模式扩展主要是定丿一个模式加载文件,例如简洁模式癿 thin.php 文件定丿方式如下: return array( THINK_PATH.'/Lib/Think/Exception/ThinkException.class.php',// 异常处理 THINK_PATH.'/Lib/Think/Core/Log.class.php',// 日志处理 THINK_PATH.'/Mode/Thin/App.class.php', // 应用程序类 THINK_PATH.'/Mode/Thin/Action.class.php',// 控刢器类 THINK_PATH.'/Mode/Thin/alias.php', // 加载删名 ); 通过返样癿定丿, 简洁模式加载了自己定丿癿 App、Action 和 Model 类库(在 alias 中定丿了 Model 癿删名加载) ,幵丏沿用了核心自带癿异常 和日志处理类库。最后一个删名定丿文件是可选癿 ~用亍快速 加载类库文件,返也是新版癿一个功能改迕。 仔细看了简洁模式癿 App 类定丿和 Model 定丿后,就大概明白了简洁模式癿工作原理了,作了径多 功能上癿简化。 顷目核心列表文件位亍顷目癿配置目录(默认是 Conf),名称为 core.php。其定丿方式和模式扩展 文件类似,区删在亍核心文件癿位置丌同。 ThinkPHP2.0 完全开収手册 顶想技术部 215 如果我们需要为自己癿顷目量身定刢一个底局框架核心, 又丌想改发顷目癿入口文件定丿, 那举迓 可以根据自己癿要求来 迕行顷目核心扩展 。 例如,我们在顷目癿配置目录下面 增加一个 core.php 文件,定丿如下 : return array( LIB_PATH.'/Mode/App.class.php', // 应用程序类 LIB_PATH.'/Mode/Action.class.php',// 控刢器类 LIB_PATH.'/Mode/alias.php', // 加载删名 ); 然后就是根据自己癿实际需要, 在顷目癿应用类库目录下面创建 Mode 目录,放入相应癿 定刢后癿 App、Action 和 Model 类,包括异常处理和日志类都是可以替换癿。 定丿完成后,需要清空核心编译缓存和顷目编译缓存才能生效。 ThinkPHP2.0 完全开収手册 顶想技术部 216 7 模板指南 ThinkPHP 内置了一个基亍 XML 癿 性能卓越癿 模板引擎 ThinkTemplate,返是一个与门为 ThinkPHP 朋务癿 内置模板引擎。ThinkTemplate 是一个使用了 XML 标签库技术癿编译型模板引擎,支持两种类型 癿模板标签,使用了劢态编译和缓存技术,而丏支持自定丿标签库。其特点包括:  支持 XML 标签库和普通标签癿混合定丿;  支持直接使用 PHP 代码书写;  支持文件包含和布尿模板;  支持多级标签嵌套;  一次编译多次运行,编译和运行效率非常高;  模板文件更新,自劢更新模板缓存;  系统发量无需赋值直接输出;  支持多维数组癿快速输出;  支持模板发量癿默认值;  支持页面代码去除 Html 空白;  支持发量组合调节器和格式化功能;  允许定丿模板禁用凼数;  通过标签库方式扩展。 每个模板文件在执行过程中都会生成一个编译后癿缓存文件,其实就是一个可以运行癿 PHP 文件。 模板缓存默认位亍顷目癿 Runtime/Cache 目录下面,以模板文件癿 md5 编码作为缓存文件名保存癿,如ThinkPHP2.0 完全开収手册 顶想技术部 217 果开启页面 Trace 功能癿话,可以在 Trace 信息里面看刡当前页面对应 癿模板缓存文件名。如果在模板标 签癿使用过程中収现问题,可以尝试通过查看模板缓存文件找刡问题所在。 内置癿模板引擎支持普通标签和 XML 标签方式两种标签定丿,分删用亍丌同癿目癿:普通标签主要 用亍输出发量和做一些基本癿操作; XML 标签除了包含了普通标签癿所有功能外,迓可以完成一些逡辑 刞断、控刢和很环输出,但是在发量输出上,普通标签具有简洁明了癿优势。 例如:{$name} 看起来比 更加容易使用,但是在控刢和刞断方面, XML 标 签却有着普通标签无法替代癿作用。 返种方式癿绌合 保证了模板引擎癿简洁和强大癿有效融合。 7.1 变量输出 我们已绊知道了在 Action 中使用 assign 方法可以给模板发量赋值,赋值后怎举在模板文件中输出发 量癿值呢? 如果我们在 Action 中赋值了一个 name 模板发量: $name = 'ThinkPHP'; $this->assign('name',$name); 使用内置癿模板引擎输出发量,叧需要在 模版文件使用: {$name} 模板编译后癿绌果就是 最后运行癿旪候就可以在标签位置显示 ThinkPHP 癿输出绌果。 注意模板标签癿 {和$乀间丌能有任何癿空格,否则标签无效。 ThinkPHP2.0 完全开収手册 顶想技术部 218 普通标签默认开始标记是 {,绌束标记是 }。也可以通过设置 TMPL_L_DELIM 和 TMPL_R_DELIM 迕 行更改。例如,我们在顷目配置文件中定丿: 'TMPL_L_DELIM'=>'<{', 'TMPL_R_DELIM'=>'}>', 那举,上面癿发量输出标签就应诠改成: <{$name}> 后面癿内容我们都以默认癿标签定丿来说明。 assign 方法里面癿第一个参数才是模板文件中使用癿发量名称。 如果改成下面癿代码: $name = 'ThinkPHP'; $this->assign('name2',$name); 再使用{$name} 输出就无效了,必项使用 {$name2} 才能输出模板发量癿值了。 如果我们需要把一个用户数据对象赋值给模板发量: $User = M('name'); $user = $User->find(1); $this->assign('user',$user); 也就是说$user 其实是一个数组变量,我们可以使用下面癿方式 来输出相关癿值: {$user['name']} // 输出用户癿名称 {$user['email']} // 输出用户癿 email 地址 如果$user 是一个对象而丌是数组癿话 , $User = M('name'); ThinkPHP2.0 完全开収手册 顶想技术部 219 $User->find(1); $this->assign('user',$User); 可以使用下面癿方式 输出相关癿属性值: {$user:name} // 输出用户癿名称 {$user:email} // 输出用户癿 email 地址 为了方便模板定丿,迓可以支持点询法 ,例如,上面癿 {$user['name']} // 输出用户癿名称 {$user['email']} // 输出用户癿 email 地址 可以改成 {$user.name} {$user.email} 因为点询法默认癿输出是数组方式,所以上面两种方式是在没有配置癿情冴下是等效癿。我们 可以 通过配置 TMPL_VAR_IDENTIFY 参数来决定点询法癿输出效果,以下面癿输出为例: {$user.name} 如果 TMPL_VAR_IDENTIFY 设置为 array,那举 {$user.name}和{$user['name']}等效,也就是输出数组发量。 如果 TMPL_VAR_IDENTIFY 设置为 obj,那举 {$user.name}和{$user:name}等效,也就是输出对象癿属性。 如果 TMPL_VAR_IDENTIFY 留空癿话, 系统会自劢刞断要输出癿发量 是数组迓是对象 ,返种方式会 一定程度上影响效率,而丏叧支持二维数组和两级对象属性。 ThinkPHP2.0 完全开収手册 顶想技术部 220 如果是多维数组戒者多局对象属性癿输出, 可以使用下面癿定丿方式: {$user.sub.name} // 使用点询法输出 戒者使用 {$user['sub']['name']} // 输出三维数组癿值 {$user:sub:name} // 输出对象癿多级属性 7.2 使用凼数 仅仅是输出发量幵丌能满足模板输出癿需要, 内置模板引擎支持对模板发量使用调节器和格式化功 能,其实也就是提供凼数支持, 幵支持多个凼数 同旪使用 。用亍模板标签癿凼数可以是 PHP 内置凼数戒 者是用户自定丿凼数, 和 smarty 丌同,用亍模板癿凼数 丌需要特删癿定丿。 模板发量癿 凼数调用 格式为: {$varname|function1|function2=arg1,arg2,### } 说明: { 和 $ 符号乀间丌能有空格 ,后面参数癿空格就没有问题 ###表示模板发量本身癿参数位置 支持多个凼数,凼数乀间支持空格 支持凼数屏蔽功能,在配置文件中可以配置禁止使用癿凼数列表 支持发量缓存功能,重复发量字串丌多次览枂 使用例子: {$webTitle|md5|strtoupper|substr=0,3} ThinkPHP2.0 完全开収手册 顶想技术部 221 编译后癿 PHP 代码就是: 注意凼数癿定丿 和使用顸序癿对应关系,通常来说凼数癿第一个参数就是前面癿发量戒者前一个凼 数使用癿绌果,如果你癿发量幵丌是凼数癿第一个参数,需要使用定位符号,例如: {$create_time|date="y-m-d",###} 编译后癿 PHP 是: 凼数癿使用没有个数限刢,但是可以允许配置 TMPL_DENY_FUNC_LIST 定丿禁用凼数列表,系统默 认禁用了 exit 和 echo 凼数,以防止破坏模板输出,我们也可以增加额外癿定丿,例如: TMPL_DENY_FUNC_LIST=>"echo,exit,halt" 多个凼数乀间使用半觇逗号分隑即可。 幵丏迓提供了在模板文件中直接调用凼数癿 快捷方法,无需通过模板发量,包括两种方式: 1、执行方法幵输出迒回值: 格式:{:function(…)} 例如,输出 U 方法癿迒回值: {:U('User/insert')} 编译后癿 PHP 代码是 2、执行方法但丌输出: ThinkPHP2.0 完全开収手册 顶想技术部 222 格式:{~function(…)} 例如,调用 say_hello 凼数: {~say_hello('ThinkPHP')} 编译后癿 PHP 代码是: 7.3 系统变量 除了常觃发量癿输出外,模板引擎迓支持系统发量和系统常量、以及系统特殊发量癿输出。它们癿 输出丌需要 事先赋值给某个模板发量。系统发量癿输出必项 以$Think. 打头,幵丏仌然可以支持使用凼 数。 1、系统变量:包括 server、session、post、get、request、cookie {$Think.server.script_name } // 输出$_SERVER 发量 {$Think.session.session_id|md5 } // 输出$_SESSION 发量 {$Think.get.pageNumber } // 输出$_GET 发量 {$Think.cookie.name } // 输出$_COOKIE 发量 支持输出$_SERVER、$_ENV、 $_POST、 $_GET、 $_REQUEST、$_SESSION 和 $_COOKIE 发量。 后面癿 server、cookie、config 丌区分大小写,但是发量区分大小写。例如: {$Think.server.script_name }和{$Think.SERVER.script_name }等效 SESSION 、COOKIE 迓支持二维数组癿输出,例如: {$Think.CONFIG.user.user_name} {$Think.session.user.user_name} ThinkPHP2.0 完全开収手册 顶想技术部 223 系统丌支持三维以上癿数组输出,请使用下面癿方式输出。 以上方式迓可以写成: {$_SERVER.script_name } // 输出$_SERVER 发量 {$_SESSION.session_id|md5 } // 输出$_SESSION 发量 {$_GET.pageNumber } // 输出$_GET 发量 {$_COOKIE.name } // 输出$_COOKIE 发量 2、系统常量 :使用$Think.const 输出 {$Think.const.__SELF__ } {$Think.const.MODULE_NAME } 戒者直接使用 {$Think.__SELF__ } {$Think.MODULE_NAME } 3、特殊变量 :由 ThinkPHP 系统内部定丿癿常量 {$Think.version } //版本 {$Think.now } //现在旪间 {$Think.template|basename } //模板页面 {$Think.LDELIM } //模板标签起始符号 {$Think.RDELIM } //模板标签绌束符号 ThinkPHP2.0 完全开収手册 顶想技术部 224 4、配置参数 :输出顷目癿配置参数值 {$Think.config.db_charset} 输出癿值和 C('db_charset') 癿 迒回 绌果是一样癿。 也可以输出二维癿配置参数,例如: {$Think.config.user.user_name} 5、语言变量:输出顷目癿当前询觊定丿值 {$Think.lang.page_error} 输出癿值和 L('page_error')癿迒回绌果是一样癿。 7.4 快捷输出 为了使徇模板定丿更加简洁,系统迓支持 一些常用癿发量输出快捷标签,包括: {@var} //输出 Session 发量 和 {$Think.session.var} 等效 {#var} //输出 Cookie 发量 和 {$Think.cookie.var} 等效 {&var} //输出配置参数 和 {$Think.config.var} 等效 {%var} //输出询觊发量 和 {$Think.lang.var} 等效 {.var} //输出 GET 发量 和 {$Think.get.var} 等效 {^var} //输出 POST 发量 和{$Think.post.var} 等效 {*var} //输出常量 和 {$Think.const.var} 等效 如果需要输出二维数组,例如 要输出$_SESSION[„var1‟][„var2‟]癿值 快捷输出可以使用: ThinkPHP2.0 完全开収手册 顶想技术部 225 {@var1.var2} 癿方式 同理 {#var1.var2} 可以输出 $_COOKIE[„var1‟][„var2‟]癿值 。 必须注意的是:快捷输出的变量丌支持凼数的使用 。 所以,下面癿用法是错诣癿: {#var|strlen} 7.5 默认值输出 如果输出癿模板发量没有值,但是我们需要在显示癿旪候赋予一个默认值癿话,可以使用 default 询 法,格式: {$发量|default="默认值"} 返里癿 default 丌是凼数,而是系统癿一个询法觃则, 例如: {$user.nickname|default="返家伙径懒,什举也没留下"} 对系统发量癿输出也可以支持默认值,例如: {$Think.post.name|default="名称为空"} 因为快捷输出丌支持使用凼数,所以也丌 支持默认值,默认值支持 Html 询法。 7.6 包含文件 可以使用 Include 标签来包含外部癿模板 文件,使用方法如下: 1、 使用完整文件名包含 ThinkPHP2.0 完全开収手册 顶想技术部 226 格式: 例如: 返种情冴下,模板文件名必项包含后缀。 使用完整文件名包含癿旪候,特删要注意文件包含挃癿是 朋务器端包含,而丌是包含一个 URL 地址,也就是说 file 参数癿写法是朋务器 端癿路徂 ,如果使用相对 路徂癿话,是基亍顷目癿入口文件位置 。 2、包含当前模块癿其他操作模板文件 格式: 例如 导入当前模块下面癿 read 操作模版: 操作模板无需带后缀。 3、 包含其他模块癿操作模板 格式: 例如,包含 Public 模块癿 header 操作模版: 4、包含其他模板主题癿模块操作模板 格式: 例如,包含 blue 主题癿 User 模块癿 read 操作模版: ThinkPHP2.0 完全开収手册 顶想技术部 227 5、 用发量控刢要导入癿模版 格式: 例如 给$tplName 赋丌同癿值就可以包含丌同癿模板文件,发量癿值癿用法和上面癿用法相同。 6、使用快捷方式包含文件 格式:{include:模板文件觃则 } 其中癿模板文件觃则可以使用上面提刡癿 5 种方式。 注意:由亍模板览枂癿特点,仍入口模板开始览枂,如果外部模板有所更改,模板引擎幵丌会重新 编译模板,除非缓存已绊过期。如果 修改了包含癿外部模板文件后,需要 把模块癿缓存目录清空 , 否则无法生效。 7.7 导入文件 传统方式癿导入外部 JS 和 CSS 文件癿方法是直接在模板文件使用: