registerTag自定义标签

Tag标签 和插件 的注册方式大体是一样的,但是 Tag 标签在这里指需要闭合的标签,如果 {list}{/list} 标签,在开发中我们可能会使用成对的标签把中间部分包括起来,使用循环等操作。


编写Tag插件

插件的定义和命名规则:

 

Tag插件在 Sdopx 模板引擎中是一个类,应该实现  sdopx\interfaces\Tag ,但是并不要强制要求以代码接口实现的形式书写代码。

下面使用一个例子来介绍如何自定义一个 Tag插件:

list 插件

define('ROOT_DIR', __DIR__);
date_default_timezone_set('PRC');
require(ROOT_DIR . '/vendor/autoload.php');

use sdopx\lib\Outer;
use sdopx\Sdopx;

Sdopx::$defaultTemplateDirs = './view';
Sdopx::$defaultCompileDir = './runtime';

class ListTag
{

    /**
     * 用于回调的参数描述变量指定
     * @return array
     */
    public function callbackParameter(): array
    {
        // 1 键名 item 是指在标签中如 {list item=rs} 中 item 的属性名称,
        // 会从这里获取的值 item 获取的rs 将作为下面 reader 中 $callback($arg) 中的参数名称,在$callback 内部可以使用 $rs 变量来读取 $arg 的值。
        // 我们可以这么理解 使用item 属性指定一个变量 $rs,使得 callback 中的 $rs=$arg; 那么 在 包含中的模板内容 可以使用 $rs 来读取数据。
        // 2 must => true 表示这个item 属性必须存在,如果不指定默认的 defualt='rs' 那么 如果不设置这个参数会报错。
        // 3 可以指定多个回调参数,但是 callback 调用时 需要对于指定的参数传值。

        return [
            'item' => ['must' => true, 'default' => 'rs'],
        ];
    }

    /**
     * 渲染
     * @param array $params 这个参数 是来自模板插件传递过来的属性参数值
     * @param $callback  中间部分内容的回调函数
     * @param \sdopx\lib\Outer $outer 用于输出内容或者抛出错误的输出对象
     */
    public function render(array $params, $callback, Outer $outer)
    {
        $data = isset($params['data']) ? $params['data'] : [];
        if (!is_array($data)) {
            $outer->throw('data 参数必须是数组形式');
            return;
        }
        $outer->html('<ul>');
        foreach ($data as $item) {
            //因为 callbackParameter 中指定了 一个参数,那么如果不传属性,默认可以在 中间模板中使用 $rs 读取,如果指定可以使用对应的变量读取
            call_user_func($callback, $item);
        }
        $outer->html('</ul>');
    }
}

//注意 提供的是 实例
Sdopx::registerTag('list', new ListTag());
$sdopx = new Sdopx();
$sdopx->assign('name', 'wj008');
$sdopx->display('index.tpl');

 

Outer 说明:

 

 //说明:sdopx\lib\Outer 提供了一些方法,以及可以获得模板对象。
$outer->sdopx //php 代码中的 $sdopx 对象
//如需要设置插件缓存值等
$outer->sdopx->_cache['name']='xxx';

$outer->text($code) // 会转成html实体输出,防止xss 漏洞攻击,建议使用。
$outer->html($code) // 原样输出 html 代码。
$outer->throw($error) // 抛出异常,使用该方法抛出异常在调试模式下会自动附上模板错误位置,建议使用。

注意:我们在模板插件开发中,所有的输出均使用 sdopx\lib\Outer 进行输出,不可使用 系统自带的 echo print 等方法输出。

callbackParameter 返回数据描述:

Sdopx 中 Tag 标记中包含的代码是以匿名回调方式提供给开发者的,因为编译器并不知道开发者您的回调到底需要多少个参数,所以 需要使用 callbackParameter 指定你的回调参数属性

{list} 包含的代码 {/list}  

参数的数量可能为因为 使用标签者设置或者不设置参数从而导致 编译器生成的参数数量是不确定的。

如下面这种情况:


class ListTag
{

    /**
     * 用于回调的参数描述变量指定
     * @return array
     */
    public function callbackParameter(): array
    {

        return [
            'item' => ['must' => true, 'default' => 'rs'],
            //这个key属性使用者可能设置或者不设置 must=false 是允许不设置
            'key' => ['must' => false],
        ];
    }

