snicco/templating

为各种模板引擎提供统一的API。

v2.0.0-beta.9 2024-09-07 14:27 UTC

README

codecov Psalm Type-Coverage Psalm level PhpMetrics - Static Analysis PHP-Versions

Snicco 项目中的 模板 组件提供了一种简单、面向对象的API,围绕流行的 PHP 模板引擎。

目录

  1. 安装
  2. 使用方法
    1. 概述
    2. 创建视图
    3. 直接渲染视图
    4. 查找第一个现有视图
    5. 引用嵌套目录
    6. 全局上下文/视图作曲家
    7. 视图工厂
    8. PHPViewFactory
      1. 实例化
      2. 模板继承
  3. 贡献
  4. 问题和PR
  5. 安全

安装

composer require snicco/templating

使用方法

本包包含以下主要组件

  • 一个不可变的 View 对象,它可以用给定的 context 渲染为其字符串表示形式。
  • 一个 TemplateEngine,它是一个门面类,用于创建和渲染 View 对象。
  • 一个 ViewFactory 接口,它抽象化了一个 View 实例是如何渲染的实现细节。参见 使用视图工厂
  • 一个 ViewContextResolver,它负责向正在渲染的视图添加全局 context 和视图作曲家 context

以下目录结构假设用于本README中的示例

your-project-root
├── templates/
│   ├── users/                 
│   │   ├── profile.php  
│   │   └── ...
│   └── hello-world.php             
└── ...
// ./templates/hello-world.php

echo "Hello $first_name";

创建视图

View 的实例总是通过调用 TemplateEngine::make() 创建的。

可以将 context 添加到 View 实例中,该 context 会在 View 渲染后对底层模板可用。

use Snicco\Component\Templating\TemplateEngine;

// The TemplateEngine accepts one or more instances of ViewFactory.
// See #Using view factories for available implementations.
$view_factory = /* */ 

$engine = new TemplateEngine($view_factory);

// hello-world is relative to the root directory "templates"
$view = $engine->make('hello-world');

$view1 = $view->with('first_name', 'Calvin');
$output = $engine->renderView($view1);
var_dump($output); // Hello Calvin

$view2 = $view->with('first_name', 'Marlon');
$output = $engine->renderView($view2);
var_dump($output); // Hello Marlon

// Views can also be created by passing an absolute path
$view = $engine->make('/path/to/templates/hello-world.php');

直接渲染视图

如果您想立即渲染模板,可以使用 TemplateEngine 上的 render 方法。

use Snicco\Component\Templating\TemplateEngine;

$view_factory = /* */ 

$engine = new TemplateEngine($view_factory);

$output = $engine->render('hello-world', ['first_name' => 'Calvin']);
var_dump($output); // Hello Calvin

查找第一个现有视图

TemplateEnginemakerender 方法都接受一个字符串的 array,以便使用第一个现有视图。

use Snicco\Component\Templating\TemplateEngine;

$view_factory = /* */ 

$engine = new TemplateEngine($view_factory);

$view = $engine->make(['hello-world-custom', 'hello-world']);

$output = $engine->render(['hello-world-custom', 'hello-world'], ['first_name' => 'Calvin']);
var_dump($output); // Hello Calvin

如果找不到视图,将抛出 ViewNotFound 异常。

引用嵌套目录

TemplateEnginemakerender 方法将点展开,以允许目录遍历。这独立于正在使用的具体 ViewFactory

use Snicco\Component\Templating\TemplateEngine;

$view_factory = /* */ 

$engine = new TemplateEngine($view_factory);

$view = $engine->make('users.profile');

$output = $engine->render('users.profile', ['first_name' => 'Calvin']);

全局上下文/视图作曲家

在渲染视图之前,它会被传递到 ViewContextResolver,它负责应用

  1. 应在所有视图中可用的全局 context
  2. context 由视图组合器提供给 某些视图

视图组合器可以是 Closure 或实现 ViewComposer 的类。

需要 ViewContextResolver 来实例化视图工厂接口的具体实现。

添加全局上下文

use Snicco\Component\Templating\Context\ViewContextResolver;
use Snicco\Component\Templating\Context\GlobalViewContext;

$global_context = new GlobalViewContext()

