mkgor / puff
适用于PHP的灵活且轻量级的模板引擎
Requires
- php: ^7.1
- ext-mbstring: *
Requires (Dev)
- phpunit/phpunit: ^8.5
This package is auto-updated.
Last update: 2024-09-29 05:49:29 UTC
README
Puff
一个可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>
重要! 如果需要所有基本语句(如 for、if-else 等),请在此处初始化CoreModule。
同样重要! Puff会自动将变量名中的'-'和'.'符号转换为'_'。
规格
基本语句的执行说明,如 if-else、for、import 等,通过字符组合 [% %]
表示
要显示变量的值,应使用 [[ ]]
适用于
类似于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() ] ]);
重要! 如果需要所有基本语句(如 for、if-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 ]]