// +---------------------------------------------------------------------- namespace think\console\output; use think\console\output\formatter\Stack as StyleStack; use think\console\output\formatter\Style; class Formatter { private $decorated = false; private $styles = []; private $styleStack; /** * 转义 * @param string $text * @return string */ public static function escape($text) { return preg_replace('/([^\\\\]?)setStyle('error', new Style('white', 'red')); $this->setStyle('info', new Style('green')); $this->setStyle('comment', new Style('yellow')); $this->setStyle('question', new Style('black', 'cyan')); $this->setStyle('highlight', new Style('red')); $this->setStyle('warning', new Style('black', 'yellow')); $this->styleStack = new StyleStack(); } /** * 设置外观标识 * @param bool $decorated 是否美化文字 */ public function setDecorated($decorated) { $this->decorated = (bool) $decorated; } /** * 获取外观标识 * @return bool */ public function isDecorated() { return $this->decorated; } /** * 添加一个新样式 * @param string $name 样式名 * @param Style $style 样式实例 */ public function setStyle($name, Style $style) { $this->styles[strtolower($name)] = $style; } /** * 是否有这个样式 * @param string $name * @return bool */ public function hasStyle($name) { return isset($this->styles[strtolower($name)]); } /** * 获取样式 * @param string $name * @return Style * @throws \InvalidArgumentException */ public function getStyle($name) { if (!$this->hasStyle($name)) { throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name)); } return $this->styles[strtolower($name)]; } /** * 使用所给的样式格式化文字 * @param string $message 文字 * @return string */ public function format($message) { $offset = 0; $output = ''; $tagRegex = '[a-z][a-z0-9_=;-]*'; preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE); foreach ($matches[0] as $i => $match) { $pos = $match[1]; $text = $match[0]; if (0 != $pos && '\\' == $message[$pos - 1]) { continue; } $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); $offset = $pos + strlen($text); if ($open = '/' != $text[1]) { $tag = $matches[1][$i][0]; } else { $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; } if (!$open && !$tag) { // $this->styleStack->pop(); } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { $output .= $this->applyCurrentStyle($text); } elseif ($open) { $this->styleStack->push($style); } else { $this->styleStack->pop($style); } } $output .= $this->applyCurrentStyle(substr($message, $offset)); return str_replace('\\<', '<', $output); } /** * @return StyleStack */ public function getStyleStack() { return $this->styleStack; } /** * 根据字符串创建新的样式实例 * @param string $string * @return Style|bool */ private function createStyleFromString($string) { if (isset($this->styles[$string])) { return $this->styles[$string]; } if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { return false; } $style = new Style(); foreach ($matches as $match) { array_shift($match); if ('fg' == $match[0]) { $style->setForeground($match[1]); } elseif ('bg' == $match[0]) { $style->setBackground($match[1]); } else { try { $style->setOption($match[1]); } catch (\InvalidArgumentException $e) { return false; } } } return $style; } /** * 从堆栈应用样式到文字 * @param string $text 文字 * @return string */ private function applyCurrentStyle($text) { return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; } }