mkgor/puff

适用于PHP的灵活且轻量级的模板引擎

v1.0 2019-12-28 17:59 UTC

This package is auto-updated.

Last update: 2024-09-29 05:49:29 UTC


README

Coverage GitHub repo size Packagist GitHub All Releases

Puff

Logo

一个可hack和闪电般的快的PHP模板引擎,灵感来自Twig。

内容

需求

  • PHP 7.1或更高版本(用于Puff核心)
  • Mbstring扩展(用于UpperCaseFilter)

安装

通过composer安装Puff

composer require mkgor/puff

快速入门

<?php

require_once "vendor/autoload.php";

$engine = new \Puff\Engine([
    'modules' => [
        new \Puff\Modules\Core\CoreModule()
    ]
]);

echo $engine->render(__DIR__ . '/template.puff.html', [
    'variable' => 'Puff'
]);
<html>
<body>
Hello, i am [[ variable ]]
</body>
</html>

重要! 如果需要所有基本语句(如 forif-else 等),请在此处初始化CoreModule。
同样重要! Puff会自动将变量名中的'-'和'.'符号转换为'_'。

规格

基本语句的执行说明,如 if-elseforimport 等,通过字符组合 [% %] 表示

要显示变量的值,应使用 [[ ]]

适用于

类似于PHP的 foreach 循环

<div class='products'>
  [% for products in item %]
  <div class='item'>
    <b>Id: </b> [[ item.id ]]
    <b>Name: </b> [[ item.name ]]
  </div>
  [% end %]
</div>

if-else

简单的if-else实现

[% if variable == true %]
  <b>[[ variable ]] is true</b>
[% else %]
  <b>[[ variable ]] is false</b>
[% end %]

导入

您可以使用 import 将模板导入另一个模板中

如果您未指定模板目录路径,则应设置相对于项目根目录的模板路径

重要! 不要忘记,您正在将当前模板中的所有变量注入到导入的模板中。如果导入的模板使用某些变量(例如,在页眉中显示页面标题),您应在 render 方法中指定它

[% import src='base.puff.html' %]

<body>
<div class='content'>
...
</body>

设置

创建/更新变量

[% set variable = 'test' %]

<!-- will display 'test' -->
<b>[[ variable ]]</b>

[% set variable = 'test2' %]

<!-- will display 'test2' -->
<b>[[ variable ]]</b>

扩展和位置

使用此标签定义父模板,并使用位置标签从当前模板加载数据到其中。

用法

主模板

[% position name="title" %]
Home page
[% endposition %]

[% position name="content" %]
Hello, [[ name ]]
[% endposition %]

[% extends src="base.puff.html" %]

父模板

