Smarty 图标

您可以根据商标通知使用 Smarty 徽标。

Smarty Template Engine Smarty Template Engine

如需赞助、广告、新闻或其他咨询,请联系我们

使用 Smarty 的网站

广告

最佳实践

尽管 Smarty 解决了业务逻辑(PHP)和演示之间的分离,但这并不意味着它会防止糟糕的实现。与任何其他软件一样,Smarty 也有一个学习曲线。Smarty 不能保证良好的应用设计或演示的正确分离,这一点需要由称职的开发人员和网站设计师来解决。我们将讨论一些更常见的陷阱以及如何避开它们。

注意:在 Smarty 中,我们通常将 PHP 开发人员和模板设计者视为不同的角色。虽然在现实生活中它们可能是同一个人,但最好把它们当作不同的角色来理解,这样才能清楚地理解 PHP 中应该包含什么,模板中应该包含什么。

现在让我们回到第一个问题:将 PHP 嵌入模板。

1. 不要嵌入 PHP!

这是 Smarty 模板中最大的错误。当 Smarty 刚开始开发时,{php} 标记作为一种解决问题的“最后手段”。经验表明,直接嵌入 PHP 不仅没有必要,而且它制造的问题多于解决的问题。强烈不推荐使用 {php} 标记,并且它现已在 Smarty 3 中弃用。

尽管不受推荐,但懒惰的开发人员还是继续依赖 {php},因为这似乎(乍一看)是最快速的实现途径。然而,这种方法只会带来更多问题。正确的方法是实现一个插件,并在模板之外执行 PHP 逻辑。

首先,让我们看看如何使用 Smarty。

示例问题:解决方法错误

假设我们有这个现有的 PHP 函数,它可以获取当前天气预报并在 HTML 表格中显示它。

PHP

function get_forecast($zipcode) {
    // some weather class we use to do the fetching
    include_once('weather.php');
    // get the forecast from the weather station
    $forecast = Weather::forecast($zipcode);
    // now mark up and return
    $output = "<table>";
    $output .= "<tr><td>Temp:</td><td>".$forecast['temp']."</td></tr>";
    $output .="</table>";
    return $output;
}

因此,现在我们的 PHP 开发人员希望在 Smarty 模板中实现它。他可能会选择一个看似简单的路由,并使用 {php} 标签放入其中

Smarty

  
  {* this is a comment *}
  {php}echo get_forecast($zipcode);{/php}
  

因此,我们暂时“转义”模板到 PHP 代码并运行该函数。嗯,我们遇到了问题。我们的 $zipcode 变量是一个模板变量,它不能在 {php} 块中“正常工作”。因此,我们必须从 Smarty 对象中获取它。不用担心,使用更多代码,我们可以访问它

Smarty

{php}echo get_forecast($this->getTemplateVars('zipcode'));{/php}

糟了,我们又遇到了另一个问题。我们的 PHP 函数 get_forecast() 无法使用。我们必须首先包含此文件。假设我们定义了 PHP 常量 MY_APP,并且我们知道相对于该常量的文件路径。因此,我们将添加一条 include() 语句来包含该文件

Smarty

{php}include(dirname(MY_APP).DIRECTORY_SEPARATOR.'forecast.php');
  echo get_forecast($this->getTemplateVars('zipcode'));{/php}

尽管在技术上此语句可以运行,但我们实际上做的是 Smarty 旨在避免的事情:我们现在遇到了语法混乱,并且在我们的模板中有业务逻辑(与演示无关的 PHP 构造和逻辑)。

假设模板设计人员查看输出并决定我们不想要 HTML 表格中的天气。相反,我们想要一个包含某些样式表标记的 <div> 容器。因此,设计人员前往模板进行更改。在模板中发现此项并不是件好事。由于无法通过模板管理标记,因此设计人员需要通知 PHP 开发人员要进行的更改,或自己查找并更改 PHP 代码(糟糕,分离开销掉了)。这可能不是最佳实现。

示例问题:正确解决方法

