Chris@4: 'red', Chris@4: self::TOKEN_COMMENT => 'yellow', Chris@4: self::TOKEN_KEYWORD => 'green', Chris@4: self::TOKEN_DEFAULT => 'default', Chris@4: self::TOKEN_HTML => 'cyan', Chris@4: Chris@4: self::ACTUAL_LINE_MARK => 'red', Chris@4: self::LINE_NUMBER => 'dark_gray', Chris@4: ); Chris@4: Chris@4: /** Chris@4: * @param ConsoleColor $color Chris@4: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@4: */ Chris@4: public function __construct(ConsoleColor $color) Chris@4: { Chris@4: $this->color = $color; Chris@4: Chris@4: foreach ($this->defaultTheme as $name => $styles) { Chris@4: if (!$this->color->hasTheme($name)) { Chris@4: $this->color->addTheme($name, $styles); Chris@4: } Chris@4: } Chris@4: } Chris@4: Chris@4: /** Chris@4: * @param string $source Chris@4: * @param int $lineNumber Chris@4: * @param int $linesBefore Chris@4: * @param int $linesAfter Chris@4: * @return string Chris@4: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@4: * @throws \InvalidArgumentException Chris@4: */ Chris@4: public function getCodeSnippet($source, $lineNumber, $linesBefore = 2, $linesAfter = 2) Chris@4: { Chris@4: $tokenLines = $this->getHighlightedLines($source); Chris@4: Chris@4: $offset = $lineNumber - $linesBefore - 1; Chris@4: $offset = max($offset, 0); Chris@4: $length = $linesAfter + $linesBefore + 1; Chris@4: $tokenLines = array_slice($tokenLines, $offset, $length, $preserveKeys = true); Chris@4: Chris@4: $lines = $this->colorLines($tokenLines); Chris@4: Chris@4: return $this->lineNumbers($lines, $lineNumber); Chris@4: } Chris@4: Chris@4: /** Chris@4: * @param string $source Chris@4: * @return string Chris@4: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@4: * @throws \InvalidArgumentException Chris@4: */ Chris@4: public function getWholeFile($source) Chris@4: { Chris@4: $tokenLines = $this->getHighlightedLines($source); Chris@4: $lines = $this->colorLines($tokenLines); Chris@4: return implode(PHP_EOL, $lines); Chris@4: } Chris@4: Chris@4: /** Chris@4: * @param string $source Chris@4: * @return string Chris@4: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@4: * @throws \InvalidArgumentException Chris@4: */ Chris@4: public function getWholeFileWithLineNumbers($source) Chris@4: { Chris@4: $tokenLines = $this->getHighlightedLines($source); Chris@4: $lines = $this->colorLines($tokenLines); Chris@4: return $this->lineNumbers($lines); Chris@4: } Chris@4: Chris@4: /** Chris@4: * @param string $source Chris@4: * @return array Chris@4: */ Chris@4: private function getHighlightedLines($source) Chris@4: { Chris@4: $source = str_replace(array("\r\n", "\r"), "\n", $source); Chris@4: $tokens = $this->tokenize($source); Chris@4: return $this->splitToLines($tokens); Chris@4: } Chris@4: Chris@4: /** Chris@4: * @param string $source Chris@4: * @return array Chris@4: */ Chris@4: private function tokenize($source) Chris@4: { Chris@4: $tokens = token_get_all($source); Chris@4: Chris@4: $output = array(); Chris@4: $currentType = null; Chris@4: $buffer = ''; Chris@4: Chris@4: foreach ($tokens as $token) { Chris@4: if (is_array($token)) { Chris@4: switch ($token[0]) { Chris@4: case T_WHITESPACE: Chris@4: break; Chris@4: Chris@4: case T_OPEN_TAG: Chris@4: case T_OPEN_TAG_WITH_ECHO: Chris@4: case T_CLOSE_TAG: Chris@4: case T_STRING: Chris@4: case T_VARIABLE: Chris@4: Chris@4: // Constants Chris@4: case T_DIR: Chris@4: case T_FILE: Chris@4: case T_METHOD_C: Chris@4: case T_DNUMBER: Chris@4: case T_LNUMBER: Chris@4: case T_NS_C: Chris@4: case T_LINE: Chris@4: case T_CLASS_C: Chris@4: case T_FUNC_C: Chris@4: case T_TRAIT_C: Chris@4: $newType = self::TOKEN_DEFAULT; Chris@4: break; Chris@4: Chris@4: case T_COMMENT: Chris@4: case T_DOC_COMMENT: Chris@4: $newType = self::TOKEN_COMMENT; Chris@4: break; Chris@4: Chris@4: case T_ENCAPSED_AND_WHITESPACE: Chris@4: case T_CONSTANT_ENCAPSED_STRING: Chris@4: $newType = self::TOKEN_STRING; Chris@4: break; Chris@4: Chris@4: case T_INLINE_HTML: Chris@4: $newType = self::TOKEN_HTML; Chris@4: break; Chris@4: Chris@4: default: Chris@4: $newType = self::TOKEN_KEYWORD; Chris@4: } Chris@4: } else { Chris@4: $newType = $token === '"' ? self::TOKEN_STRING : self::TOKEN_KEYWORD; Chris@4: } Chris@4: Chris@4: if ($currentType === null) { Chris@4: $currentType = $newType; Chris@4: } Chris@4: Chris@4: if ($currentType !== $newType) { Chris@4: $output[] = array($currentType, $buffer); Chris@4: $buffer = ''; Chris@4: $currentType = $newType; Chris@4: } Chris@4: Chris@4: $buffer .= is_array($token) ? $token[1] : $token; Chris@4: } Chris@4: Chris@4: if (isset($newType)) { Chris@4: $output[] = array($newType, $buffer); Chris@4: } Chris@4: Chris@4: return $output; Chris@4: } Chris@4: Chris@4: /** Chris@4: * @param array $tokens Chris@4: * @return array Chris@4: */ Chris@4: private function splitToLines(array $tokens) Chris@4: { Chris@4: $lines = array(); Chris@4: Chris@4: $line = array(); Chris@4: foreach ($tokens as $token) { Chris@4: foreach (explode("\n", $token[1]) as $count => $tokenLine) { Chris@4: if ($count > 0) { Chris@4: $lines[] = $line; Chris@4: $line = array(); Chris@4: } Chris@4: Chris@4: if ($tokenLine === '') { Chris@4: continue; Chris@4: } Chris@4: Chris@4: $line[] = array($token[0], $tokenLine); Chris@4: } Chris@4: } Chris@4: Chris@4: $lines[] = $line; Chris@4: Chris@4: return $lines; Chris@4: } Chris@4: Chris@4: /** Chris@4: * @param array $tokenLines Chris@4: * @return array Chris@4: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@4: * @throws \InvalidArgumentException Chris@4: */ Chris@4: private function colorLines(array $tokenLines) Chris@4: { Chris@4: $lines = array(); Chris@4: foreach ($tokenLines as $lineCount => $tokenLine) { Chris@4: $line = ''; Chris@4: foreach ($tokenLine as $token) { Chris@4: list($tokenType, $tokenValue) = $token; Chris@4: if ($this->color->hasTheme($tokenType)) { Chris@4: $line .= $this->color->apply($tokenType, $tokenValue); Chris@4: } else { Chris@4: $line .= $tokenValue; Chris@4: } Chris@4: } Chris@4: $lines[$lineCount] = $line; Chris@4: } Chris@4: Chris@4: return $lines; Chris@4: } Chris@4: Chris@4: /** Chris@4: * @param array $lines Chris@4: * @param null|int $markLine Chris@4: * @return string Chris@4: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@4: */ Chris@4: private function lineNumbers(array $lines, $markLine = null) Chris@4: { Chris@4: end($lines); Chris@4: $lineStrlen = strlen(key($lines) + 1); Chris@4: Chris@4: $snippet = ''; Chris@4: foreach ($lines as $i => $line) { Chris@4: if ($markLine !== null) { Chris@4: $snippet .= ($markLine === $i + 1 ? $this->color->apply(self::ACTUAL_LINE_MARK, ' > ') : ' '); Chris@4: } Chris@4: Chris@4: $snippet .= $this->color->apply(self::LINE_NUMBER, str_pad($i + 1, $lineStrlen, ' ', STR_PAD_LEFT) . '| '); Chris@4: $snippet .= $line . PHP_EOL; Chris@4: } Chris@4: Chris@4: return $snippet; Chris@4: } Chris@4: }