wol-soft/php-micro-template

PHP 的一个极简模板引擎

1.9.0 2023-09-05 14:24 UTC

This package is auto-updated.

Last update: 2024-08-29 14:49:35 UTC


README

Latest Version Maintainability Build Status Coverage Status MIT License

php-micro-template

基于正则表达式的 PHP 极简、轻量级模板引擎。

功能

  • 在模板内替换变量
  • 遍历数组或可迭代对象
  • 条件部分
  • 传递对象
  • 调用函数

要求

  • 至少需要 PHP 7.1

安装

推荐通过 Composer 安装 php-micro-template

$ composer require wol-soft/php-micro-template

示例

首先创建您的模板文件

<html>
    <h1>{{ pageTitle }}</h1>

    <ul class="row">
        {% foreach products as product %}
            {% if product.isVisible() %}
                <li class="product">
                    <span>{{ productHead }}</span>
                    <span>{{ product.getTitle() }}</span>
                </li>
            {% endif %}
        {% endforeach %}
    </ul>

    {% if showVersion %}
        <div class="version">1.0.1</div>
    {% endif %}
</html>

之后创建 Render 类的新实例并渲染模板

<?php

use PHPMicroTemplate\Render;

/* ... */

$render = new Render(__DIR__ . '/Templates/');

$result = $render->renderTemplate(
    'productList.template',
    [
        'pageTitle' => $translator->translate('availableProducts'),
        'productHead' => $translator->translate('product'),
        'products' => $products,
        'showVersion' => true
    ]
);

/* ... */

您也可以准备一个包含模板的字符串,而不是将其保存到文件中

<?php

use PHPMicroTemplate\Render;

/* ... */

$myPartialTemplate = '
    {% foreach products as product %}
        {% if product.isVisible() %}
            <li class="product">
                <span>{{ productHead }}</span>
                <span>{{ product.getTitle() }}</span>
            </li>
        {% endif %}
    {% endforeach %}
';

$render = new Render();

$result = $render->renderTemplateString(
    $myPartialTemplate,
    [
        'productHead' => $translator->translate('product'),
        'products' => $products
    ]
);

/* ... */

变量替换

分配给模板并直接使用的值将被转换为字符串。对于分配的对象,您可以调用返回值的方法。然后返回的值将被转换为字符串。支持的常量值包括整数、单引号中的字符串和布尔值(true、false)。

{{ simpleValue }}
{{ myObject.getProperty() }}
{{ 'Hello World' }}
{{ 12345 }}

您提供的数据可能是一个嵌套数组,可以在模板中解析

$render->renderTemplateString(
    '{{ render.productRender.renderProductName(product.details.name) }}',
    [
        'product' => [
            'details' => [
                'name' => 'MyProduct',
            ],
        ],
        'render' => [
            'productRender' => new ProductRender(),
        ]
    ]
);

此外,还可以从模板中访问对象的公共属性

$person = new stdClass();
$person->name = 'Hans';

$render->renderTemplateString(
    '{{ person.name }}',
    [
        'person' => $person,
    ]
);

默认情况下,未提供的已使用变量将导致 UndefinedSymbolException。您可以通过 onResolveError 注册回调函数来处理未解析的变量错误。回调函数必须实现签名 function (string $unresolvedVariable): string。提供的 $unresolvedVariable 将包含整个失败解析的表达式(例如,myUnresolvedVariablemyUnresolvedObject.render(var1, var2))。

$render->onResolveError(function (string $var): string {
    return 'Undefined';
});

// will result in "Person name: Undefined"
$result = $render->renderTemplateString('Person name: {{ name }}');

循环

如果您分配了一个数组或可迭代对象,则可以使用 foreach 循环进行遍历。

{% foreach products as product %}
    <span>{{ product.getTitle() }}</span>
{% endforeach %}

循环中可以访问父作用域的所有变量,以及循环的当前项。可以嵌套多个 foreach 循环(比较测试)。您还可以提供返回数组或可迭代对象的函数

{% foreach product.getIngredients() as ingredient %}
    <span>{{ ingredient.getTitle() }}</span>
{% endforeach %}

循环支持使用键值对

{% foreach products as bestSellerNumber, product %}
    <b>Bestseller Nr. {{ bestSellerNumber }}:</b>{{ product.getTitle() }}<br/>
{% endforeach %}

条件部分

使用 if 语句可以创建条件部分。条件可以是转换为 bool 的值或调用对象上的方法。在这种情况下,函数的返回值将被转换为 bool。不提供通过运算符组合的单个条件中的多个值,也不提供计算或类似的其他函数。对于更复杂的条件,请比较 函数调用 部分与 ViewHelper-对象。

{% if showProducts %}
    {% if product.isVisible() %}
        <span>{{ product.getTitle() }}</span>
    {% else %}
        <span>Product {{ product.getTitle() }} currently not available</span>
    {% endif %}
{% endif %}

可以嵌套多个 if 语句。要反转 if 条件,可以使用关键字 not

{% if not product.isVisible() %}
    <span>Product {{ product.getTitle() }} currently not available</span>
{% endif %}

函数调用

调用的方法可以接受参数。允许的参数是当前作用域中取出的变量或当前作用域中可用对象的另一个函数调用,以及支持的常量值(整数、单引号中的字符串和布尔值(true、false))。例如,可以将 ViewHelper-对象分配给渲染过程,并在模板中使用 ViewHelper 的方法来执行模板内的复杂逻辑。

<?php

use PHPMicroTemplate\Render;

/* ... */

class ViewHelper
{
    public function count(iterable $list): int
    {
        return count($list);
    }

    public function sum(float ...$values): float
    {
        return array_sum($values);
    }

    public function weight(string $label, int $weight = 400): string
    {
        return sprintf('<span style="font-weight: %d;">%s</span>', $weight, $label);
    }
}

/* ... */

$render = new Render(__DIR__ . '/Templates/');

$result = $render->renderTemplate(
    'functionExample.template',
    [
        'viewHelper' => new ViewHelper(),
        'currencyFormatter' => new CurrencyFormatter(),
        'basePrice' => 3.00,
        'products' => $products
    ]
);

/* ... */
<html>
    <p>Products: {{ viewHelper.count(products) }}
    <ul class="row">
        {% foreach products as product %}
            <li class="product">
                <span>{{ viewHelper.weightFont(product.getTitle(), 600) }}</span>
                <span>Price: {{
                    currencyFormatter.format(
                        viewHelper.sum(
                            product.getPrice(),
                            basePrice
                        )
                    )
                }}</span>
            </li>
        {% endforeach %}
    </ul>
</html>

此外,还可以直接在模板中使用 PHP 全局函数以及分配回调方法

<?php

use PHPMicroTemplate\Render;

/* ... */

$render = new Render(__DIR__ . '/Templates/');

$result = $render->renderTemplate(
    'functionExample.template',
    [
        'customCallback' => function(string $in): string {
            return trim(strtoupper($in));
        },
    ]
);

/* ... */
<html>
    <p>{{ customCallback('products') }}</p>
    <span>{{ strtolower('UNDER CONSTRUCTION') }}</span>
</html>

空白字符容忍度

模板语法对空白字符具有容忍度,因此下面的模板是完全可以接受的

{%if
    product.getCategories()
%}
    <p>Categories:</p>
    <ul>
    {%foreach
         product.getCategories()
            as
         category
    %}
        <li>{{product.getTitle()} [{{   category   }}]</li>
    {%endforeach%}
    </ul>
{%endif%}