<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st <liu21st@gmail.com> // +---------------------------------------------------------------------- namespace think\template; use Exception; use think\Template; /** * ThinkPHPæ ‡ç¾åº“TagLib解æžåŸºç±» * @category Think * @package Think * @subpackage Template * @author liu21st <liu21st@gmail.com> */ class TagLib { /** * æ ‡ç¾åº“定义XML文件 * @var string * @access protected */ protected $xml = ''; protected $tags = []; // æ ‡ç¾å®šä¹‰ /** * æ ‡ç¾åº“å称 * @var string * @access protected */ protected $tagLib = ''; /** * æ ‡ç¾åº“æ ‡ç¾åˆ—表 * @var array * @access protected */ protected $tagList = []; /** * æ ‡ç¾åº“分æžæ•°ç»„ * @var array * @access protected */ protected $parse = []; /** * æ ‡ç¾åº“是å¦æœ‰æ•ˆ * @var bool * @access protected */ protected $valid = false; /** * 当å‰æ¨¡æ¿å¯¹è±¡ * @var object * @access protected */ protected $tpl; protected $comparison = [' nheq ' => ' !== ', ' heq ' => ' === ', ' neq ' => ' != ', ' eq ' => ' == ', ' egt ' => ' >= ', ' gt ' => ' > ', ' elt ' => ' <= ', ' lt ' => ' < ']; /** * 架构函数 * @access public * @param Template $template 模æ¿å¼•æ“Žå¯¹è±¡ */ public function __construct(Template $template) { $this->tpl = $template; } /** * 按ç¾æ ‡åº“替æ¢é¡µé¢ä¸çš„æ ‡ç¾ * @access public * @param string $content 模æ¿å†…容 * @param string $lib æ ‡ç¾åº“å * @return void */ public function parseTag(string &$content, string $lib = ''): void { $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); } } /** * æŒ‰æ ‡ç¾ç”Ÿæˆæ£åˆ™ * @access public * @param array|string $tags æ ‡ç¾å * @param boolean $close 是å¦ä¸ºé—åˆæ ‡ç¾ * @return string */ public function getRegex($tags, bool $close): string { $begin = $this->tpl->getConfig('taglib_begin'); $end = $this->tpl->getConfig('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'; } /** * 分æžæ ‡ç¾å±žæ€§ æ£åˆ™æ–¹å¼ * @access public * @param string $str æ ‡ç¾å±žæ€§å—符串 * @param string $name æ ‡ç¾å * @param string $alias 别å * @return array */ public function parseAttr(string $str, string $name, string $alias = ''): array { $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($this->tpl->getConfig('taglib_begin_origin') . $name); $_taglibs[$name][1] = strlen($this->tpl->getConfig('taglib_end_origin')); } $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; } /** * 解æžæ¡ä»¶è¡¨è¾¾å¼ * @access public * @param string $condition 表达å¼æ ‡ç¾å†…容 * @return string */ public function parseCondition(string $condition): string { if (strpos($condition, ':')) { $condition = ' ' . substr(strstr($condition, ':'), 1); } $condition = str_ireplace(array_keys($this->comparison), array_values($this->comparison), $condition); $this->tpl->parseVar($condition); return $condition; } /** * 自动识别构建å˜é‡ * @access public * @param string $name å˜é‡æè¿° * @return string */ public function autoBuildVar(string &$name): string { $flag = substr($name, 0, 1); if (':' == $flag) { // 以:开头为函数调用,解æžå‰åŽ»æŽ‰: $name = substr($name, 1); } elseif ('$' != $flag && preg_match('/[a-zA-Z_]/', $flag)) { // XXX: è¿™å¥çš„写法å¯èƒ½è¿˜éœ€è¦æ”¹è¿› // 常é‡ä¸éœ€è¦è§£æž if (defined($name)) { return $name; } // ä¸ä»¥$开头并且也ä¸æ˜¯å¸¸é‡ï¼Œè‡ªåŠ¨è¡¥ä¸Š$å‰ç¼€ $name = '$' . $name; } $this->tpl->parseVar($name); $this->tpl->parseVarFunction($name, false); return $name; } /** * 获å–æ ‡ç¾åˆ—表 * @access public * @return array */ public function getTags(): array { return $this->tags; } }