Memo View Component

最近在对自写的 Memo 框架做一些结构调整,准备将它的一部分组件独立出来以便分开使用,最先完成的是 Memo View Component。Memo View 是一个轻量级的使用 PHP 原生语法的模板“引擎”,你可以使用它来实现简单的模板继承功能。

Memo View 使用 PHP 原生语法,因此我们可以这样定义一个 base 模板:

// file templates/base.php
<html>
    <head>
        <meta charset="utf-8">
        <?=$this->section("title")?>
    </head>
    <body>
        <?=$this->section("content")?>
    </body>
</html>

base 模板中的 section 方法相当于一个占位符,具体内容需要子模板来填充。我们再定义一个基于 base 的 index 子模板:

// file templates/index.php
<?php $this->layout("base"); ?>

<?php $this->open("title"); ?>
    <title>Memo Framework</title>
<?php $this->close(); ?>

<?php $this->open("content"); ?>
    <h1>Memo</h1>
    <p>Memo is a PHP micro framework.</p>
    <?=$this->section("github")?>
<?php $this->close(); ?>

在 index 模板中使用 layout 方法来声明其继承于 base 模板。然后使用 open 和 close 方法来填充对应 section 的内容。Memo View 支持 layout 的嵌套,所以我们在 index 模板中同样可以使用 section 方法。

然后我们可以使用 Memo View 来载入之前定义的模板:

// file index.php
require __DIR__ . "/Memo/src/Memo/View.php";

use Memo\View;

try {
    $view = new View(array(
        "template" => "index",
        "folders" => array(__DIR__ . "/templates/")
    ));
    $view->display();
} catch (Exception $e) {
    echo $e->getMessage(), PHP_EOL;
}

Memo View 的 constructor 可以接受一个数组作为初始化参数,template 是模板名,folders 是模板目录。folders 可以同时定义多个目录,载入时会按顺序查找,并使用最先找到的模板。输出的内容大致如下:

<html>
    <head>
        <meta charset="utf-8">
        <title>Memo Framework</title>
    </head>
    <body>
        <h1>Memo</h1>
        <p>Memo is a PHP micro framework.</p>
    </body>
</html>

在模板中使用变量也非常方便,我们继续定义一个基于 index 的子模板 github:

// file templates/github.php
<?php $this->layout("index"); ?>

<?php $this->open("title"); ?>
    <title>Fork me on Github</title>
<?php $this->close(); ?>

<?php $this->open("github"); ?>
    <p>Github: <a href="<?=$repo?>"><?=$this->toUpper($repo)?></a></p>
<?php $this->close(); ?>

在 github 模板中又设置了一次 title secion 的内容,并且在 github section 中使用了 $repo 变量 以及 toUpper 方法。

// file index.php
require __DIR__ . "/Memo/src/Memo/View.php";

use Memo\View;

class Helper 
{
    public function toUpper($string)
    {
        return strtoupper($string);
    }
}

try {
    $view = new View(array(
        "template" => "github",
        "helper" => new Helper(),
        "folders" => array(__DIR__ . "/templates/")
    ));
    $view->assign("repo", "https://github.com/zither/Memo");
    // 可以通过 $view->render() 方法来获取内容
    echo $view->render();
} catch (Exception $e) {
    echo $e->getMessage(), PHP_EOL;
}

上面的代码中我们新增了一个 helper 参数,其值为 Helper 类的对象,我们需要通过它来提供 github 模板中需要的 toUpper 函数。然后使用 assign 方法设置了 repo 变量。输出内容大致如下:

<html>
    <head>
        <meta charset="utf-8">
        <title>Fork me on Github</title>
    </head>
    <body>
        <h1>Memo</h1>
        <p>Memo is a PHP micro framework.</p>
        <p>Github: <a href="https://github.com/zither/Memo">HTTPS://GITHUB.COM/ZITHER/MEMO</a></p>
    </body>
</html>

从输出的内容可以看到,github 模板中定义的 title 覆盖了 index 模板中设置的内容,这点需要注意。同时 index 模板中定义的 github section 也被正确填充,这说明 Helper 中的 toUpper 方法也被正确调用。