126. 占位符替换token服务

  在系统很多地方进行设置时,事先并不知道具体的设置值,典型的比如:当用户注册时,系统可以发送一封欢迎邮件,但在设置邮件内容时并不知道用户的名字,该名字需要等到用户注册后才知道,此时可以先设置一个占位符,等到发送时进行替换即可,这样的占位符就是“token”,这样的需求有很多,又比如文件上传的保存目录如果是日期,那么也需要先使用占位符,上传保存时需要替换为真实的日期。系统统一使用“token”服务来识别和替换占位符。

占位符格式:

为了便于描述,下文“token”和“占位符”是相同意思,为了能够自动识别和替换token,需要token自带含义,因此需要具备一定格式,系统通过派发钩子的方式来收集替换值,钩子又通过token的格式进行解析并生成替换值,因此token格式尤为重要,需要有统一规范,drupal标准token格式如下:
   [$type:$name]
这里$type是类型名,如nodeusercommentsitedate等(通常采用模块名,但不一定是,准确讲应该称为类型名,理解为token类型,用于识别是哪一种占位符),$name是该类型下给定占位符的名字

示例如下:
    [node:title][user:display-name]
token也可以分层级,格式如下:
    [$type:$pointer:$name]
示例如下:
    [node:author:mail]:被替换为节点作者的邮件
    [date:custom:Y]:被替换为4位数的年份
这种层级可以理解为:第一个冒号后面的部分均是$name,只不过$name内部又继续采用了冒号分隔,这里的冒号和第一个冒号在含义上是有区别的,第一个冒号可以严格称为token分隔符,而$name里面的冒号仅供提供该token的钩子使用,其含义和层级的数量也由钩子决定

token规则是:

整个token字符串用英文方括号包裹,方括号内不允许再包括方括号,里面至少有一个英文冒号将字符分隔为两部分,第一部分字符称为类型标识符($type),标识符可以是除空白字符和方括号以外的任意字符,第一个冒号后面的全部字符称为占位符名($name),占位符名可以包含英文冒号做进一步的层级规划,可以包含方括号以外的任意字符,甚至空格,占位符名的规则和解析均由提供该token的钩子实现。

token服务:

用于识别字符串中的token并替换

服务idtoken

类:Drupal\Core\Utility\Token

获取方式:\Drupal::token();

使用示例(替换目录中的日期token):

        $data = [];
        $destination = '[date:custom:Y]-[date:custom:m]';
        $destination = trim($destination, '/');
        $destination = \Drupal::token()->replace($destination, $data);
        $destination = \Drupal\Component\Render\PlainTextOutput::renderFromHtml($destination);
        print_r($destination);

该服务各方法说明如下:

public function replace($text, array $data = [], array $options = [], BubbleableMetadata $bubbleable_metadata = NULL)

参数如下:

$text

包含占位符的字符串,可以是html字符串,不可以为对象

$data

部分token的转化需要传递额外的对象,比如节点token就需要节点对象,用该数组传递所需的对象,如不需要可以留空

$options

选项数组,用于控制方法的行为,有两个通用选项:$options['clear']如果不为空,则在没有替换值时删除占位符;$options['callback']是一个回调(在PHP7前仅允许函数名或静态方法,不能为数组),可以用该回调来调整即将使用的替换数组;不同token可以按序传递选项数组

$bubbleable_metadata

渲染数组的可冒泡元数据对象,用于收集缓存或附属物

该方法返回替换后的字符串,调用者负责转义,如果没有传递可冒泡元数据,那么收集的可冒泡元数据将被自动添加到当前渲染上下文,如果有传递,那么由调用者处理

 

public function scan($text)

传入一个字符串,扫描其中所有可能包含的占位符,返回一个多维数组,第一级键名为$type,第二级键名为占位符中除开$type剩余的部分,值为完整占位符,示例如下:

如果输入值$text为:

$text='我是[a:b]和[x:y:z]';

那么输出值$var为:

$var['a']['b']='[a:b]';
$var['x']['y:z']='[x:y:z]';

如果没有扫描到token将返回空数组

 

public function generate($type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata)

收集可冒泡渲染元数据及派发钩子收集替换信息,派发tokens钩子

