barcodex/temple

简单的平面文本处理器

dev-master 2015-04-04 19:28 UTC

This package is not auto-updated.

Last update: 2024-09-28 17:31:26 UTC


README

Temple是一个轻量级的文本处理器,适用于喜欢厚重控制器的MVC系统。

尽管模板处理假设从某处读取模板内容,但Temple只处理文本,将实现模板读取/写入/缓存的任务留给使用库的库。如果您想查看实现此方面的项目示例,请查看Templar

## 安装

使用Composer安装Temple,通过将需求添加到项目的composer.json中

{
   "require": {
        "barcodex/temple": "*" 
   } 
}

或在命令行中要求它

composer require barcodex/temple:* 

基本用法

基本上,Processor类只是一个静态方法库,提供动态填充文本数据。

我们传递给Processor方法的,只是一个普通的PHP关联数组,其中元素可以是标量值或其它数组。

<?php

require_once "vendor/autoload.php";

use Temple\Processor;

$template = 'Hello, {{name.first}} {{name.last}}!';
$params = array('name' => array('first' => 'John', 'last' => 'Doe'));

print Processor::doText($template, $params) . PHP_EOL;

正如您可以从这个片段中看到的那样,标签遵循mustache形式,很大程度上像在Twig中。然而,与Twig不同,Temple中的处理没有控制流。没有条件语句和循环,一切都是平的。这意味着Temple\Processor的调用者负责为模板准备所有数据,并在需要时处理子模板。

所以,如果您不喜欢厚重控制器和愚蠢模板的想法,请另寻他处。实际上,有Twig,它非常好,试试看。

然而,如果您仍然在这页上,这里有您可能会喜欢的愚蠢模板的理由

  • 您可以使用相同的模板与不同的后端。
  • HTML设计师不需要了解控制流语法。他们仍然需要训练自己以对{{}} 标签视而不见
  • 您可以在Temple周围构建自己的缓存和翻译逻辑
  • 如果您想,您可以处理具有不同分隔符的模板(查找doTextVariation()方法)

是的,这听起来可能像是一个DIY套件,实际上Temple就是这样。它只是迈出了第一步,将表示层从后端代码中解耦。

任何后端都可以立即使用Temple,如果它的控制器足够厚重,可以提供所有必要的数据。您甚至可以使用与用不同语言编写的后端相同的模板。这个概念在C#和Perl端口中得到了成功验证。

修饰符

为了使愚蠢的模板稍微智能一些,并从控制器中减轻一些负担,您仍然可以实施一些格式化逻辑,使用修饰符。

这是您在许多年前在Smarty和Django中看到的同样好的旧技术。您只需在模板变量后设置管道,然后使用支持的修饰符之一。

与Smarty不同,Temple不支持标准PHP函数作为修饰符,以保持理论上跨平台。因此,Temple的其它语言端口负责实现相同的修饰符。

修饰符可以更改,这很有趣

<h2>{{name.first|shortener?chars=10|uppercase|htmlsafe}}</h2>

正如您从这个示例中看到的那样,修饰符可以有参数,我们以URI查询字符串参数的形式传递参数。

在某些情况下,修改值的类型在处理过程中发生变化 - Temple确保在应用整个修饰符链后的最终结果是字符串。

突然间,我们意识到Temple也可以用来准备SQL语句!

UPDATE users SET fname='{{name.first|shortener?chars=50|dbsafe}}'
WHERE id = {{id|zero}}

即使不使用某些ORM,您也可以轻松地将控制器与SQL代码解耦。

Processor::applyModifier()方法逐个应用链中的修饰符,选择合适的Modifier类来完成工作。它根据要修改的值的类型决定使用Modifier类的哪个派生类。

目前我们区分了4种不同的情况

  • ArrayModifier - 当基本值是数组时
  • ObjectModifier - 当基本值是对象时
  • NumericModifier - 当基本值是数字时
  • ScalarModifier - 当基本值是字符串时

扩展Temple

这个库的目标是迈出将PHP代码与表示解耦的第一步,所以它远非全面。

好消息是它可以轻松扩展。关于Temple类本身的注释中提供了一些提示,所以您不需要总是回到这份文档来刷新记忆。

您肯定会想要添加更多的修饰符,因此,您可能需要扩展一个或多个Modifier派生类的calculateValue()方法。如果您创建了自己的Modifier派生类,还需要扩展Processor类,以便applyModifier()方法使用正确的类。

扩展Modifier类的示例

<?php

use Temple\ScalarModifier;

class BetterScalarModifier extends ScalarModifier 
{
    public static function calculateValue($modifierName, $modifierParams, $value, $params)
    {
        switch($modifierName) {
            case 'bark': // forget all values and modifiers and just bark
                $value = 'rrr auh auh auh';
                break;
            default:
                $value = parent::calculateValue($modifierName, $modifierParams, $value, $params);
        }
        
        return $value;
    }
}

因为方法的重载版本首先检查它自己的支持修饰符,如果没有找到匹配项,则调用父方法,所以您也可以提供自己的标准修饰符版本。例如,如果您对查询中的SQL值有特殊的清理要求,可以重载'dbsafe'修饰符。扩展标准修饰符的另一个好理由是注入配置文件/日志记录/验证/通知代码。

以下是扩展Processor以使用新的Modifier类派生类的示例

<?php

use Temple\Processor;
use Temple\ObjectModifier;
use Temple\ArrayModifier;
use Temple\NumericModifier;

