tp5源码分析之模板标签库
标签库,可以用来自定义模板文件中的标签解析方式
在tp5中自定义了内置标签库(Cx.php)
$taglib->__construct()
标签库构造函数,创建标签库对象
public function __construct($template)
{
$this->tpl = $template;
}
2-1 标签库的标签解析
标签库可以用来解析模板文件中的自定义标签
$taglib->parseTag()
public function parseTag(&$content, $lib = "")
{
$tags = [];
$lib = $lib ? strtolower($lib) . ":" : "";
foreach ($this->tags as $name => $val) {
$close = !isset($val["close"]) || $val["close"] ? 1 : 0;
$tags[$close][$lib . $name] = $name;
if (isset($val["alias"])) {
// 别名设置
$array = (array) $val["alias"];
foreach (explode(",", $array[0]) as $v) {
$tags[$close][$lib . $v] = $name;
}
}
}
// 闭合标签
if (!empty($tags[1])) {
$nodes = [];
$regex = $this->getRegex(array_keys($tags[1]), 1);
if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
$right = [];
foreach ($matches as $match) {
if ("" == $match[1][0]) {
$name = strtolower($match[2][0]);
// 如果有没闭合的标签头则取出最后一个
if (!empty($right[$name])) {
// $match[0][1]为标签结束符在模板中的位置
$nodes[$match[0][1]] = [
"name" => $name,
"begin" => array_pop($right[$name]), // 标签开始符
"end" => $match[0], // 标签结束符
];
}
} else {
// 标签头压入栈
$right[strtolower($match[1][0])][] = $match[0];
}
}
unset($right, $matches);
// 按标签在模板中的位置从后向前排序
krsort($nodes);
}
$break = "<!--###break###--!>";
if ($nodes) {
$beginArray = [];
// 标签替换 从后向前
foreach ($nodes as $pos => $node) {
// 对应的标签名
$name = $tags[1][$node["name"]];
$alias = $lib . $name != $node["name"] ? ($lib ? strstr($node["name"], $lib) : $node["name"]) : "";
// 解析标签属性
$attrs = $this->parseAttr($node["begin"][0], $name, $alias);
$method = "tag" . $name;
// 读取标签库中对应的标签内容 replace[0]用来替换标签头,replace[1]用来替换标签尾
$replace = explode($break, $this->$method($attrs, $break));
if (count($replace) > 1) {
while ($beginArray) {
$begin = end($beginArray);
// 判断当前标签尾的位置是否在栈中最后一个标签头的后面,是则为子标签
if ($node["end"][1] > $begin["pos"]) {
break;
} else {
// 不为子标签时,取出栈中最后一个标签头
$begin = array_pop($beginArray);
// 替换标签头部
$content = substr_replace($content, $begin["str"], $begin["pos"], $begin["len"]);
}
}
// 替换标签尾部
$content = substr_replace($content, $replace[1], $node["end"][1], strlen($node["end"][0]));
// 把标签头压入栈
$beginArray[] = ["pos" => $node["begin"][1], "len" => strlen($node["begin"][0]), "str" => $replace[0]];
}
}
while ($beginArray) {
$begin = array_pop($beginArray);
// 替换标签头部
$content = substr_replace($content, $begin["str"], $begin["pos"], $begin["len"]);
}
}
}
// 自闭合标签
if (!empty($tags[0])) {
$regex = $this->getRegex(array_keys($tags[0]), 0);
$content = preg_replace_callback($regex, function ($matches) use (&$tags, &$lib) {
// 对应的标签名
$name = $tags[0][strtolower($matches[1])];
$alias = $lib . $name != $matches[1] ? ($lib ? strstr($matches[1], $lib) : $matches[1]) : "";
// 解析标签属性
$attrs = $this->parseAttr($matches[0], $name, $alias);
$method = "tag" . $name;
return $this->$method($attrs, "");
}, $content);
}
return;
}
$taglib->getRegex()
标签的正则表达式
private function getRegex($tags, $close)
{
$begin = $this->tpl->config("taglib_begin");
$end = $this->tpl->config("taglib_end");
$single = strlen(ltrim($begin, "\")) == 1 && strlen(ltrim($end, "\")) == 1 ? true : false;
$tagName = is_array($tags) ? implode("|", $tags) : $tags;
if ($single) {
if ($close) {
// 如果是闭合标签
$regex = $begin . "(?:(" . $tagName . ")(?>[^" . $end . "]*)|/(" . $tagName . "))" . $end;
} else {
$regex = $begin . "(" . $tagName . ")(?>[^" . $end . "]*)" . $end;
}
} else {
if ($close) {
// 如果是闭合标签
$regex = $begin . "(?:(" . $tagName . ")(?>(?:(?!" . $end . ").)*)|/(" . $tagName . "))" . $end;
} else {
$regex = $begin . "(" . $tagName . ")(?>(?:(?!" . $end . ").)*)" . $end;
}
}
return "/" . $regex . "/is";
}
$taglib->parseAttr()
使用正则表达式分析标签属性
public function parseAttr($str, $name, $alias = "")
{
$regex = "/s+(?>(?P<name>[w-]+)s*)=(?>s*)([""])(?P<value>(?:(?!\2).)*)\2/is";
$result = [];
if (preg_match_all($regex, $str, $matches)) {
foreach ($matches["name"] as $key => $val) {
$result[$val] = $matches["value"][$key];
}
if (!isset($this->tags[$name])) {
// 检测是否存在别名定义
foreach ($this->tags as $key => $val) {
if (isset($val["alias"])) {
$array = (array) $val["alias"];
if (in_array($name, explode(",", $array[0]))) {
$tag = $val;
$type = !empty($array[1]) ? $array[1] : "type";
$result[$type] = $name;
break;
}
}
}
} else {
$tag = $this->tags[$name];
// 设置了标签别名
if (!empty($alias) && isset($tag["alias"])) {
$type = !empty($tag["alias"][1]) ? $tag["alias"][1] : "type";
$result[$type] = $alias;
}
}
if (!empty($tag["must"])) {
$must = explode(",", $tag["must"]);
foreach ($must as $name) {
if (!isset($result[$name])) {
throw new Exception("tag attr must:" . $name);
}
}
}
} else {
// 允许直接使用表达式的标签
if (!empty($this->tags[$name]["expression"])) {
static $_taglibs;
if (!isset($_taglibs[$name])) {
$_taglibs[$name][0] = strlen(ltrim($this->tpl->config("taglib_begin"), "\") . $name);
$_taglibs[$name][1] = strlen(ltrim($this->tpl->config("taglib_end"), "\"));
}
$result["expression"] = substr($str, $_taglibs[$name][0], -$_taglibs[$name][1]);
// 清除自闭合标签尾部/
$result["expression"] = rtrim($result["expression"], "/");
$result["expression"] = trim($result["expression"]);
} elseif (empty($this->tags[$name]) || !empty($this->tags[$name]["attr"])) {
throw new Exception("tag error:" . $name);
}
}
return $result;
}
2-2 标签库的其他辅助解析函数
$taglib->getTags()
获取标签定义
public function getTags()
{
return $this->tags;
}
TP5在 emplate abligCx.php中,定义了内置标签的解析方式
每个标签的解析实现为相应标签的tagxx()方法
$cx->tagPhp()
$cx->tagVolist()
$cx->tagForeach()
$cx->tagIf()
$cx->tagElseif()
$cx->tagElse()
$cx->tagSwitch()
$cx->tagCase()
$cx->tagDefault()
$cx->tagCompare()
$cx->tagRange()
$cx->tagPresent()
$cx->tagNotPresent()
$cx->tagEmpty()
$cx->tagNotempty()
$cx->tagDefined()
$cx->tagNotdefined()
$cx->tagLoad()
$cx->tagAssign()
$cx->tagDefine()
$cx->tagFor()
$cx->tagUrl()
$cx->tagFunction()
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: thinkphp中 taglib标签应用
- 下一篇: thinkphp5使html5实现动态跳转