hook_tokens($type, $tokens, array $data, array $options, $bubbleable_metadata)

其中$type为占位符中类型部分,$tokens为一个数组,键名为余下的占位符部分,键值为完整占位符,其他参数与replace方法中的一样,钩子应返回一个数组,键名为完整占位符字符串,键值为替换字符串,如键值不想再被转义,应该用MarkupInterface对象表示,可返回空数组

修改钩:

hook_tokens_alter(array &$replacements, array $context, $bubbleable_metadata)

其中$replacements为全系统收集到的替换数组,$context为上下文数组,如下:

$context = [
      'type' => $type,
      'tokens' => $tokens,
      'data' => $data,
      'options' => $options,
];

含义同上

 

public function findWithPrefix(array $tokens, $prefix, $delimiter = ':')

从占位符扫描结果中提取具备某前缀的部分,假设输入$tokens如下:

$data = array(
        'author:name' => '[node:author:name]',
        'title'       => '[node:title]',
        'created'     => '[node:created]',
      );

前缀$prefixauthor,那么返回:

   array('name' => '[node:author:name]');

 

public function getInfo()

返回系统所支持的全部token的元数据描述,这通过钩子“token_info”及其修改钩收集,通常实现了tokens钩子的模块应该实现该钩子

钩子实现情况:

在系统默认安装中,以下模块实现了tokens钩子,模块名及钩子函数名如下:

comment : comment_tokens
file : file_tokens
node : node_tokens
statistics : statistics_tokens
system : system_tokens
taxonomy : taxonomy_tokens
user : user_tokens
views : views_tokens

没有模块实现其修改钩,你可以用本系列配套模块“yunke_help”查看钩子实现情况

钩子“token_info”的实现情况同上

自定义token

模块只需要实现以上的tokenstoken_info钩子即可,在钩子逻辑中规划token格式

可通过以下代码测试查看token

        $data = [];
        $token = "token is :\n[date:custom:Y]\n[site:url-brief]\n[site:login-url]";
        $token = \Drupal::token()->replace($token, $data);
        print_r($token);
        die;

常用token

[date:short]

短时间,如:10/21/2019 - 20:38

[date:medium]

中长日期,如:周一, 10/21/2019 - 20:38

[date:long]

长日期,如:星期一, 十月 21, 2019 - 20:38

[date:raw]

时间戳原始输出,如:1571661728

[date:since]

在数据参数中给出一个时间,该占位符返回一个用本地语言显示的时间差,如:

        $data = ['date'=>time()-300];
        $token = "[date:since] ";
        $token = \Drupal::token()->replace($token, $data);

将输出类似:4 分钟 58

通常用于显示已注册时间

[date:custom:Y]

四位数的年份,如2019

[date:custom:m]

带前导0的两位月份,如09

[date:custom:d]

带前导0的两位日期,如0821

[date:custom:Y-m-d H:i:s]

将输出完整的年月日时分秒,如:“2019-10-22 19:27:01”,如你所见,在“date:custom:”后面可以使用和php函数date第一个参数相同的格式字符,这就是自定义的含义,请参考:

https://www.php.net/manual/zh/function.date.php

除此之外,drupal还很贴心的进行了本地化翻译,如:

[date:custom:F(D)]

在翻译系统有数据时,将输出“十月(周二)”,而phpdate函数只能输出“October(Tue)

 [site:name]

在网站基本设置中设置的站点名

[site:slogan]

在网站基本设置中设置的站点口号或标语

[site:mail]

在网站基本设置中设置的邮件地址

[site:url]

完整的站点首页地址,带协议及域名部分

[site:url-brief]

简洁版首页地址,带域名但不带协议部分

[site:login-url]

用户登录地址

补充:

1、并非设置了token就会被替换,而是仅允许使用token的地方才会替换,如在文章正文中设置就不会被替换

2、注意占位符不要使用中文的方括号或冒号,这样将无效

3、不要将token占位符和渲染数组中的占位符($element['#attached']['placeholders'])搞混淆,这是完全不同的两个概念

 

 

添加新评论

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。
请输入以上问题的答案,换一个问题请刷新,不区分大小写