[TOC]
* * * * *
# 1 標簽庫
>>標簽庫,可以用來自定義模板文件中的標簽解析方式
>在tp5中自定義了內置標簽庫(Cx.php)
### $taglib->__construct()
>>標簽庫構造函數,創建標簽庫對象
~~~
public function __construct($template)
{
$this->tpl = $template;
}
~~~
# 2 標簽庫的標簽解析
## 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 . ')\b(?>[^' . $end . ']*)|\/(' . $tagName . '))' . $end;
} else {
$regex = $begin . '(' . $tagName . ')\b(?>[^' . $end . ']*)' . $end;
}
} else {
if ($close) {
// 如果是閉合標簽
$regex = $begin . '(?:(' . $tagName . ')\b(?>(?:(?!' . $end . ').)*)|\/(' . $tagName . '))' . $end;
} else {
$regex = $begin . '(' . $tagName . ')\b(?>(?:(?!' . $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;
}
~~~
# 3 內置標簽庫
>> TP5在\template\tablig\Cx.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()
- 框架簡介
- 簡介
- 框架目錄
- 根目錄
- 應用目錄
- 核心目錄
- 擴展目錄
- 其他目錄
- 框架流程
- 啟動流程
- 請求流程
- 響應流程
- 框架結構
- 應用組織
- 網絡請求
- 路由組織
- 數據驗證
- 數據模型(M)
- 數據庫連接(Connection)
- 數據庫(Db)
- 查詢構造(Builder)
- 數據庫查詢(Query)
- 模型(Model)
- 模板視圖(V)
- 視圖(View)
- 模板引擎(Think)
- 模板標簽庫(TagLib)
- 控制器(C)
- 網絡響應
- 配置與緩存
- 配置操作
- 緩存操作
- cookie與session
- Cookie操作
- Session操作
- 自動加載
- 鉤子注冊
- 文件上傳
- 分頁控制
- 控制臺
- 自動構建
- 日志異常調試
- 異常處理
- 代碼調試
- 日志記錄
- 框架使用
- 1 環境搭建(Server)
- 2 網絡請求(Request)
- 3 請求路由(Route)
- 4 響應輸出(Response)
- 5 業務處理(Controller)
- 6 數據存取(Model)
- 7 Web界面(View)