现在,让我们通过制作一个简单的模板插件来解决相同的问题。通过实现插件,我们将把业务逻辑置于模板之外,并将所有演示元素移入模板,从而建立正确的分离开销。我们既可以在 Smarty 插件目录中创建一个新的插件文件,也可以在我们的应用程序代码中的某个位置创建一个 PHP 函数(或类方法)并使用 Smarty 手动注册它。让我们使用插件文件方法。以下是我们插件文件的外观

PHP

<?php
/*
 * Smarty plugin
 * -------------------------------------------------------------
 * File:     function.get_forecast.php
 * Type:     function
 * Name:     get_forecast
 * Purpose:  gets the weather forecast
 * -------------------------------------------------------------
 */
function smarty_function_get_forecast($params, $smarty)
{
  // some weather class we use to do the fetching
  include_once('weather.php');
  // get the forecast from the weather station
  $forecast = Weather::forecast($params['zipcode']);
  // now mark up and return
  $output = "<table>";
  $output .= "<tr><td>Temp:</td><td>".$forecast['temp']."</td></tr>";
  $output .="</table>";
  return $output;
}
?>

现在让我们在模板中实现此点

Smarty

{get_forecast zipcode=$zipcode}

现在,这看起来好多了。模板现在包含了一些非常容易理解的东西。但我们尚未完成:请记住,我们希望能够处理模板端的演示,因此我们需要将标记移入模板。这也将简化我们的插件逻辑,因为它不再需要处理演示(应该如此)。让我们进行这些更改

PHP

<?php
/*
 * Smarty plugin
 * -------------------------------------------------------------
 * File:     function.get_forecast.php
 * Type:     function
 * Name:     get_forecast
 * Purpose:  gets the weather forecast
 * -------------------------------------------------------------
 */
function smarty_function_get_forecast($params, $smarty)
{
  include_once('weather.php');
  // get the forecast from the weather station
  $forecast = Weather::forecast($params['zipcode']);
  // assign forecast data directly to given template var
  $smarty->assign($params['assign'],$forecast);
}
?>

并在模板中

Smarty

{get_forecast zipcode=$zipcode assign="forecast"}
<div class="forecast">Temp: {$forecast.temp}</div>

我们已将业务逻辑 (PHP) 移入插件,并且将演示 (HTML/CSS) 移入模板。我们完成了!现在将此解决方案与之前的示例进行比较,你可能会认识到程序员和模板设计人员对此的偏好。此语法非常容易理解和维护。

当然,还有其他方法可以解决此问题。你可以简单地将 $forecast 从 PHP 代码分配到模板,从而不必创建 {get_forecast} 插件

PHP

$smarty->assign('forecast',get_forecast($zipcode));

Smarty

<div class="forecast">Temp: {$forecast.temp}</div>

此方法也可以使用。第一种方法允许我们从任何模板中独立获取预测。你可以自己决定最佳的实现方式。

2. 保持 PHP 构造独立!

另一个常见的陷阱是在模板中嵌入 PHP 结构(如对象/类/函数),而更好的做法是将它们分开。让我们看一个编辑文章的示例。我们在页面上显示一篇文章,如果当前用户是编辑,我们还想显示编辑按钮。在我们的 PHP 逻辑中,我们有一个 $roles 对象,我们用它来检查用户的角色

PHP

if($roles->isEditor($_SESSION['user_id'])) { /* do something */ }

开发人员可能会想做的第一件事是将 $roles 对象直接分配给模板,并按原样使用它

Smarty

{if $roles->isEditor($smarty.session.user_id)}
   ... show edit buttons here ...
{/if}

这引入了几个问题。首先,我们正在创建底层 PHP 对象结构与模板之间的紧密耦合,例如,我们不能再更改应用程序而不会直接影响模板。其次,我们正在将应用程序逻辑引入模板(访问用户角色)。第三,这会使模板语法复杂化,并且您会公开可能与演示无关的 PHP 类/方法/参数。模板设计器不需要处理用户角色,他们只需要知道是否显示编辑按钮即可。