class BetterProcessor extends Processor 
{
	public static function applyModifier($value, $filters, $params = array()) 
	{
		if (is_object($value)) {
			return ObjectModifier::apply($value, $filters, $params);
		} else if (is_array($value)) {
			return ArrayModifier::apply($value, $filters, $params);
		} else if (is_numeric($value)) {
			return NumericModifier::apply($value, $filters, $params);
		} else {
			return BetterScalarModifier::apply($value, $filters, $params);
		}
	}
}

您可以看到,我们在这里只是将ScalarModifier替换为BetterScalarModifier,因为我们示例中只有一个扩展的类。

## 标准修饰符的参考

让我们快速看一下所有支持的修饰符。它们可以根据应用它们的类、它们期望的参数数量以及它们返回的值的类型进行分类。

如您所见,由于PHP为我们自动进行类型转换,有时修饰符似乎似乎是多余的。但是,Temple语法是为了无后端设计,以便用其他编程语言编写的控制器也可以与相同的模板一起工作。这就是为什么鼓励容忍这种冗余,即使它们似乎不必要,也要使用类型转换修饰符。

修饰符的示例

尽管我们将大部分工作委托给了胖控制器,但结合修饰符使得在模板中计算一些值甚至模拟一些控制流成为可能。

计算值

想象一下,博客文章的分类列表存储在数据库字段'categories'中的JSON字符串。我们可以在不特别准备控制器中的值的情况下显示分配的分类数量

<div class="blog-post-title">
    {{title}} 
    <span class="blog-post-categories-count">{{categories|unserialize|count}}</span>
</div>

### 模拟条件

所以,没有真正的IF条件,但如果您的控制器预先评估了布尔表达式,则可以停止处理修饰符管道并返回一个空字符串。

想象一下,我们想根据客户端类型显示用户名或公司名,并使用不同的样式显示这些信息。这可以通过这样的模板轻松完成

<div class="client {{is_company|fixbool|iftrue|replace?default=company}}">{{display_name}}</div>

占位符'is_company'将被替换为单词'company',这样我们就可以创建一个特殊的CSS类'.client.company'来显示它。

当然,这意味着我们的控制器应该做这项工作。我们必须根据公司类型准备'display_name',并提供'is_company'字段

$userData['is_company'] = ($userData['company_name'] != '');
$userData['display_name'] = ($userData['company_name'] == '') 
    ? $userData['first_name'] . ' ' . $userData['last_name']
    : $userData['company_name'];

在这个示例中,使用链中的'fixbool'修改器只是一个HTML编码者对控制器程序员不信任的例子。); 实际上,Temple的PHP实现明确地将值转换为布尔值,然后再使用iftrue/iffalse修改器进行检查。所以如果你信任PHP将null和空字符串转换为'false',你就可以直接使用这些修改器而不需要进行显式的类型转换。但是,如果你计划在不同语言的处理器实现之间重用模板,更加明确一点总是没有坏处的。

扩展技巧

这个库故意保持尽可能简单,但易于扩展。可参数化的修改器允许实现小巧的代码片段。想象一下,你反复对特定类型的数据执行相同的固定格式化或微观操作 - 你可以使用带有参数的修改器来模拟你通常在控制器中编写的函数。真正酷的是,这些修改器仍然可以访问传递给文本处理器的整个数据数组,该文本处理器稍后使用其修改器解析了特定的{{标签}}。

这使得可以伪造使用此数据作为输入的简单函数。为了举例,让我们想象一个拥有库存以欧元计价的书店,但在线上还以美元和英镑出售。我们可以轻松编写一个修改器,它会打印出会话货币的价格,自动转换价格并添加货币符号。让我们还假设某些CurrencyRate类的getRate($fromCurrency, $toCurrency)静态方法已经实现,显示原始价格的代码如下

<?php

require_once "vendor/autoload.php";

use Temple\Processor;

$template = 'Book {{title}} by {{author}} costs EUR {{price}}';
$book = array('title' => 'Moby Dick', 'author' => 'Herman Melville', 'price' => 12);

print Processor::doText($template, $book)) . PHP_EOL;

所以,我们可以在准备值进行处理的每次都实现转换

$template = 'Book {{title}} by {{author}} costs EUR {{price}}';
$book = array('title' => 'Moby Dick', 'author' => 'Herman Melville', 'price' => 12);
$book['price'] = ('EUR' == $_SESSION['currency']) 
    ? $book['price']
    : $book['price'] * CurrencyRate::getRate('EUR', $_SESSION['currency']);

但是,我们可以将其移动到修改器中

<?php

use Temple\ScalarModifier;

class BetterScalarModifier extends ScalarModifier 
{
    public static function calculateValue($modifierName, $modifierParams, $value, $params)
    {
        switch($modifierName) {
            case 'currency': 
                $price = ($_SESSION['currency'] == 'EUR') ? $value : $value * CurrencyRate::getRate('EUR', $_SESSION['currency']);
                $value = $_SESSION['currency'] . ' ' . $price;
                break;
            default:
                $value = parent::calculateValue($modifierName, $modifierParams, $value, $params);
        }
        
        return $value;
    }
}

然后在我们的模板中,我们只需使用新的'currency'修改器,从而让控制器免于检查会话货币、转换汇率和打印正确货币代码的常规任务

$template = 'Book {{title}} by {{author}} costs {{price|currency}}'; // note: no hardcoded currency code here
$book = array('title' => 'Moby Dick', 'author' => 'Herman Melville', 'price' => 12);