// All templates now have access to a variable called $site_name
$global_context->add('site_name', 'snicco.io');

// The value can be a closure which will be called lazily.
$global_context->add('some_var', fn() => 'some_value');

$context_resolver = new ViewContextResolver($global_context);

如果您将一个 array 作为 GlobalViewContext::add 的第二个参数传递,您可以在视图中这样引用嵌套值

use Snicco\Component\Templating\Context\ViewContextResolver;
use Snicco\Component\Templating\Context\GlobalViewContext;

$global_context = new GlobalViewContext()
$global_context->add('app', [
   'request' => [
       'path' => '/foo',
       'query_string' => 'bar=baz'
   ]
]);

// Inside any template
echo $app['request.path']
echo $app['request.query_string']

添加视图组合器

$context_resolver = /* */

// Using a closure
$context_resolver->addComposer('hello-world', fn(View $view) => $view->with('foo', 'bar'));

// Using a class that implements ViewComposer
$context_resolver->addComposer('hello-world', HelloWorldComposer::class);

// Adding a composer to multiple views
$context_resolver->addComposer(['hello-world', 'other-view'], fn(View $view) => $view->with('foo', 'bar'));

// Adding a composer by wildcard
// This will make the current user available to all views inside the templates/users directory.
$context_resolver->addComposer('users.*', fn(View $view) => $view->with('current_user', Auth::user()));

使用视图工厂

所有视图工厂都实现了 ViewFactory 接口。

它们由 TemplateEngine 使用,并包含将 View 实例渲染为其字符串表示形式的底层逻辑。

可以同时使用多个视图工厂,在这种情况下,将使用第一个可以渲染给定 View 的工厂。

目前可用的视图工厂有

  • PHPViewFactory,包含在此包中。这是一个裸骨的实现,非常适合只有少数视图的小项目。
  • BladeViewFactory,包含在 单独的包 中。将 Laravel 的 Blade 集成为与该包一起使用的独立模板引擎,同时保留两个包的所有功能。
  • TwigViewFactory - 即将推出。

PHPViewFactory

PHPViewFactory 是一个裸骨实现,非常适合只有少数视图的小项目。

实例化

PHPViewFactory 将一个 ViewContextResolver 作为第一个参数,以及一个包含 模板目录的 array 作为第二个参数。

如果视图存在于多个模板目录中,将使用第一个匹配的视图。这对于允许某些模板被另一个(自定义)模板目录中的模板覆盖非常有用。

use Snicco\Component\Templating\TemplateEngine;
use Snicco\Component\Templating\ViewFactory\PHPViewFactory;

$context_resolver = /* */

$php_view_factory = new PHPViewFactory(
    $context_resolver, 
    [__DIR__.'/templates']
);

$template_engine = new TemplateEngine($php_view_factory);

模板继承

PHPViewFactory 允许进行非常基本的模板继承。

假设我们有以下两个模板

<?php
// post.php

/*
 * Extends: post-layout
 */

echo "Post one content"
<?php
// post-layout.php
?>
<html lang="en">

    <head>
        Head content goes here
        <title><?= htmlentities($title, ENT_QUOTES, 'UTF-8') ?></title>
    </head>
    <body>
        <main>
            <?= $__content ?>
        </main>
        
        <footer>
            Footer content goes here
        </footer>
    </body>
</html>

渲染帖子视图将产生

$template_engine->render('post', ['title' => 'Post 1 Title']);
<html lang="en">

<head>
    Head content goes here
    <title>Post 1 Title</title>
</head>
<body>
<main>
    Post one content
</main>

<footer>
    Footer content goes here
</footer>
</body>

</html>

以下几点需要注意

  • 父模板通过在模板的前 100 个字节 内的 /* */ 注释中放置 Extends: parent-view-name 来表示。
  • 传递给子视图的 Context 对父视图可用。
  • 父视图可以使用 $__content 变量输出子内容。
  • 嵌套继承是可能的。例如,post-layout.php 可以扩展 layout.php

贡献

此存储库是 Snicco 项目 开发存储库的只读分割。

您可以通过以下方式做出贡献.

报告问题和发送拉取请求

请在 Snicco monorepo 中报告问题。

安全

如果您发现安全漏洞,请遵循我们的 披露程序