以下是保持内容分离的方法。在我们的 PHP 逻辑中,我们将一个简单标记分配给 Smarty

PHP

// assign a flag for Smarty
$smarty->assign('show_edit_buttons',
  $roles->isEditor($_SESSION['user_id']));

并在我们的模板中使用该标记

Smarty

{if $show_edit_buttons}
   ... show edit buttons here ...
{/if}

现在,我们的模板纯粹专注于演示。在某些情况下像 $is_editor 这样的标记名称可能更合适,具体取决于其使用情况。但是你明白了基本思想。保持业务逻辑不变,专注于演示。

现在,在嵌入 PHP 结构到模板中时,这是一个很好的界限。可能会有某些情况下这对你来说更好。你有能力嵌入它们,只要你了解其暗示即可。

3. 保持业务逻辑分离!

创建模板函数来进行一些简洁的事情可能很诱人,但请记住,让它们专注于演示而不是业务逻辑。以下是一个打破分离的编写模板函数的主要示例

Smarty

{sql statement="select * from categories order by catname limit=$start,$limit" assign="result"}
  {result.catname}
{/sql}

这里有几个问题。首先,直接 SQL 语句是业务逻辑。设计人员不应该需要了解任何关于 SQL 或内容来自何处的信息,更不用说确切地控制如何检索此内容了。其次,我们在模板中使用 SQL 语句打开了潜在的安全漏洞。设计人员可以轻易地破坏 SQL 语句,参数可能是错误的,或者(取决于你的参数来自何处)可能会发生恶意注入攻击。以下是上述示例的一个更好的方法,使用了一个插件来对业务逻辑与演示进行明确分离

Smarty

{get_categories limit=10 assign="result"}
  {$result.catname}
{/get_categories}

在我们的 get_categories 块插件中,我们处理参数清除和 SQL 访问,而模板纯粹专注于演示。你还可以从 PHP 中将 $result 直接分配给模板(可能更常见的方法),但这种设计允许我们从任何模板中任意地检索类别。根据你的实现方式自行判断。

4. 如何识别业务与演示逻辑

业务逻辑通常是不直接处理内容呈现或显示的任何逻辑。例如,嵌入在模板中的正则表达式通常通过 PHP 处理,要么采用插件,要么在分配内容之前处理。尽管 Smarty 附带了 regex_replace 修饰符,但通常最好在 PHP 中执行此操作。

下面是使用 regex_replace 的示例。我们希望在描述文本中将所有电子邮件变成链接

Smarty - 模板中的业务逻辑(糟糕!)

{$description|regex_replace:"!(\w+@\w+)!":'<a href="mailto:$1">$1/a>'}

除非您是正则表达式专家,否则此处的内容并不明显。此简洁模板语法暗示我们在模板中使用了业务逻辑。一个更好的方法是使用自定义插件(修饰符)

PHP

<?php
/*
 * Smarty plugin
 * -------------------------------------------------------------
 * File:     modifier.link_emails.php
 * Type:     modifier
 * Name:     link_emails
 * Purpose:  make emails in text into HTML links
 * -------------------------------------------------------------
 */
function smarty_modifier_link_emails($string)
{
    return preg_replace('!(\w+@\w+)!','<a href="mailto:$1">$1</a>',$string);
}
?>

Smarty

{$description|link_emails}

现在,无论模板设计人员是否了解正则表达式,都可以清楚地了解正在发生的事情。

请记住,我们保持模板设计人员和应用程序开发人员角色分开。这样做的目的是定义呈现和业务逻辑之间的分离。如果编辑模板的人很熟悉 PHP,那并不意味着这些规则不适用。目的是将业务逻辑排除在模板之外。这将在快速开发、简洁模板语法和轻松维护方面发挥很大作用。

在决定将逻辑放置在何处(在模板中还是在 PHP 中)时,请尝试将模板集中在呈现上。如果您发现自己在使用模板语法时遇到困难,令语法过于复杂,或试图从模板访问 PHP,那么通常有更好的方法。考虑分离,以及如何以一种使模板语法最小化并集中在呈现上的方式实现它。