企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 使用模板的好处 ThinkPHP内置了一个基于XML的性能卓越的模板引擎 ThinkTemplate,这是一个专门为ThinkPHP服务的内置模板引擎。ThinkTemplate是一个使用了XML标签库技术的编译型模板引擎,支持两种类型的模板标签,使用了动态编译和缓存技术,而且支持自定义标签库。其特点包括: - 支持XML标签库和普通标签的混合定义; - 支持直接使用PHP代码书写; - 支持文件包含; - 支持多级标签嵌套; - 支持布局模板功能; - 一次编译多次运行,编译和运行效率非常高; - 模板文件和布局模板更新,自动更新模板缓存; - 系统变量无需赋值直接输出; - 支持多维数组的快速输出; - 支持模板变量的默认值; - 支持页面代码去除Html空白; - 支持变量组合调节器和格式化功能; - 允许定义模板禁用函数和禁用PHP语法; - 通过标签库方式扩展。 每个模板文件在执行过程中都会生成一个编译后的缓存文件,其实就是一个可以运行的PHP文件。模板缓存默认位于项目的Runtime/模块/Cache目录下面,以模板文件的md5编码作为缓存文件名保存的。如果在模板标签的使用过程中发现问题,可以尝试通过查看模板缓存文件找到问题所在。 内置的模板引擎支持普通标签和XML标签方式两种标签定义,分别用于不同的目的: | 标签类型 | 描述 | |-----|-----| | 普通标签 | 主要用于输出变量和做一些基本的操作 | | XML标签 | 主要完成一些逻辑判断、控制和循环输出,并且可扩展 | 这种方式的结合保证了模板引擎的简洁和强大的有效融合。 # 逻辑概念上的视图 前面讲了控制器和模型,分别对应 MVC 中的 M 和 C。 而ThinkPHP中的V,就是视图,不需要定义视图文件,只是输出视图时,可能需要模板文件TPL,使用时不需要定义单独的视图类。 # 通向模板前的道路 ## 位置 决定模板所在位置的有几个配置: - `DEFAULT_V_LAYER` 视图层名称配置,默认是View,变了应用下的名称也得变 - `DEFAULT_THEME` 默认主题名称配置,默认空。 如果定义了View下会多主题目录 ## 模板中的变量从何而来 ### 1. 控制器里assign赋值 如 `$this->assign('data', 'data');` ### 2. 控制器里的属性 `$this->data = 'data';` ### 3. 模板中assign标签赋值 `<assign name="data" value="data" />` 当然赋值标签后才能获取,前面的看之前控制器有没有赋值data,没有就是空了。 ## 获取模板地址 为了更方便的输出模板文件,新版封装了一个T函数用于生成模板文件名。 用法: T([资源://][模块@][主题/][控制器/]操作,[视图分层])T函数的返回值是一个完整的模板文件名,可以直接用于display和fetch方法进行渲染输出。 例如: ~~~ T('Public/menu'); // 返回 当前模块/View/Public/menu.html T('blue/Public/menu'); // 返回 当前模块/View/blue/Public/menu.html T('Public/menu','Tpl'); // 返回 当前模块/Tpl/Public/menu.html T('Public/menu'); // 如果TMPL_FILE_DEPR 为 _ 返回 当前模块/Tpl/Public_menu.html T('Public/menu'); // 如果TMPL_TEMPLATE_SUFFIX 为.tpl 返回 当前模块/Tpl/Public/menu.tpl T('Admin@Public/menu'); // 返回 Admin/View/Public/menu.html T('Extend://Admin@Public/menu'); // 返回 Extend/Admin/View/Public/menu.html (Extend目录取决于AUTOLOAD_NAMESPACE中的配置) ~~~ 在display方法中直接使用T函数: ~~~ // 使用T函数输出模板 $this->display(T('Admin@Public/menu')); ~~~ > T函数可以输出不同的视图分层模板。 ## 渲染模板 渲染模板输出最常用的是使用display方法,调用格式: display('[模板文件]'[,'字符编码'][,'输出类型'])模板文件的写法支持下面几种: | 用法 | 描述 | |-----|-----| | 不带任何参数 | 自动定位当前操作的模板文件 | | [模块@][控制器:][操作] | 常用写法,支持跨模块 模板主题可以和theme方法配合 | | 完整的模板文件名 | 直接使用完整的模板文件名(包括模板后缀) | 下面是一个最典型的用法,不带任何参数: ~~~ // 不带任何参数 自动定位当前操作的模板文件 $this->display(); ~~~ 表示系统会按照默认规则自动定位模板文件,其规则是: 如果当前没有启用模板主题则定位到:`当前模块/默认视图目录/当前控制器/当前操作.html` 如果有启用模板主题则定位到:`当前模块/默认视图目录/当前主题/当前控制器/当前操作.html` 如果有更改TMPL_FILE_DEPR设置(假设 `'TMPL_FILE_DEPR'=>'_'`)的话,则上面的自动定位规则变成: `当前模块/默认视图目录/当前控制器_当前操作.html` 和 `当前模块/默认视图目录/当前主题/当前控制器_当前操作.html`。 所以通常display方法无需带任何参数即可输出对应的模板,这是模板输出的最简单的用法。 > 通常默认的视图目录是View 如果没有按照模板定义规则来定义模板文件(或者需要调用其他控制器下面的某个模板),可以使用: ~~~ // 指定模板输出 $this->display('edit'); ~~~ 表示调用当前控制器下面的edit模板 ~~~ $this->display('Member:read'); ~~~ 表示调用Member控制器下面的read模板。 如果我们使用了模板主题功能,那么也可以支持跨主题调用,使用: ~~~ $this->theme('blue')->display('User:edit'); ~~~ 表示调用blue主题下面的User控制器的edit模板。 如果你不希望每个主题都重复定义一些相同的模版文件的话,还可以启用差异主题定义方式,设置: ~~~ 'TMPL_LOAD_DEFAULTTHEME'=>true ~~~ 设置后,如果blue主题下面不存在edit模板的话,就会自动定位到默认主题中的edit模板。 渲染输出不需要写模板文件的路径和后缀,确切地说,这里面的控制器和操作并不一定需要有实际对应的控制器和操作,只是一个目录名称和文件名称而已,例如,你的项目里面可能根本没有Public控制器,更没有Public控制器的menu操作,但是一样可以使用 ~~~ $this->display('Public:menu'); ~~~ 输出这个模板文件。理解了这个,模板输出就清晰了。 display方法支持在渲染输出的时候指定输出编码和类型,例如,可以指定编码和类型: ~~~ $this->display('read', 'utf-8', 'text/xml'); ~~~ 表示输出XML页面类型(配合你的应用需求可以输出很多类型)。 事情总有特例,如果的模板目录是自定义的,或者根本不需要按模块进行分目录存放,那么默认的display渲染规则就不能处理,这个时候,我们就需要使用另外一种方式来应对,直接传入模板文件名即可,例如: ~~~ $this->display('./Template/Public/menu.html'); ~~~ 这种方式需要指定模板路径和后缀,这里的Template/Public目录是位于当前项目入口文件位置下面。如果是其他的后缀文件,也支持直接输出,例如:`$this->display('./Template/Public/menu.tpl');` 只要`./Template/Public/menu.tpl`是一个实际存在的模板文件。 > 要注意模板文件位置是相对于项目的入口文件,而不是模板目录。 ## 获取模板内容 如果需要获取渲染模板的输出内容而不是直接输出,可以使用fetch方法。 fetch方法的用法和display基本一致(只是不需要指定输出编码和输出类型): fetch('模板文件')模板文件的调用方法和display方法完全一样,区别就在于fetch方法渲染后不是直接输出,而是返回渲染后的内容,例如: ~~~ $content = $this->fetch('Member:edit'); ~~~ 使用fetch方法获取渲染内容后,你可以进行过滤和替换等操作,或者用于对输出的复杂需求。 ### 渲染内容 如果你没有定义任何模板文件,或者把模板内容存储到数据库中的话,你就需要使用show方法来渲染输出了,show方法的调用格式: show('渲染内容'[,'字符编码'][,'输出类型']) 例如,`$this->show($content);` 也可以指定编码和类型: `$this->show($content, 'utf-8', 'text/xml');` > show方法中的内容也可以支持模板解析。 # 模板使用的技巧 ## 主题 一个模块如果需要支持多套模板文件的话,就可以使用模板主题功能。 默认情况下,没有开启模板主题功能,如果需要开启,设置 **DEFAULT_THEME** 参数即可: ~~~ // 设置默认的模板主题 'DEFAULT_THEME' => 'default' ~~~ 采用模板主题后,需要在视图目录下面创建对应的主题目录,和不启用模板主题的情况相比,模板文件只是多了一层目录: ~~~ View/User/add.html // 没有启用模板主题之前 View/default/User/add.html // 启用模板主题之后 ~~~ 在视图渲染输出之前,我们可以通过动态设置来改变需要使用的模板主题。 ~~~ // 在控制器中动态改变模板主题 $this->theme('blue')->display('add'); ~~~ ## 模板继承 模板继承是一项更加灵活的模板布局方式,模板继承不同于模板布局,甚至来说,应该在模板布局的上层。模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区块(block),然后继承(extend)该基础模板的子模板中就可以对基础模板中定义的区块进行重载。 因此,模板继承的优势其实是设计基础模板中的区块(block)和子模板中替换这些区块。 每个区块由`<block></block>`标签组成。 下面就是基础模板中的一个典型的区块设计(用于设计网站标题): ~~~ <block name="title"><title>网站标题</title></block> ~~~ block标签必须指定name属性来标识当前区块的名称,这个标识在当前模板中应该是唯一的,block标签中可以包含任何模板内容,包括其他标签和变量,例如: ~~~ <block name="title"><title>{$web_title}</title></block> ~~~ 你甚至还可以在区块中加载外部文件: ~~~ <block name="include"><include file="Public:header" /></block> ~~~ 一个模板中可以定义任意多个名称标识不重复的区块,例如下面定义了一个`base.html`基础模板: ~~~ <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <block name="title"><title>标题</title></block> </head> <body> <block name="menu">菜单</block> <block name="left">左边分栏</block> <block name="main">主内容</block> <block name="right">右边分栏</block> <block name="footer">底部</block> </body> </html> ~~~ 然后我们在子模板(其实是当前操作的入口模板)中使用继承: ~~~ <extend name="base" /> <block name="title"><title>{$title}</title></block> <block name="menu"> <a href="/" >首页</a> <a href="/info/" >资讯</a> <a href="/bbs/" >论坛</a> </block> <block name="left"></block> <block name="content"> <volist name="list" id="vo"> <a href="/new/{$vo.id}">{$vo.title}</a><br/> {$vo.content} </volist> </block> <block name="right"> 最新资讯: <volist name="news" id="new"> <a href="/new/{$new.id}">{$new.title}</a><br/> </volist> </block> <block name="footer"> @ThinkPHP2012 版权所有 </block> ~~~ 可以看到,子模板中使用了extend标签定义需要继承的模板,extend标签的用法和include标签一样,你也可以加载其他模板: ~~~ <extend name="Public:base" /> ~~~ 或者使用绝对文件路径加载 ~~~ <extend name="./Template/Public/base.html" /> ~~~ 在当前子模板中,只能定义区块而不能定义其他的模板内容,否则将会直接忽略,并且只能定义基础模板中已经定义的区块。 例如,如果采用下面的定义: ~~~ <block name="title"><title>{$title}</title></block> <a href="/" >首页</a> <a href="/info/" >资讯</a> <a href="/bbs/" >论坛</a> ~~~ 导航部分将是无效的,不会显示在模板中。 在子模板中,可以对基础模板中的区块进行重载定义,如果没有重新定义的话,则表示沿用基础模板中的区块定义,如果定义了一个空的区块,则表示删除基础模板中的该区块内容。 上面的例子,我们就把left区块的内容删除了,其他的区块都进行了重载。 子模板中的区块定义顺序是随意的,模板继承的用法关键在于基础模板如何布局和设计规划了,如果结合原来的布局功能,则会更加灵活。 ## 模板布局 ThinkPHP的模板引擎内置了布局模板功能支持,可以方便的实现模板布局以及布局嵌套功能。 有三种布局模板的支持方式: ### 第一种方式:全局配置方式 这种方式仅需在项目配置文件中添加相关的布局模板配置,就可以简单实现模板布局功能,比较适用于全站使用相同布局的情况,需要配置开启LAYOUT_ON 参数(默认不开启),并且设置布局入口文件名LAYOUT_NAME(默认为layout)。 ~~~ 'LAYOUT_ON'=>true, 'LAYOUT_NAME'=>'layout', ~~~ 开启LAYOUT_ON后,我们的模板渲染流程就有所变化,例如: ~~~ namespace Home\Controller; use Think\Controller; Class UserController extends Controller{ Public function add() { $this->display('add'); } } ~~~ 在不开启LAYOUT_ON布局模板之前,会直接渲染 `Application/Home/View/User/add.html` 模板文件,开启之后,首先会渲染`Application/Home/View/layout.html` 模板,布局模板的写法和其他模板的写法类似,本身也可以支持所有的模板标签以及包含文件,区别在于有一个特定的输出替换变量`{__CONTENT__}`,例如,下面是一个典型的layout.html模板的写法: ~~~ <include file="Public:header" /> {__CONTENT__} <include file="Public:footer" /> ~~~ 读取layout模板之后,会再解析`User/add.html` 模板文件,并把解析后的内容替换到layout布局模板文件的{**CONTENT**} 特定字符串。 当然可以通过设置来改变这个特定的替换字符串,例如: ~~~ 'TMPL_LAYOUT_ITEM' => '{__REPLACE__}' ~~~ > 一个布局模板同时只能有一个特定替换字符串。 采用这种布局方式的情况下,一旦User/add.html 模板文件或者layout.html布局模板文件发生修改,都会导致模板重新编译。 如果需要指定其他位置的布局模板,可以使用: ~~~ 'LAYOUT_NAME'=>'Layout/layoutname', ~~~ 就表示采用`Application/Home/View/Layout/layoutname.html`作为布局模板。 如果某些页面不需要使用布局模板功能,可以在模板文件开头加上 `{__NOLAYOUT__}` 字符串。 如果上面的User/add.html 模板文件里面包含有`{__NOLAYOUT__}`,则即使当前开启布局模板,也不会进行布局模板解析。 ### 第二种方式:模板标签方式 这种布局模板不需要在配置文件中设置任何参数,也不需要开启LAYOUT_ON,直接在模板文件中指定布局模板即可,相关的布局模板调整也在模板中进行。 以前面的输出模板为例,这种方式的入口还是在User/add.html 模板,但是我们可以修改下add模板文件的内容,在头部增加下面的布局标签(记得首先关闭前面的LAYOUT_ON设置,否则可能出现布局循环): ~~~ <layout name="layout" /> ~~~ 表示当前模板文件需要使用`layout.html` 布局模板文件,而布局模板文件的写法和上面第一种方式是一样的。当渲染`User/add.html` 模板文件的时候,如果读取到layout标签,则会把当前模板的解析内容替换到layout布局模板的{**CONTENT**} 特定字符串。 一个模板文件中只能使用一个布局模板,如果模板文件中没有使用任何layout标签则表示当前模板不使用任何布局。 如果需要使用其他的布局模板,可以改变layout的name属性,例如: ~~~ <layout name="newlayout" /> ~~~ 还可以在layout标签里面指定要替换的特定字符串: ~~~ <layout name="Layout/newlayout" replace="{__REPLACE__}" /> ~~~ 由于所有include标签引入的文件都支持layout标签,所以,我们可以借助layout标签和include标签相结合的方式实现布局模板的嵌套。例如,上面的例子 ~~~ <include file="Public:header" /> <div id="main" class="main" > {__CONTENT__} </div> <include file="Public:footer" /> ~~~ 在引入的header和footer模板文件中也可以添加layout标签,例如header模板文件的开头添加如下标签: ~~~ <layout name="menu" /> ~~~ 这样就实现了在头部模板中引用了menu布局模板。 也可以采用两种布局方式的结合,可以实现更加复杂的模板布局以及嵌套功能。 ### 第三种方式:使用layout控制模板布局 使用内置的layout方法可以更灵活的在程序中控制模板输出的布局功能,尤其适用于局部需要布局或者关闭布局的情况,这种方式也不需要在配置文件中开启LAYOUT_ON。例如: ~~~ namespace Home\Controller; use Think\Controller; Class UserController extends Controller{ Public function add() { layout(true); $this->display('add'); } } ~~~ 表示当前的模板输出启用了布局模板,并且采用默认的layout布局模板。 如果当前输出需要使用不同的布局模板,可以动态的指定布局模板名称,例如: ~~~ namespace Home\Controller; use Think\Controller; Class UserController extends Controller{ Public function add() { layout('Layout/newlayout'); $this->display('add'); } } ~~~ 或者使用layout方法动态关闭当前模板的布局功能(这种用法可以配合第一种布局方式,例如全局配置已经开启了布局,可以在某个页面单独关闭): ~~~ namespace Home\Controller; use Think\Controller; Class UserController extends Controller{ Public function add() { layout(false); // 临时关闭当前模板的布局功能 $this->display('add'); } } ~~~ > 三种模板布局方式中,第一种和第三种是在程序中配置实现模板布局,第二种方式则是单纯通过模板标签在模板中使用布局。具体选择什么方式,需要根据项目的实际情况来了。 ## 模板注释 模板支持注释功能,该注释文字在最终页面不会显示,仅供模板制作人员参考和识别。 ## 单行注释 格式: ~~~ {/* 注释内容 */ } 或 {// 注释内容 } ~~~ 例如: ~~~ {// 这是模板注释内容 } ~~~ > 注意{和注释标记之间不能有空格。 ## 多行注释 支持多行注释,例如: ~~~ {/* 这是模板 注释内容*/ } ~~~ 模板注释支持多行,模板注释在生成编译缓存文件后会自动删除,这一点和Html的注释不同。 ## 原样输出 可以使用`literal`标签来防止模板标签被解析,例如: ~~~ <literal> <if condition="$name eq 1 "> value1 <elseif condition="$name eq 2"/>value2 <else /> value3 </if> </literal> ~~~ 上面的if标签被literal标签包含,因此if标签里面的内容并不会被模板引擎解析,而是保持原样输出。 如果你的php标签中需要输出类似{$user} 或者 XML标签的情况,可以通过添加literal标签解决混淆问题,例如: ~~~ <php>echo '{$Think.config.CUSTOM.'.$key.'}';</php> ~~~ 这个php标签中的{$Think 可能会被模板引擎误当做标签解析,解决的办法就是加上literal,例如: ~~~ <php><literal>echo '{$Think.config.CUSTOM.'.$key.'}';</literal></php> ~~~ Literal标签还可以用于页面的JS代码外层,确保JS代码中的某些用法和模板引擎不产生混淆。 总之,所有可能和内置模板引擎的解析规则冲突的地方都可以使用literal标签处理。 # 模板中TP使用 Think模板引擎使用,其他的如Smarty模板可以参考手册 ## 输出变量 在模板中输出变量的方法很简单,例如,在控制器中我们给模板变量赋值: ~~~ $name = 'ThinkPHP'; $this->assign('name',$name); $this->display(); ~~~ 然后就可以在模板中使用: ~~~ Hello,{$name}! ~~~ 模板编译后的结果就是: ~~~ Hello,<?php echo($name);?>! ~~~ 这样,运行的时候就会在模板中显示: `Hello,ThinkPHP!` 注意模板标签的`{`和`$`之间不能有任何的空格,否则标签无效。所以,下面的标签 ~~~ Hello,{ $name}! ~~~ 将不会正常输出name变量,而是直接保持不变输出: `Hello,{ $name}!` 普通标签默认开始标记是`{`,结束标记是 `}`。也可以通过设置`TMPL_L_DELIM`和`TMPL_R_DELIM`进行更改。例如,我们在项目配置文件中定义: ~~~ 'TMPL_L_DELIM'=>'<{', 'TMPL_R_DELIM'=>'}>', ~~~ 那么,上面的变量输出标签就应该改成: ~~~ Hello,<{$name}>! ~~~ 后面的内容我们都以默认的标签定义来说明。 模板标签的变量输出根据变量类型有所区别,刚才我们输出的是字符串变量,如果是数组变量, ~~~ $data['name'] = 'ThinkPHP'; $data['email'] = 'thinkphp@qq.com'; $this->assign('data',$data); ~~~ 那么,在模板中我们可以用下面的方式输出: ~~~ Name:{$data.name} Email:{$data.email} ~~~ 或者用下面的方式也是有效: ~~~ Name:{$data['name']} Email:{$data['email']} ~~~ > 当我们要输出多维数组的时候,往往要采用后面一种方式。 如果data变量是一个对象(并且包含有name和email两个属性),那么可以用下面的方式输出: ~~~ Name:{$data:name} Email:{$data:email} ~~~ 或者 ~~~ Name:{$data->name} Email:{$data->email} ~~~ ## 系统变量 ### 系统变量输出 普通的模板变量需要首先赋值后才能在模板中输出,但是系统变量则不需要,可以直接在模板中输出,系统变量的输出通常以**{$Think** 打头,例如: ~~~ {$Think.server.script_name} // 输出$_SERVER['SCRIPT_NAME']变量 {$Think.session.user_id} // 输出$_SESSION['user_id']变量 {$Think.get.pageNumber} // 输出$_GET['pageNumber']变量 {$Think.cookie.name} // 输出$_COOKIE['name']变量 ~~~ 支持输出 `$_SERVER`、`$_ENV`、 `$_POST`、 `$_GET`、 `$_REQUEST`、`$_SESSION`和 `$_COOKIE`变量。 ### 常量输出 还可以输出常量 ~~~ {$Think.const.MODULE_NAME} ~~~ 或者直接使用 ~~~ {$Think.MODULE_NAME} ~~~ ## 配置输出 输出配置参数使用: ~~~ {$Think.config.db_charset} {$Think.config.url_model} ~~~ ### 语言变量 输出语言变量可以使用: ~~~ {$Think.lang.page_error} {$Think.lang.var_error} ~~~ ## 使用函数 我们往往需要对模板输出变量使用函数,可以使用: ~~~ {$data.name|md5} ~~~ 编译后的结果是: ~~~ <?php echo (md5($data['name'])); ?> ~~~ 如果函数有多个参数需要调用,则使用: ~~~ {$create_time|date="y-m-d",###} ~~~ 表示date函数传入两个参数,每个参数用逗号分割,这里第一个参数是`y-m-d`,第二个参数是前面要输出的`create_time`变量,因为该变量是第二个参数,因此需要用###标识变量位置,编译后的结果是: ~~~ <?php echo (date("y-m-d",$create_time)); ?> ~~~ 如果前面输出的变量在后面定义的函数的第一个参数,则可以直接使用: ~~~ {$data.name|substr=0,3} ~~~ 表示输出 ~~~ <?php echo (substr($data['name'],0,3)); ?> ~~~ 虽然也可以使用: ~~~ {$data.name|substr=###,0,3} ~~~ 但完全没用这个必要。 还可以支持多个函数过滤,多个函数之间用“|”分割即可,例如: ~~~ {$name|md5|strtoupper|substr=0,3} ~~~ 编译后的结果是: ~~~ <?php echo (substr(strtoupper(md5($name)),0,3)); ?> ~~~ 函数会按照从左到右的顺序依次调用。 如果你觉得这样写起来比较麻烦,也可以直接这样写: ~~~ {:substr(strtoupper(md5($name)),0,3)} ~~~ > 变量输出使用的函数可以支持内置的PHP函数或者用户自定义函数,甚至是静态方法。 ## 默认值输出 我们可以给变量输出提供默认值,例如: ~~~ {$user.nickname|default="这家伙很懒,什么也没留下"} ~~~ 对系统变量依然可以支持默认值输出,例如: ~~~ {$Think.get.name|default="名称为空"} ~~~ 默认值和函数可以同时使用,例如: ~~~ {$Think.get.name|getName|default="名称为空"} ~~~ > 默认值输出可以避免报notice错误,当某些数组键值没有定义的时候 ## 使用运算符 我们可以对模板输出使用运算符,包括对“+”“ ” “*” “/”和“%”的支持。 例如: | 运算符 | 使用示例 | |-----|-----| | + | {$a+$b} | | - | {$a-$b} | | * | {$a*$b} | | / | {$a/$b} | | % | {$a%$b} | | ++ | {$a++} 或 {++$a} | | -- | {$a--} 或 {--$a} | | 综合运算 | {$a+$b*10+$c} | 在使用运算符的时候,不再支持点语法和常规的函数用法,例如: ~~~ {$user.score+10} //错误的 {$user['score']+10} //正确的 {$user['score']*$user['level']} //正确的 {$user['score']|myFun*10} //错误的 {$user['score']+myFun($user['level'])} //正确的 ~~~ > 总结就是运算符只支持原生变量写法 ## 标签库 内置的模板引擎除了支持普通变量的输出之外,更强大的地方在于标签库功能。 标签库类似于Java的Struts中的JSP标签库,每一个标签库是一个独立的标签库文件,标签库中的每一个标签完成某个功能,采用XML标签方式(包括开放标签和闭合标签)。 标签库分为内置和扩展标签库,内置标签库是Cx标签库。 ### 导入标签库 使用taglib标签导入当前模板中需要使用的标签库,例如: ~~~ <taglib name="html" /> ~~~ > 如果没有定义html标签库的话,则导入无效。 也可以导入多个标签库,使用: ~~~ <taglib name="html,article" /> ~~~ 导入标签库后,就可以使用标签库中定义的标签了,假设article标签库中定义了read标签: ~~~ <article:read name="hello" id="data" > {$data.id}:{$data.title} </article:read> ~~~ 在上面的标签中,`<article:read>... </article:read>` 就是闭合标签,起始和结束标签必须成对出现。 如果是 `<article:read name="hello" />` 就是开放标签。 > 闭合和开放标签取决于标签库中的定义,一旦定义后就不能混淆使用,否则就会出现错误。 ### 内置标签 内置标签库无需导入即可使用,并且不需要加XML中的标签库前缀,ThinkPHP内置的标签库是Cx标签库,所以,Cx标签库中的所有标签,我们可以在模板文件中直接使用,我们可以这样使用: ~~~ <eq name="status" value="1"> 正常 </eq> ~~~ 如果Cx不是内置标签的话,可能就需要这么使用了: ~~~ <cx:eq name="status" value="1 > 正常 </cx:eq> ~~~ 更多的Cx标签库中的标签用法,参考[内置标签](#)。 内置标签库可以简化模板中标签的使用,所以,我们还可以把其他的标签库定义为内置标签库(前提是多个标签库没有标签冲突的情况),例如: ~~~ 'TAGLIB_BUILD_IN' => 'cx,article' ~~~ 配置后,上面的标签用法就可以改为: ~~~ <read name="hello" id="data" > {$data.id}:{$data.title} </read> ~~~ ### 标签库预加载 标签库预加载是指无需手动在模板文件中导入标签库即可使用标签库中的标签,通常用于某个标签库需要被大多数模板使用的情况。 在应用或者模块的配置文件中添加: ~~~ 'TAGLIB_PRE_LOAD' => 'article,html' ~~~ 设置后,模板文件就不再需要使用 ~~~ <taglib name="html,article" /> ~~~ 但是仍然可以在模板中调用: ~~~ <article:read name="hello" id="data" > {$data.id}:{$data.title} </article:read> ~~~ ## 三元运算 模板可以支持三元运算符,例如: ~~~ {$status?'正常':'错误'} {$info['status']?$info['msg']:$info['error']} ~~~ > 注意:三元运算符中暂时不支持点语法。 ## 内置标签 变量输出使用普通标签就足够了,但是要完成其他的控制、循环和判断功能,就需要借助模板引擎的标签库功能了,系统内置标签库的所有标签无需引入标签库即可直接使用。 内置标签包括: | 标签名 | 作用 | 包含属性 | |-----|-----|-----| | include | 包含外部模板文件(闭合) | file | | import | 导入资源文件(闭合 包括js css load别名) | file,href,type,value,basepath | | volist | 循环数组数据输出 | name,id,offset,length,key,mod | | foreach | 数组或对象遍历输出 | name,item,key | | for | For循环数据输出 | name,from,to,before,step | | switch | 分支判断输出 | name | | case | 分支判断输出(必须和switch配套使用) | value,break | | default | 默认情况输出(闭合 必须和switch配套使用) | 无 | | compare | 比较输出(包括eq neq lt gt egt elt heq nheq等别名) | name,value,type | | range | 范围判断输出(包括in notin between notbetween别名) | name,value,type | | present | 判断是否赋值 | name | | notpresent | 判断是否尚未赋值 | name | | empty | 判断数据是否为空 | name | | notempty | 判断数据是否不为空 | name | | defined | 判断常量是否定义 | name | | notdefined | 判断常量是否未定义 | name | | define | 常量定义(闭合) | name,value | | assign | 变量赋值(闭合) | name,value | | if | 条件判断输出 | condition | | elseif | 条件判断输出(闭合 必须和if标签配套使用) | condition | | else | 条件不成立输出(闭合 可用于其他标签) | 无 | | php | 使用php代码 | 无 | 具体使用参见官方手册。常用的Volist的几个参数要注意,比如变量接收的是name 不是id,奇偶判断用mod 等。 # 自行了解的 ## 修改定界符 ## 模板布局 ## 标签嵌套