news 2026/10/8 13:00:13

php smarty的预保留变量总结

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
php smarty的预保留变量总结

前言


严格说,Smarty 里并没有一个叫「预保留变量」的官方分类。这个说法指的是一组以$smarty为前缀、由 Smarty 自己在模板里提供的内置数据:常量、配置项、循环信息、请求参数镜像、当前模板信息等等。它们的共同点是:不需要你assign(),模板里直接就能读到。


这里要纠正两个常见误解。其一,有人以为{$smarty.now}之类的变量在 PHP 代码里也能直接用——不能,它们只在模板作用域里存在,PHP 侧要走对应的 API(比如用getTemplateVars()看已赋值的变量)。其二,有人以为这些内置变量是「安全的、系统给的」,可以随便输出。事实恰恰相反:$smarty.get、$smarty.post这一组是把未经校验的用户输入原样搬进了模板,直接输出就是 XSS。


本文按用途把这组内置变量分四类梳理,并把真正会出问题的地方点出来。示例基于 Smarty 4.x 与 PHP 8.1;Smarty 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 常量,等价于模板里访问XXX:


<p>站点:{$smarty.const.APP_NAME}</p>
<p>PHP 版本:{$smarty.const.PHP_VERSION}</p>

常量必须在使用前define()过,否则模板里取到的是空值。这个入口适合放「部署环境相关、不随请求变化」的值,比如站点名、静态资源版本号。


{$smarty.config.XXX}读取 Smarty 配置文件里的变量。配置文件默认放在配置目录下,用{config_load file="site.conf"}加载,或者由 PHP 侧加载:


site_name = 演示站点
page_size = 20

{config_load file="site.conf"}
<p>站点:{$smarty.config.site_name}</p>

也可以用更老的花括号井号语法直接读:{#site_name#}。这种写法在 Smarty 2 时代更常见,新项目一般不需要配置文件这层,直接assign()更直白。


{$smarty.capture.NAME}读取{capture}块捕获的内容,用途是「先渲染一段,稍后再用」:


{capture name="sidebar"}
<aside>这一段被存起来了</aside>
{/capture}

<div class="main">
{$smarty.capture.sidebar}
</div>

{capture}里的内容不会输出到当前位置,而是存进$smarty.capture。写布局时需要「先定义后使用」的场景(比如把某个区块放在页面底部渲染、但内容在头部生成)就靠它。


二、循环信息:foreach 与 section


循环属性有两种写法,必须先分清版本:




写法形式版本



元素属性法{$item@index}、{$item@first}Smarty 3 起推荐

全局属性法{$smarty.foreach.名字.index}、{$smarty.section.名字.index}Smarty 2 风格,需给循环起name属性



这里只讲$smarty.系列这一种,因为它正是「内置变量」的一部分。要让{$smarty.foreach.名称.属性}可用,{foreach}必须带name:


{foreach from=$items item=row name=itemLoop}
<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 name=i loop=$items}
{if $smarty.section.i.first}<hr>{/if}
<li>{$items[i]}</li>
{/section}

新代码建议直接用{$item@iteration}这一类元素属性法,它不依赖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>

但这正是本文要强调的地方:这些值全是原始的用户输入,没有经过任何过滤。下面两件事必须做到:



  1. 输出必须转义。要么在 PHP 侧打开$smarty->escape_html = true,要么逐处写{$smarty.get.kw|escape:'html'}。

  2. 不要直接参与业务判断。{if $smarty.get.admin == 1}这种写法把权限判断建立在请求参数上,是典型的设计缺陷。


更稳妥的做法是根本不用这一组:在 PHP 侧接收参数、校验、清洗,再以明确的变量名assign()进去。这样模板里出现的数据都是「已经处理过的」,评审时一眼能看出哪些是用户输入。


常见坑点



  1. ❌ 以为{$smarty.get.kw}自带安全处理


✅ 它就是$_GET['kw']的镜像,原样输出等于把用户输入直接写进页面。全局打开escape_html,或逐处|escape:'html'。



  1. ❌ 在 PHP 代码里写$smarty.now想拿时间戳