    /**
     * 渲染
     * @param array $params 这个参数 是来自模板插件传递过来的属性参数值
     * @param $callback  中间部分内容的回调函数
     * @param \sdopx\lib\Outer $outer 用于输出内容或者抛出错误的输出对象
     */
    public function render(array $params, $callback, Outer $outer)
    {
        $data = isset($params['data']) ? $params['data'] : [];
        if (!is_array($data)) {
            $outer->throw('data 参数必须是数组形式');
            return;
        }
        $outer->html('<ul>');
        foreach ($data as $key => $item) {
            //因为 callbackParameter 中指定了 一个参数,那么如果不传属性,默认可以在 中间模板中使用 $rs 读取,如果指定可以使用对应的变量读取
            if (isset($params['key'])) {
                call_user_func($callback, $item, $key);
            } else {
                call_user_func($callback, $item);
            }
        }
        $outer->html('</ul>');
    }
}

key 的属性 {list data=$data key=i}  它可能被设置,但是也可能不被设置,那么我们调用 callback 的时候 就需要传递不同的参数数量。
注意:回调函数 callback 的参数顺序必须和 callbackParameter 返回的数组参数位置一致。


测试模板:

{*不指定 item 使用 callbackParameter 设置的默认值 rs*}
{list data=[['id'=>1,'name'=>'wj007'],['id'=>2,'name'=>'wj008']]}
    <li>id:{$rs.id},名字:{$rs.name}</li>
{/list}
{*指定 item 为 vo*}
{list data=[['id'=>1,'name'=>'wj007'],['id'=>2,'name'=>'wj008']] item='vo'}
    <li>id:{$vo.id},名字:{$vo.name}</li>
{/list}

输出:

<ul>
    <li>id:1,名字:wj007</li>
    <li>id:2,名字:wj008</li>
</ul>

<ul>
    <li>id:1,名字:wj007</li>
    <li>id:2,名字:wj008</li>
</ul>

 

自动装载插件

 

Tag 插件的自动载入和 Plugin 基本一致,其不同之处在于 命名后缀要求是 Tag


1 需要被自动载入的插件命名要求。

需要自动载入的插件命名必须为大驼峰形式的类名 其类名后缀必须是 Tag 结尾,文件名与类名必须保持一致。

如:

 

class List  //无效,没有以Tag 结尾,找不到。
class listTag  //无效,类名不是大驼峰,找不到。
class My_listTag  //无效,类名不可出现下划线。
class MyListTag  //有效,但是 插件使用时是 {my_list} 而不是 {myList} 也不是 {mylist}
class ListTag // 有效  插件使用是 {list}

文件名 如  ListTag.php

2 需要被自动载入的插件 命名空间必须是 namespace sdopx\plugin;


Sdopx 是有命名空间要求的,所以需要被自动载入插件的类,必须是在 sdopx\plugin 下的。
如:
ListTag.php

 

namespace sdopx\plugin;


use sdopx\lib\Outer;

class ListTag
{

    /**
     * 用于回调的参数描述变量指定
     * @return array
     */
    public function callbackParameter(): array
    {
        return [
            'item' => ['must' => true, 'default' => 'rs'],
            'key' => ['must' => false],
        ];
    }

    /**
     * 渲染
     * @param array $params 这个参数 是来自模板插件传递过来的属性参数值
     * @param $callback  中间部分内容的回调函数
     * @param \sdopx\lib\Outer $outer 用于输出内容或者抛出错误的输出对象
     */
    public function render(array $params, $callback, Outer $outer)
    {
        $data = isset($params['data']) ? $params['data'] : [];
        if (!is_array($data)) {
            $outer->throw('data 参数必须是数组形式');
            return;
        }
        $outer->html('<ul>');
        foreach ($data as $key => $item) {
            if (isset($params['key'])) {
                call_user_func($callback, $item, $key);
            } else {
                call_user_func($callback, $item);
            }
        }
        $outer->html('</ul>');
    }
}


需要被自动载入的插件 存放位置要求。


如果 你使用的是 composer 自动加载路径,那么 你只要在你的 composer.json 文件中指定 sdopx/plugin 命名空间的对应目录即可。

如:

