<?php /** * Подсветка синтаксиса PHP на основе простых шаблонов * Можно использовать предопределенные шаблоны через * статические методы, либо создать обьект со своими * шаблонами и/или цветами. * * @author sTalkEr <stalk at zelnet dot ru> * @package Highlight * @version 0.1 */ // Если true, то считать что теги пхп всегда открыты if (!defined("HI_IGNORE_PHP_TAG")) define("HI_IGNORE_PHP_TAG", true); // Шаблоны для IB кодов. // Из-за того, что я не знаю как(да и можно ли вообще) // экранировать IB коды, добавил пробелы в начало тегов, // что бы исходник нормально отобразился. Их нужно удалить! if (!defined("HI_IB_MAINPREFIX")) define("HI_IB_MAINPREFIX", "[ code][ font=Courier][ color=%s]"); if (!defined("HI_IB_MAINPOSTFIX")) define("HI_IB_MAINPOSTFIX", "[ /color][ /font][ /code]"); if (!defined("HI_IB_COLORPREFIX")) define("HI_IB_COLORPREFIX", "[ color=%s]"); if (!defined("HI_IB_COLORPOSTFIX")) define("HI_IB_COLORPOSTFIX", "[ /color]"); // Шаблоны для HTML if (!defined("HI_HTML_MAINPREFIX")) define("HI_HTML_MAINPREFIX", "<pre style=\"color: %s;\">"); if (!defined("HI_HTML_MAINPOSTFIX")) define("HI_HTML_MAINPOSTFIX", "</pre>"); if (!defined("HI_HTML_COLORPREFIX")) define("HI_HTML_COLORPREFIX", "<span style=\"color: %s;\">"); if (!defined("HI_HTML_COLORPOSTFIX")) define("HI_HTML_COLORPOSTFIX", "</span>"); // Дополнительные типы token-ов if (!defined("T_UNKNOWN")) define("T_UNKNOWN", -1); if (!defined("T_OLD_FUNCTION")) define("T_OLD_FUNCTION", -1); if (!defined("T_QUOTE")) define("T_QUOTE", 1001); if (!defined("T_SINGS")) define("T_SINGS", 1002); if (!defined("T_CURLY_CLOSE")) define("T_CURLY_CLOSE", 1003); // Состояния текущего место положения элемента if (!defined("HI_STATE_NONE")) define("HI_STATE_NONE", 0); // HTML Inline if (!defined("HI_STATE_PHPTAG")) define("HI_STATE_PHPTAG", 1); // PHP код if (!defined("HI_STATE_QUOTEDSTRING")) define("HI_STATE_QUOTEDSTRING", 2); // Внутри двойных ковычек if (!defined("HI_STATE_HEREDOC")) define("HI_STATE_HEREDOC", 3); // HEREDOC ( <<< EOF ) if (!defined("HI_STATE_HEREDOCCURLY")) define("HI_STATE_HEREDOCCURLY", 4); // HEREDOC и фигурные скобки if (!defined("HI_STATE_QUOTEDCURLY")) define("HI_STATE_QUOTEDCURLY", 5); // Двойные кавычки и фигурные скобки /** * Один элемент * * @package Highlight */ class TokenElement { /** * Тип токена * * @var integer */ public $TokenType = T_UNKNOWN; /** * Текст элемента * * @var string */ public $Text = ""; /** * Конструктор. * * @param mixed $elem * @return TokenElement */ public function TokenElement($elem) { if (is_array($elem)) { $this->TokenType = trim($elem[1]) == "" ? T_WHITESPACE : $elem[0]; $this->Text = $elem[1]; } else { switch (trim($elem)) { case "": $this->TokenType = T_WHITESPACE; break; case "\"": $this->TokenType = T_QUOTE; break; case "}": $this->TokenType = T_CURLY_CLOSE; break; default: if (preg_match('/^[\{\}=\(\)\[\],.?:;]$/', $elem)) { $this->TokenType = T_SINGS; } break; } $this->Text = $elem; } } /** * Магический метод, возращает текст элемента * * @return unknown */ public function __toString() { return $this->Text; } } /** * Класс элементов сгруппированых по цвету * * @package Highlight */ class GroupElements { /** * Цвет для группы элементов * * @var string */ public $Color = null; /** * Массив элементов TokenElement * * @var array */ public $Elements = array(); /** * Конструктор * * @param string $color * @param TokenElement $elem * @return GroupElements */ public function GroupElements($color = null, $elem = null) { $this->Color = $color; if ($elem !== null) { $this->Elements[] = $elem; } } /** * Магический метод, возращает строку с обьединенными элементами * * @return string */ public function __toString() { $out = ""; foreach ($this->Elements as $elem) { $out .= $elem; } return $out; } } /** * @package Highlight */ class Highlight { /** * Cсылка на обьект класса * * @var Highlight * @see Highlight */ private static $_thisInstance = null; /** * Массив с группами элементов * * @var array */ private $ElementsCollection = array(); /** * Группировка элементов по цветам * * @var GroupElements */ private $ColorGroup = null; /** * Последний обработанный элемент (за исключением пробелов) * * @var TokenElement */ private $LastElement = null; /** * Расположение элемента (внутри php тегов, строк итп) * * @var int */ private $State = HI_STATE_NONE; /** * Исходная строка * * @var string */ private $Source = ""; /** * Нужно ли экранировать спецсимволы? * * @var boolean */ public $IsHTML = true; /** * Основной цвет, помещается в префикс основного шаблона * * @var string */ public $ColorBase = "green"; /** * Цвет ключевыех слов * * @var string */ public $ColorKeywords = "green"; /** * Цвет строковых констант * * @var string */ public $ColorStrings = "red"; /** * Цвет комментариев * * @var string */ public $ColorComments = "orange"; /** * Цвет скобок и операторов * * @var string */ public $ColorSings = "green"; /** * Цвет переменных * * @var string */ public $ColorVariables = "darkblue"; /** * Цвет чисел * * @var string */ public $ColorNumbers = "darkblue"; /** * Цвет спецсимволов (\n, \t, etc...) * * @var string */ public $ColorCharacters = "red"; /** * Цвет плохих символов * * @var string */ public $ColorBadCharacters = "violet"; /** * Цвет элементов, не попавших ни в одну категорию * * @var string */ public $ColorOther = "green"; /** * Цвет HTML (то что вне тегов <?php ?>) * * @var string */ public $ColorHTML = "black"; /** * Префикс основного шаблона * (формат аналогичен функции printf()) * Подставляется с базовым цветом в начало всей строки * * @var string */ public $MainPrefix = ""; /** * Постфикс основного шаблона * Подставляется в конец всей строки * * @var string */ public $MainPostfix = ""; /** * Префикс для одного элемента * (формат аналогичен функции printf()) * Подставляется с цветом в начало всей строки * * @var string */ public $ColorPrefix = ""; /** * Постфикс для одного элемента * Подставляется в конец элемента * * @var string */ public $ColorPostfix = ""; /** * Пустой конструктор */ public function Highlight() {} /** * Возращает ссылку на обьект класса. * Требуется для работы через статические методы * * @return Highlight */ public static function Instance() { if (Highlight::$_thisInstance === null) { Highlight::$_thisInstance = new Highlight(); } return Highlight::$_thisInstance; } /** * Возращает строку с подсветкой при помощи IB кодов * * @param string $string * @return string */ public static function IB($string) { Highlight::Instance()->MainPrefix = HI_IB_MAINPREFIX; Highlight::Instance()->MainPostfix = HI_IB_MAINPOSTFIX; Highlight::Instance()->ColorPrefix = HI_IB_COLORPREFIX; Highlight::Instance()->ColorPostfix = HI_IB_COLORPOSTFIX; Highlight::Instance()->IsHTML = false; return Highlight::Instance()->FromString($string); } /** * Возращает строку с подсветкой при помощи HTML тегов * * @param string $string * @return string */ public static function HTML($string) { Highlight::Instance()->MainPrefix = HI_HTML_MAINPREFIX; Highlight::Instance()->MainPostfix = HI_HTML_MAINPOSTFIX; Highlight::Instance()->ColorPrefix = HI_HTML_COLORPREFIX; Highlight::Instance()->ColorPostfix = HI_HTML_COLORPOSTFIX; return Highlight::Instance()->FromString($string); } /** * Возращает подсвеченную строку $source * * @param string $source * @return string */ public function FromString($source) { $this->Source = $source; $this->Parse(); //var_export($this->ElementsCollection); $out = sprintf($this->MainPrefix, $this->ColorBase); foreach ($this->ElementsCollection as $group) { $out .= sprintf($this->ColorPrefix, $group->Color); $out .= $this->IsHTML ? htmlspecialchars($group, ENT_NOQUOTES) : $group; $out .= $this->ColorPostfix; } $out .= $this->MainPostfix; return $out; } /** * Добавляет элемент в группу * * @param mixed $elem */ private function AddElement($elem) { $type = null; $tElem = new TokenElement($elem); $type = $this->GetType($tElem->TokenType); if ($tElem->TokenType != T_UNKNOWN && $tElem->TokenType != T_WHITESPACE) { switch ($this->State) { case HI_STATE_PHPTAG: switch ($tElem->TokenType) { case T_QUOTE: $this->State = HI_STATE_QUOTEDSTRING; break; case T_START_HEREDOC: $this->State = HI_STATE_HEREDOC; break; case T_CLOSE_TAG: $this->State = HI_STATE_NONE; break; } break; case HI_STATE_QUOTEDSTRING: switch ($tElem->TokenType) { case T_QUOTE: $this->State = HI_STATE_PHPTAG; break; case T_CURLY_OPEN: $this->State = HI_STATE_QUOTEDCURLY; break; } break; case HI_STATE_HEREDOC: switch ($tElem->TokenType) { case T_END_HEREDOC: $this->State = HI_STATE_PHPTAG; break; case T_CURLY_OPEN: $this->State = HI_STATE_HEREDOCCURLY; break; } break; case HI_STATE_HEREDOCCURLY: switch ($tElem->TokenType) { case T_CURLY_CLOSE: $this->State = HI_STATE_HEREDOC; break; } break; case HI_STATE_QUOTEDCURLY: switch ($tElem->TokenType) { case T_CURLY_CLOSE: $this->State = HI_STATE_QUOTEDSTRING; break; } break; case HI_STATE_NONE: switch ($tElem->TokenType) { case T_OPEN_TAG: $this->State = HI_STATE_PHPTAG; break; } break; } if ($type !== null && isset($this->{"Color$type"})) { $color = $this->{"Color$type"}; } if ($this->ColorGroup !== null) { if ($this->ColorGroup->Color != $color) { $this->ElementsCollection[] = $this->ColorGroup; $this->ColorGroup = new GroupElements($color, $tElem); } else { $this->ColorGroup->Elements[] = $tElem; } } else { $this->ColorGroup = new GroupElements($color, $tElem); } } else { $this->ColorGroup->Elements[] = $tElem; } } /** * Парсим исходную строку * */ private function Parse() { $this->ElementsCollection = array(); $isOpenTag = (substr(trim($this->Source), 0, 2) == "<?"); if (HI_IGNORE_PHP_TAG && !$isOpenTag) { $tokens = token_get_all("<?php\n{$this->Source}\n?>"); if ($tokens[count($tokens) - 1] == T_CLOSE_TAG) unset($tokens[count($tokens) - 1]); if ($tokens[0] == T_OPEN_TAG) unset($tokens[0]); $this->State = HI_STATE_PHPTAG; } else { $tokens = token_get_all($this->Source); } $this->ElementsCollection = array(); $this->ColorGroup = null; foreach ($tokens as $token) { $this->AddElement($token); } $this->ElementsCollection[] = $this->ColorGroup; $this->ColorGroup = null; } /** * Возращает тип для элемента * * @param integer $token * @return string */ private function GetType($token) { switch ($token) { /** * Ключевые слова */ case T_ABSTRACT: // abstract case T_ARRAY: // array() case T_ARRAY_CAST: // (array) case T_AS: // as case T_BOOLEAN_AND: // && case T_BOOLEAN_OR: // || case T_BOOL_CAST: // (bool) or (boolean) case T_BREAK: // break case T_CASE: // case case T_CATCH: // catch case T_CLASS: // class case T_CLONE: // clone case T_CLOSE_TAG: // ? > or % > case T_CONCAT_EQUAL: // .= case T_CONST: // const case T_CONTINUE: // continue case T_DEC: // -- case T_DECLARE: // declare case T_DEFAULT: // default case T_DIV_EQUAL: // /= case T_DO: // do case T_DOUBLE_ARROW: // => case T_DOUBLE_CAST: // (real), (double) or (float) case T_DOUBLE_COLON: // :: case T_ECHO: // echo case T_ELSE: // else case T_ELSEIF: // elseif case T_EMPTY: // empty case T_ENDDECLARE: // enddeclare case T_ENDFOR: // endfor case T_ENDFOREACH: // endforeach case T_ENDIF: // endif case T_ENDSWITCH: // endswitch case T_ENDWHILE: // endwhile case T_EVAL: // eval() case T_EXIT: // exit or die case T_EXTENDS: // extends case T_FILE: // __FILE__ case T_FINAL: // final case T_FOR: // for case T_FOREACH: // foreach case T_FUNCTION: // function or cfunction case T_GLOBAL: // global case T_HALT_COMPILER: // __halt_compiler() case T_IF: // if case T_IMPLEMENTS: // implements case T_INC: // ++ case T_INCLUDE: // include() case T_INCLUDE_ONCE: // include_once() case T_INSTANCEOF: // instanceof case T_INT_CAST: // (int) or (integer) case T_INTERFACE: // interface case T_ISSET: // isset() case T_IS_EQUAL: // == case T_IS_NOT_EQUAL; // != or <> case T_IS_IDENTICAL: // === case T_IS_NOT_IDENTICAL: // !== case T_IS_SMALLER_OR_EQUAL: // <= case T_LINE: // __LINE__ case T_LIST: // list() case T_LOGICAL_AND: // and case T_LOGICAL_OR: // or case T_LOGICAL_XOR: // xor case T_MINUS_EQUAL: // -= case T_MOD_EQUAL: // %= case T_MUL_EQUAL: // *= case T_NEW: // new case T_OBJECT_CAST: // (object) case T_OBJECT_OPERATOR: // -> case T_OLD_FUNCTION: // old_function case T_OPEN_TAG: // <?php, <? or <% case T_OPEN_TAG_WITH_ECHO: // <?= or <%= case T_OR_EQUAL: // |= case T_PAAMAYIM_NEKUDOTAYIM: // :: case T_PLUS_EQUAL: // += case T_PRINT: // print() case T_PRIVATE: // private case T_PUBLIC: // public case T_PROTECTED: // protected case T_REQUIRE: // require() case T_REQUIRE_ONCE: // require_once() case T_RETURN: // return case T_SL: // << case T_SL_EQUAL: // <<= case T_SR: // >> case T_SR_EQUAL: // >>= case T_STATIC: // static case T_STRING_CAST: // (string) case T_SWITCH: // switch case T_THROW: // throw case T_TRY: // try case T_UNSET_CAST: // unset() case T_UNSET: // (unset) case T_USE: // use case T_VAR: // var case T_WHILE: // while case T_XOR_EQUAL: // ^= case T_FUNC_C: // __FUNCTION__ case T_CLASS_C: // __CLASS__ case T_NUM_STRING: // case T_CURLY_OPEN: // case T_CURLY_CLOSE: // } case T_DOLLAR_OPEN_CURLY_BRACES: // ${ return "Keywords"; /** * Строковые константы */ case T_END_HEREDOC: // case T_START_HEREDOC: // <<< case T_CONSTANT_ENCAPSED_STRING: // "foo" or 'bar' case T_QUOTE: // "(quote) return "Strings"; /** * Числа */ case T_LNUMBER: // 123, 012, 0x1ac, etc case T_DNUMBER: // 0.12, etc return"Numbers"; /** * Комментарии */ case T_DOC_COMMENT: // // or #, and /* */ on PHP 5 case T_COMMENT: // /** */ return "Comments"; /** * Переменные */ case T_ENCAPSED_AND_WHITESPACE: // case T_STRING: if ($this->State == HI_STATE_QUOTEDSTRING || $this->State == HI_STATE_HEREDOC) return "Strings"; case T_VARIABLE: // $foo case T_STRING_VARNAME: // return "Variables"; /** * HTML */ case T_INLINE_HTML: return "HTML"; /** * Спец символы (\n, \t, etc...) */ case T_CHARACTER: return "Characters"; /** * Любые символы ASCII 32 за исключением \t (0x09), \n (0x0a) и \r (0x0d) */ case T_BAD_CHARACTER: return "BadCharacters"; /** * Различные скобки и операторы присваевания */ case T_SINGS: return "Sings"; /** * Пробелы */ case T_WHITESPACE: return null; // Пробелам цвет не нужен, наверное... т.ч. игнорируем /** * Все остальное (не используется) */ default: return null; } } } ?> |