ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

php smarty的预保留变量总结

php smarty的预保留变量总结 前言严格说Smarty 里并没有一个叫「预保留变量」的官方分类。这个说法指的是一组以$smarty为前缀、由 Smarty 自己在模板里提供的内置数据常量、配置项、循环信息、请求参数镜像、当前模板信息等等。它们的共同点是不需要你assign()模板里直接就能读到。这里要纠正两个常见误解。其一有人以为{$smarty.now}之类的变量在 PHP 代码里也能直接用——不能它们只在模板作用域里存在PHP 侧要走对应的 API比如用getTemplateVars()看已赋值的变量。其二有人以为这些内置变量是「安全的、系统给的」可以随便输出。事实恰恰相反$smarty.get、$smarty.post这一组是把未经校验的用户输入原样搬进了模板直接输出就是 XSS。本文按用途把这组内置变量分四类梳理并把真正会出问题的地方点出来。示例基于 Smarty 4.x 与 PHP 8.1Smarty 3 也提供同一组变量个别项在新版本中已被建议改用别的写法遇到这类情况本文会标注。文中示例无法在本机执行验证请结合本地实测阅读。一、常量、配置、时间与捕获这一组是日常用得最多的四个。{$smarty.now}返回当前的 Unix 时间戳整数通常配合date_format修饰器使用{* 现在是{$smarty.now|date_format:Y-m-d H:i:s} *}注意一个版本坑date_format修饰器的格式串如果含有%如%Y-%m-%d走的是 PHP 的strftime()风格而PHP 8.1 起strftime()已被废弃会触发 deprecation 提示。新代码请用date()风格的格式串Y-m-d H:i:s这种不含%或者干脆在 PHP 侧date()好再 assign。{$smarty.const.XXX}读取 PHP 常量等价于模板里访问XXXp站点{$smarty.const.APP_NAME}/ppPHP 版本{$smarty.const.PHP_VERSION}/p常量必须在使用前define()过否则模板里取到的是空值。这个入口适合放「部署环境相关、不随请求变化」的值比如站点名、静态资源版本号。{$smarty.config.XXX}读取 Smarty 配置文件里的变量。配置文件默认放在配置目录下用{config_load filesite.conf}加载或者由 PHP 侧加载site_name 演示站点page_size 20{config_load filesite.conf}p站点{$smarty.config.site_name}/p也可以用更老的花括号井号语法直接读{#site_name#}。这种写法在 Smarty 2 时代更常见新项目一般不需要配置文件这层直接assign()更直白。{$smarty.capture.NAME}读取{capture}块捕获的内容用途是「先渲染一段稍后再用」{capture namesidebar}aside这一段被存起来了/aside{/capture}div classmain{$smarty.capture.sidebar}/div{capture}里的内容不会输出到当前位置而是存进$smarty.capture。写布局时需要「先定义后使用」的场景比如把某个区块放在页面底部渲染、但内容在头部生成就靠它。二、循环信息foreach 与 section循环属性有两种写法必须先分清版本写法形式版本元素属性法{$itemindex}、{$itemfirst}Smarty 3 起推荐全局属性法{$smarty.foreach.名字.index}、{$smarty.section.名字.index}Smarty 2 风格需给循环起name属性这里只讲$smarty.系列这一种因为它正是「内置变量」的一部分。要让{$smarty.foreach.名称.属性}可用{foreach}必须带name{foreach from$items itemrow nameitemLoop}li{$smarty.foreach.itemLoop.iteration} / {$smarty.foreach.itemLoop.total}{$row.name}/li{/foreach}{section}的信息挂在$smarty.section下面属性略有不同属性含义index当前下标从 0 开始iteration当前是第几轮从 1 开始first/last是否第一轮 / 最后一轮布尔rownum已循环的次数从 1 开始与iteration相近total循环总次数loop被遍历的数组本身show当前这一轮是否应该输出配合show属性做条件跳过{section namei loop$items}{if $smarty.section.i.first}hr{/if}li{$items[i]}/li{/section}新代码建议直接用{$itemiteration}这一类元素属性法它不依赖name也不会和其他循环的属性互相干扰。三、模板自身信息与定界符{$smarty.template}是当前处理的模板名调试时很有用{$smarty.current_dir}是当前模板所在目录配合{include}写相对路径时会用到。{$smarty.version}是 Smarty 的版本号字符串排查「这台机器上装的是哪一版」时最直接。{$smarty.ldelim}与{$smarty.rdelim}分别输出左、右定界符默认是{和}。要在页面上显示一对花括号、又不想被 Smarty 当成标签解析时用它比写{literal}更轻便要在模板里输出一个花括号可以写 {$smarty.ldelim}{$smarty.rdelim}{$smarty.block.parent}与{$smarty.block.child}属于模板继承体系用{extends}继承父模板、用{block}定义可覆盖区块时父模板里写{$smarty.block.child}表示「把子模板覆写的内容插到这里」子模板里写{$smarty.block.parent}表示「保留父模板原内容的基础上追加」。没用到模板继承的话这两个变量不会出现。四、请求镜像变量好用但危险这一组是把 PHP 的超全局变量搬进模板的镜像模板变量对应 PHP说明{$smarty.get.x}$_GET[x]URL 查询参数{$smarty.post.x}$_POST[x]表单提交{$smarty.cookies.x}$_COOKIE[x]Cookie{$smarty.server.x}$_SERVER[x]服务器与请求环境{$smarty.env.x}$_ENV[x]环境变量{$smarty.session.x}$_SESSION[x]会话数据{$smarty.request.x}$_REQUEST[x]GET/POST/Cookie 的合集部分版本已不再提供不要依赖用法很直观p搜索关键词{$smarty.get.kw}/p但这正是本文要强调的地方这些值全是原始的用户输入没有经过任何过滤。下面两件事必须做到输出必须转义。要么在 PHP 侧打开$smarty-escape_html true要么逐处写{$smarty.get.kw|escape:html}。不要直接参与业务判断。{if $smarty.get.admin 1}这种写法把权限判断建立在请求参数上是典型的设计缺陷。更稳妥的做法是根本不用这一组在 PHP 侧接收参数、校验、清洗再以明确的变量名assign()进去。这样模板里出现的数据都是「已经处理过的」评审时一眼能看出哪些是用户输入。常见坑点❌ 以为{$smarty.get.kw}自带安全处理✅ 它就是$_GET[kw]的镜像原样输出等于把用户输入直接写进页面。全局打开escape_html或逐处|escape:html。❌ 在 PHP 代码里写$smarty.now想拿时间戳✅ 这类变量只存在于模板作用域。PHP 侧直接用time()。❌ 用{$smarty.now|date_format:%Y-%m-%d}在 PHP 8.1 上报 deprecated✅ 改用不含%的格式串{$smarty.now|date_format:Y-m-d}格式串含%时会走已被 PHP 8.1 废弃的strftime()。❌ 用{$smarty.const.FOO}时忘了先define(FOO, ...)✅ 常量必须已定义。未定义常量在 PHP 8 里是Error模板里则表现为取到空值容易误判成「Smarty 坏了」。❌ 用{$smarty.foreach.x.index}却没给{foreach}起name✅ 这种全局写法要求循环有name属性。新代码直接用{$itemindex}不依赖 name。❌ 直接用{$smarty.capture.name}而对应的{capture namename}还没执行✅{capture}必须先于读取执行。模板是从上往下渲染的顺序反了就是空值。❌ 依赖{$smarty.request.x}✅ 这个变量在不同版本中的可用性不一致且合并来源不清晰。要么显式用$smarty.get/$smarty.post要么在 PHP 侧决定取哪个数组。❌ 把{$smarty.server.PATH}之类的环境信息展示给用户✅ 服务器路径、PHP 版本、软件版本都属于敏感信息暴露出去等于给攻击者省事。这类信息只应写进服务端日志。总结内置变量读什么主要注意点{$smarty.now}当前时间戳格式串含%会走已废弃的strftime(){$smarty.const.X}PHP 常量常量需已定义{$smarty.config.X}配置文件项需先加载配置文件{$smarty.capture.X}{capture}捕获的内容必须先捕获后读取{$smarty.foreach.X.Y}循环信息Smarty 2 风格循环需要name属性{$smarty.section.X.Y}section 循环信息属性名与 foreach 不同{$smarty.template}/{$smarty.current_dir}当前模板名 / 目录适合调试{$smarty.version}Smarty 版本号排查环境差异时最有用{$smarty.ldelim}/{$smarty.rdelim}定界符输出花括号时用它{$smarty.block.parent}/child模板继承内容只在继承体系中使用{$smarty.get/post/...}请求参数镜像原始用户输入必须转义一句话结论这组以$smarty为前缀的内置变量价值在于「省一次 assign」代价是把数据来源藏了起来。常量和时间这类只读信息尽可以放心用循环信息建议改用{$itemindex}这类新写法而$smarty.get/$smarty.post这一组除非只是做调试输出否则都应该换成「PHP 侧校验后再 assign」的方式。模板的数据来源越明确安全审查就越省力。
返回列表