composer.json

 

{
.... 你的其他配置信息
  "autoload": {
    "psr-4": {
      "sdopx\\plugin\\": "myplugin",
    }
  },
.... 你的其他配置信息
}

这个时候 你可以把你的插件放入跟目录下的 myplugin 文件夹中,并且运行 composer update

如果上例子 存放路径为  /myplugin/ListTag.php

如果你不使用 composer  那么 你需要用 Sdopx::setPluginDir(); 来设置你插件的存放路径

使用代码如下:

 

define('ROOT_DIR', __DIR__);
date_default_timezone_set('PRC');
require(ROOT_DIR . '/sdopx/Sdopx.php');

use sdopx\Sdopx;

Sdopx::$defaultTemplateDirs = './view';
Sdopx::$defaultCompileDir = './runtime';

//设置自定义插件存放路径
Sdopx::setPluginDir('./myplugin');

$sdopx = new Sdopx();
$sdopx->assign('name', 'wj008');
$sdopx->display('index.tpl');

 

在这里 我们依然建议您使用 composer 的方式自动载入命名空间。

 

使用继承的方式创建 Tag

 

例如,我们系统内置提供了 volist 这个 Tag标签,那么我们可能并不想自己去实现太多功能,我们可以继承它来实现一些我们想要的东西。

这里 我演示实现一个 文章列表插件

 

 

class ArticleTag extends \sdopx\plugin\VolistTag
{

    public function render(array $params, $callback, Outer $outer)
    {
        //我需要从上下文中获取数据库操作类 db 需要创建的时候 使用 new Sdopx($context) 来实例化,并且假设 $context->db 为数据库操作类
        $context = $outer->sdopx->context;
        if ($context == null) {
            $outer->throw('没有设置上下文 context');
        }
        /**
         * @var \mysqli
         */
        $db = $context->db; //可以从 $context 拿到 db 也可以PHP使用 $sdopx->_cache['db']=$db; 存入并在插件中获取
        $offset = isset($params['offset']) ? intval($params['offset']) : 0;
        $length = isset($params['length']) ? intval($params['length']) : 20;
        $mod = isset($params['mod']) ? intval($params['mod']) : 1;
        $limit = $length * $mod;//保证足够的数据可以被 mod 逃过
        $from = $db->query('select * from Article limit ' . $offset . ',' . $limit)->fetch_all(MYSQLI_ASSOC);
        //因为已经使用了sql 跳过,所以volist 中不需要跳过
        $params['offset'] = 0;
        $params['from'] = $from;//这里将读取的数据交给 volist
        $outer->html('<ul>');
        parent::render($params, $callback, $outer);
        $outer->html('</ul>');
    }
}

 

对于$ db 的传入,如果上下文支持 db 可以使用上下文的方式,如果不支持 可以使用 $sdopx->_cache 的方式存入db操作对象, $sdopx->_cache  键名需要避免重复覆盖。

使用 cache

class ArticleTag extends \sdopx\plugin\VolistTag
{

    public function render(array $params, $callback, Outer $outer)
    {
        $db = $outer->sdopx->_cache['sdopx_db'];
        if ($db == null) {
            $outer->throw('没有设置 sdopx_db 的缓存');
        }
        $offset = isset($params['offset']) ? intval($params['offset']) : 0;
        $length = isset($params['length']) ? intval($params['length']) : 20;
        $mod = isset($params['mod']) ? intval($params['mod']) : 1;
        $limit = $length * $mod;//保证足够的数据可以被 mod 逃过
        $from = $db->query('select * from Article limit ' . $offset . ',' . $limit)->fetch_all(MYSQLI_ASSOC);
        //因为已经使用了sql 跳过,所以volist 中不需要跳过
        $params['offset'] = 0;
        $params['from'] = $from;//这里将读取的数据交给 volist
        $outer->html('<ul>');
        parent::render($params, $callback, $outer);
        $outer->html('</ul>');
    }
}


使用的时候需要传入db

$sdopx = new Sdopx();
$sdopx->_cache['sdopx_db']=$db;
$sdopx->assign('name', 'wj008');
$sdopx->display('index.tpl');

 

Copyright © 2021 海南的叶子 All Rights Reserved 琼ICP备2021000725号

琼公网安备 46900702000037号