<html>
<head>
<title>[% position for="title" %]</title>
</head>
<body>
[% position for="content' %]
</body>
</html>

过滤器系统

您可以在显示之前修改一些变量,或者如果某些语句支持过滤器,您可以在使用之前修改变量。

要指定变量的过滤器,应通过 ~ 符号指定。

示例

[[ variable ~ uppercase ~ transliterate ]]
[[ int_variable ~ round(1) ]]

您还可以在 for 语句中使用过滤器

[% for products ~ uppercase in item %]
  <!-- uppercase filter recursiely transforms all items of array into uppercase -->
  [[ item.name ]]
[% end %]

您可以创建自己的过滤器。请参阅 扩展系统 块了解如何创建。

扩展系统

Puff是可扩展的,因此您可以创建自己的模块,这些模块可以包含您自己的语句和过滤器。

要创建模块,只需创建一个实现 Puff\Modules\ModuleInterface 的类,并将其插入到 Engine 配置的 modules 数组中。

$engine = new \Puff\Engine([
    'modules' => [
        new \Puff\Modules\Core\CoreModule()
        new \Puff\Modules\NewModule\MyModule()
    ]
]);

重要! 如果需要所有基本语句(如 forif-else 等),请在此处初始化CoreModule。

创建新的语句(元素)

要创建新元素,您应该创建一个类,该类应该扩展 Puff\Compilation\Element\AbstractElement

您应在您的 Module 类的 setUp() 方法中指定元素的类

...
/**
     * Returns an array of elements and filters which will be initialized
     *
     * @return array
     */
    public function setUp(): array
    {
        return [
            'elements' => [
                'new_element' => new NewElement(),
                'another_new_element' => new AnotherNewElement()
            ],
            ...
        ];
    }
    ...

现在,您的元素可以通过 test_element 关键字在模板中使用

元素的 process 方法应返回PHP代码。

<?php

use Puff\Compilation\Element\AbstractElement;

/**
 * Class NewElement
 */
class NewElement extends AbstractElement
{
    /**
     * @param array $attributes
     * @return mixed
     */
    public function process(array $attributes)
    {
        return "<?php echo 'Some result of processing'; ?>";
    }
}

您可以提供一些属性处理规则。默认情况下,处理方式如下

[% element attribute='some_attribute' %]

Process method will get an array:

[
  'attribute' => 'some_attribute'
]

但如果你想创建像 for 这样的语句(它不使用像 "attribute='attr'" 这样的属性)

您可以通过在元素类中指定 handleAttributes 方法来提供自己的属性处理规则

它获取一个包含所有元素数组的令牌化器,并 应该返回一个数组

[% new_element attribute anotherAttribute ~ 123 %]

handleAttributes() will get an array:

[
  'new_element',
  'attribute',
  'anotherAttribute',
  '~',
  '123'
]
<?php

use Puff\Compilation\Element\AbstractElement;

/**
 * Class NewElement
 */
class NewElement extends AbstractElement
{
    /**
     * @param array $attributes
     * @return mixed
     */
    public function process(array $attributes)
    {
        return "<?php echo $attributes['result']; ?>";
    }
    
    public function handleAttributes(array $tokenAttributes) 
    {
      if(array_search('attribute', $tokenAttributes)) {
        return ['result' => 'attribute found'];
      } else {
        return ['result' => 'attribute not found'];
      }
    }
}
Testing for example above:

[% new attribute %]

Will display: attribute found

[% new %]

Will display: attribute not found

创建新的过滤器

要创建新元素,您应该创建一个类,该类应该扩展 Puff\Compilation\Element\AbstractElement

例如

/**
     * Returns an array of elements and filters which will be initialized
     *
     * @return array
     */
    public function setUp(): array
    {
        return [
            ...
            'filters' => [
                'new_filter' => NewFilter::class
            ]
        ];
    }
    ...

您应在 Module 类的 setUp() 方法中指定元素的 class名称

您的过滤器类应该实现 Puff\Compilation\Filter\FilterInterface

<?php

namespace Puff\Compilation\Filter;

/**
 * Class UpperCaseFilter
 * @package Puff\Compilation\Filter
 */
class UpperCaseFilter implements FilterInterface
{
    /**
     * @param $variable
     * @param array $args
     * @return string|array
     */
    public static function handle($variable, ...$args) {
        mb_internal_encoding('UTF-8');

        if(!is_array($variable)) {
            return mb_strtoupper($variable);
        } else {
            array_walk_recursive($variable, function(&$item) {
                if(!is_array($item)) {
                    $item = mb_strtoupper($item);
                }
            });

            return $variable;
        }
    }
}

例如,了解 UpperCaseFilter 代码以了解其工作原理

语法编辑

您可以根据需要在标签中使用一些语法元素,例如Puff使用的符号、等号、过滤器分隔符等。

要实现这一点,您应该创建一个新类,该类可以实现 Puff\Tokenization\Syntax\SyntaxInterface 接口或扩展 Puff\Tokenization\Syntax\AbstractSyntax 类。让我们看看如何使用 AbstractSyntax

<?php


namespace Puff\Tokenization\Syntax;

/**
 * Class NewSyntax
 * @package Puff\Tokenization\Syntax
 */
class NewSyntax extends AbstractSyntax
{
    public function getElementTag() : array{
        return ["(@", "@)"];
    }
}

因此,我们指定了新元素标签的符号。为了使其生效,您应该在 Engine 构造函数中的配置数组中设置它,或者在 Module 的 setUp() 方法中设置它。

<?php
$engineInstance = new Engine([
    'modules' => [
        new \Puff\Modules\Core\CoreModule(),
    ],
    'syntax' => new MySyntax()
]);

现在,所有标签都应该使用新的语法,让我们看看我们应该如何更新模板。

(@ if variable == 1 @)
    <span>Syntax updated!</span>
(@ end @)

转义标签

要转义标签,您应该在标签之前设置转义符号,以告诉编译器忽略它。

Puff 中的默认转义符号是 //,但您可以通过设置自己的语法类来编辑它。

[% set variable = 1 %]
[[ variable ]]

//[[variable]]

将显示

1
[[ variable ]]