✅ 这类变量只存在于模板作用域。PHP 侧直接用time()。



  1. ❌ 用{$smarty.now|date_format:'%Y-%m-%d'},在 PHP 8.1 上报 deprecated


✅ 改用不含%的格式串:{$smarty.now|date_format:'Y-m-d'};格式串含%时会走已被 PHP 8.1 废弃的strftime()。



  1. ❌ 用{$smarty.const.FOO}时忘了先define('FOO', ...)


✅ 常量必须已定义。未定义常量在 PHP 8 里是Error,模板里则表现为取到空值,容易误判成「Smarty 坏了」。



  1. ❌ 用{$smarty.foreach.x.index}却没给{foreach}起name


✅ 这种全局写法要求循环有name属性。新代码直接用{$item@index},不依赖 name。



  1. ❌ 直接用{$smarty.capture.name}而对应的{capture name="name"}还没执行


✅{capture}必须先于读取执行。模板是从上往下渲染的,顺序反了就是空值。



  1. ❌ 依赖{$smarty.request.x}


✅ 这个变量在不同版本中的可用性不一致,且合并来源不清晰。要么显式用$smarty.get/$smarty.post,要么在 PHP 侧决定取哪个数组。



  1. ❌ 把{$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」,代价是把数据来源藏了起来。常量和时间这类只读信息尽可以放心用;循环信息建议改用{$item@index}这类新写法;而$smarty.get/$smarty.post这一组,除非只是做调试输出,否则都应该换成「PHP 侧校验后再 assign」的方式。模板的数据来源越明确,安全审查就越省力。





版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 12:58:27

CUDA与N卡驱动安装全指南:从版本匹配到多版本共存

1. 从一张显卡到一套工具链&#xff1a;CUDA 与 N 卡驱动到底在装什么 很多人第一次接触深度学习或者 GPU 加速计算&#xff0c;都是从一句“先把 CUDA 装上”开始的。但真正动手之后才发现&#xff0c;事情远没有想象中那么简单&#xff1a;驱动版本、CUDA 版本、cuDNN 版本、…

作者头像 李华
网站建设 2026/10/8 12:57:25

基于TPS259483与ATmega1284的工业电源保护方案设计

做工业控制板这几年&#xff0c;我有一台设备因为传感器供电回路的保护设计跟不上&#xff0c;现场一次接线短路直接烧了一片主控&#xff0c;排查了两天才发现罪魁祸首是电源路径上的保护器件动作太慢。后来我把方案整体换成了以 TPS259483AYWPR 电子保险丝为保护核心、ATmega…

作者头像 李华
网站建设 2026/10/8 12:57:10

eFuse+MCU电源保护实战:TPS259483与TM4C1294

哪怕你只做过几块带单片机的板子&#xff0c;也大概率在实验室里烧过电源&#xff1a;负载一短路、输入一抖动、某个传感器线被反接&#xff0c;整条12V轨直接掉电压&#xff0c;下一级MCU跟着复位重启。经验少的时候会怀疑是程序问题&#xff0c;实际上真正该背锅的是电源路径…

作者头像 李华
网站建设 2026/10/8 12:56:26

eFuse 加 MCU 的工业电源路径保护设计:过流、过压与浪涌一次搞定

做嵌入式和工业产品的人&#xff0c;基本都被“电源路径”问题教育过。新板子第一次上电&#xff0c;后端一颗电容短路&#xff0c;输入端的保险丝直接烧断&#xff0c;整个调试被迫中止&#xff1b;或者负载侧有个大电解电容&#xff0c;上电瞬间的浪涌电流把前级母线拉垮&…

作者头像 李华
网站建设 2026/10/8 12:55:42

全栈拆解开源扫地机器人:STM32+ROS2从底层驱动到SLAM导航的完整实践

1. 一台扫地机为什么值得全栈拆解扫地机器人这个品类&#xff0c;市面上从几百块到几千块的机器都有&#xff0c;但真正把它拆开、把每一层软件硬件都讲清楚的资料并不多。大多数人接触到的要么是厂商的宣传页&#xff0c;要么是某个单一模块的教程——比如只讲ROS2建图&#x…

作者头像 李华