Datum: 08.10.2011, 18:37, Kategorie: PHP, Aufrufe: 174, Kommentare: 0
Syntax-Highlighter in PHP für Delphi, Java, PHP, C/C++ und JavaScript
Für meinen Blog habe ich einen einfachen Syntax-Highlighter benötigt, der aus Plain-Text-Quellcode eine HTML-formatierte Ausgabe erstellt und dabei verschiedene Programmiersprachen - mindestens Pascal, Java, PHP und C/C++ - unterstützt. Ich habe daraufhin einen kleinen Highlighter geschrieben, der ab sofort auch auf meiner Internetseite zum Einsatz kommt.Vollständiger Quelltext
<?php function highlightCode($input, $keywords, $comments, $singleQuotedString, $doubleQuotedString) { $input = str_replace(""", '"', $input); $input = str_replace("\t", " ", $input); // $result = ""; $length = strlen($input); // $lastWord = ""; $char; // $inComment = false; $commentTagNr = -1; $inString = false; $lastQuote = ""; // for ($i = 'a'; $i <= 'z'; $i++) $ordinaryChars[] = $i; for ($i = 'A'; $i <= 'Z'; $i++) $ordinaryChars[] = $i; for ($i = '0'; $i <= '9'; $i++) $ordinaryChars[] = $i; $ordinaryChars[] = '_'; // for ($i = 0; $i < $length; $i++) { $char = $input[$i]; // if ($inComment) { $commentEnd = $comments[$commentTagNr][1]; // if (strpos($input, $commentEnd, $i) === $i) { $inComment = false; $result .= "$commentEnd</span>"; $i += strlen($commentEnd)-1; // continue; } else { $result .= $char; // continue; } } else { if (!$inString) { for ($c = 0; $c < count($comments); $c++) { $comment = $comments[$c]; // if (strpos($input, $comment[0], $i) === $i) { $inComment = true; $commentTagNr = $c; $result .= "<span class=\"hlComment\">"; } } } } // if (in_array($char, $ordinaryChars)) { $lastWord .= $char; } else { $new; // if (in_array(strtolower($lastWord), $keywords) && !$inString) { $new = "<span class=\"hlKeyword\">$lastWord</span>"; } else { if (is_numeric($lastWord) && !$inString) { $new = "<span class=\"hlNumber\">$lastWord</span>"; } else { $new = $lastWord; } } // $lastWord = ""; $result .= $new; // if ((($char == "'" && $singleQuotedString) || ($char == '"' && $doubleQuotedString)) && ($lastQuote == "" || $char == $lastQuote)) { $inString = !$inString; // if ($inString) { $result .= "<span class=\"hlString\">$char"; $lastQuote = $char; } else { $result .= "$char</span>"; $lastQuote = ""; } } else { $result .= $char; } } } // return $result; } function highlightJava($input) { $keywords = array('abstract', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const', 'continue', 'default', 'do', 'double', 'else', 'extends', 'final', 'finally', 'float', 'for', 'goto', 'if', 'implements', 'import', 'instanceof', 'int', 'interface', 'long', 'native', 'new', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'strictfp', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'try', 'void', 'volatile', 'while', 'null', 'true', 'false'); // $comments = array(array('//',"\n"), array('/*','*/')); // $singleQuotedString = false; $doubleQuotedString = true; // return highlightCode($input, $keywords, $comments, $singleQuotedString, $doubleQuotedString); } function highlightPascal($input) { $keywords = array('and', 'array', 'as', 'begin', 'case', 'class', 'const', 'constructor', 'destructor', 'div', 'do', 'downto', 'else', 'end', 'except', 'file', 'finally', 'for', 'function', 'goto', 'if', 'implementation', 'in', 'inherited', 'interface', 'is', 'mod', 'not', 'object', 'of', 'on', 'or', 'packed', 'procedure', 'program', 'property', 'raise', 'record', 'repeat', 'set', 'shl', 'shr', 'then', 'threadvar', 'to', 'try', 'type', 'unit', 'until', 'uses', 'var', 'while', 'with', 'xor', 'nil', 'true', 'false', 'public', 'private', 'published', 'protected'); // $comments = array(array('//',"\n"), array('(*','*)'), array('{','}')); // $singleQuotedString = true; $doubleQuotedString = false; // return highlightCode($input, $keywords, $comments, $singleQuotedString, $doubleQuotedString); } function highlightPHP($input) { $keywords = array('abstract', 'and', 'array', 'as', 'break', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'do', 'else', 'elseif', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'interface', 'instanceof', 'namespace', 'new', 'or', 'private', 'protected', 'public', 'static', 'switch', 'throw', 'try', 'use', 'var', 'while', 'xor', 'die', 'echo', 'empty', 'exit', 'eval', 'include', 'include_once', 'isset', 'list', 'require', 'require_once', 'return', 'print', 'unset', 'true', 'false', 'null'); // $comments = array(array('//',"\n"), array('/*','*/'), array('#',"\n")); // $singleQuotedString = true; $doubleQuotedString = true; // return highlightCode($input, $keywords, $comments, $singleQuotedString, $doubleQuotedString); } function highlightCPP($input) { $keywords = array('and', 'and_eq', 'alignas', 'alignof', 'asm', 'auto', 'bitand', 'bitor', 'bool', 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const', 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'double', 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'float', 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'private', 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed', 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this', 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq'); // $comments = array(array('//',"\n"), array('/*','*/'), array('#',"\n")); // $singleQuotedString = false; $doubleQuotedString = true; // return highlightCode($input, $keywords, $comments, $singleQuotedString, $doubleQuotedString); } function highlightC($input) { $keywords = array('auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'); // $comments = array(array('//',"\n"), array('/*','*/'), array('#',"\n")); // $singleQuotedString = false; $doubleQuotedString = true; // return highlightCode($input, $keywords, $comments, $singleQuotedString, $doubleQuotedString); } function highlightJavaScript($input) { $keywords = array('break', 'else', 'new'. 'var', 'case', 'finally', 'return', 'void', 'catch', 'for', 'switch', 'while', 'continue', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'do', 'instanceof', 'typeof', 'null', 'true', 'false'); // $comments = array(array('//',"\n"), array('/*','*/')); // $singleQuotedString = true; $doubleQuotedString = true; // return highlightCode($input, $keywords, $comments, $singleQuotedString, $doubleQuotedString); } ?>
Syntax-Highlighter für Pascal, Java, PHP, C, C++ und JavaScript
Highlighter verwenden
Um den Highlighter zu verwenden, muss lediglich eine der Methoden highlightXYZ(), wobei XYZ für die entsprechende Programmiersprache steht, aufgerufen und als Parameter der zu formatierende Quelltext übergeben werden. Der Rückgabewert der Funktion ist ein String mit der in HTML formatierten Ausgabe.Dabei werden Schlüsselwörter, Kommentare, String-Literale und Zahlen jeweils mit einem Span-Tag versehen, der eine der Klassen hlKeyword, hlComment, hlString oder hlNumber besitzt. Per CSS-Definitionen können diese Spans formatiert werden. Eine CSS-Datei könnte beispielsweise wie folgt aussehen:
.hlKeyword {
font-weight: bold;
color:#0d358c;
}
.hlNumber {
font-weight: bold;
color:#de00c8;
}
.hlComment {
color:#159500;
font-style: italic;
}
.hlString {
color:red;
}
Beispiel-CSS-Datei für den Syntax-Highlighter
Die Ausgabe des Highlighters sollte in einen <pre> ... </pre>-Block eingebettet werden.
Highlighter erweitern
Der Highlighter lässt sich einfach um weitere Programmiersprachen erweitern. Dazu muss lediglich eine neue Funktion in der Form von highlightJava() oder der anderen Programmiersprachen erstellt werden, in der folgende Variablen angepasst und an die eigentliche Highlighter-Methode highlightCode() übergeben werden müssen:- $keywords: Array mit allen Schlüsselwörtern der Programmiersprache
- $comments: Array, das aus Unter-Arrays mit genau zwei Elementen besteht, wobei das erste Element jeweils den Beginn eines Kommentars kennzeichnet und das zweite Element das Ende eines solchen ("\n" als Zeilen-Trenner für einzeilige Kommentare)
- $singleQuotedString: Boolscher Wert, der angibt, ob String-Literale mit einem einfachen Hochkomma (') eingeleitet und beendet werden
- $doubleQuotedString: Boolscher Wert, der angibt, ob String-Literale mit einem doppelten Hochkomma (") eingeleitet und beendet werden
Bekannte Probleme
Der Highlighter funktioniert zu meiner Zufriedenheit. Das einzige Problem, das ich bisher feststellen konnte, ist ein fehlerhaftes Highlighten von String-Literalen in denen ein escaptes Hochkomma vorkommt. So wird beispielweise aus folgender Zeile;echo "Das hier ist ein \"Test\"!";die folgende formatierte Ausgabe:
echo "Das hier ist ein \"Test\"!";Der Grund, warum dieser "Fehler" noch nicht behoben wurde, liegt darin, dass verschiedene Programmiersprachen unterschiedlich mit dem Escapen von solchen Zeichen umgehen und mir noch keine universelle Lösung dafür eingefallen ist.
