'python',
'pyw' => 'python',
'js' => 'javascript',
'mjs' => 'javascript',
'go' => 'go',
'rs' => 'rust',
'sh' => 'bash',
'bash' => 'bash',
'zsh' => 'bash',
'fish' => 'bash',
'md' => 'markdown',
'markdown' => 'markdown',
'css' => 'css',
'scss' => 'css',
'sass' => 'css',
'html' => 'html',
'htm' => 'html',
'xhtml' => 'html',
'php' => 'php',
'php3' => 'php',
'php4' => 'php',
'php5' => 'php',
'phtml' => 'php',
'env' => 'env',
'json' => 'json',
'yaml' => 'yaml',
'yml' => 'yaml',
'config' => 'config',
'conf' => 'config',
'ini' => 'config',
'cfg' => 'config',
'txt' => 'text',
'log' => 'text',
'sql' => 'sql',
'mysql' => 'sql',
'pgsql' => 'sql',
'xml' => 'xml',
'c' => 'c',
'cpp' => 'cpp',
'cxx' => 'cpp',
'cc' => 'cpp',
'h' => 'c',
'hpp' => 'cpp',
'hxx' => 'cpp',
'java' => 'java',
'rb' => 'ruby',
'ruby' => 'ruby',
'pl' => 'perl',
'pm' => 'perl',
'ts' => 'typescript',
'tsx' => 'typescript',
'jsx' => 'javascript',
'vue' => 'html',
'dockerfile' => 'bash',
'makefile' => 'bash',
'gitignore' => 'text',
'toml' => 'config',
'properties' => 'config'
];
private static function getLanguage($extension) {
return self::$languageMap[strtolower($extension)] ?? 'text';
}
public static function highlight($code, $extension, $theme = 'dark', $showLineNumbers = true) {
$language = self::getLanguage($extension);
// Escape HTML first
$code = htmlspecialchars($code, ENT_NOQUOTES);
// Apply syntax highlighting based on language
switch ($language) {
case 'python':
$highlighted = self::highlightPython($code, $theme);
break;
case 'javascript':
case 'typescript':
$highlighted = self::highlightJavaScript($code, $theme);
break;
case 'go':
$highlighted = self::highlightGo($code, $theme);
break;
case 'rust':
$highlighted = self::highlightRust($code, $theme);
break;
case 'bash':
$highlighted = self::highlightBash($code, $theme);
break;
case 'markdown':
$highlighted = self::highlightMarkdown($code, $theme);
break;
case 'css':
$highlighted = self::highlightCSS($code, $theme);
break;
case 'html':
$highlighted = self::highlightHTML($code, $theme);
break;
case 'php':
$highlighted = self::highlightPHP($code, $theme);
break;
case 'json':
$highlighted = self::highlightJSON($code, $theme);
break;
case 'yaml':
$highlighted = self::highlightYAML($code, $theme);
break;
case 'sql':
$highlighted = self::highlightSQL($code, $theme);
break;
case 'c':
case 'cpp':
$highlighted = self::highlightC($code, $theme);
break;
case 'java':
$highlighted = self::highlightJava($code, $theme);
break;
default:
$highlighted = self::highlightGeneric($code, $theme);
break;
}
// Add line numbers if requested
if ($showLineNumbers) {
return self::addLineNumbers($highlighted, $theme);
}
return $highlighted;
}
private static function getColors($theme = 'dark') {
if ($theme === 'dark') {
return [
'keyword' => '#569CD6', // Blue
'string' => '#CE9178', // Orange
'comment' => '#6A9955', // Green
'number' => '#B5CEA8', // Light green
'operator' => '#D4D4D4', // Light gray
'function' => '#DCDCAA', // Yellow
'variable' => '#9CDCFE', // Light blue
'type' => '#4EC9B0', // Teal
'constant' => '#4FC1FF', // Bright blue
'tag' => '#92C5F8', // Light blue
'attribute' => '#FFCB6B', // Yellow
'punctuation' => '#D4D4D4', // Light gray
'builtin' => '#FFCB6B' // Yellow
];
} else {
return [
'keyword' => '#0000FF', // Blue
'string' => '#008000', // Green
'comment' => '#008000', // Green
'number' => '#098658', // Dark green
'operator' => '#000000', // Black
'function' => '#795E26', // Brown
'variable' => '#001080', // Dark blue
'type' => '#267F99', // Teal
'constant' => '#0070C1', // Blue
'tag' => '#800000', // Maroon
'attribute' => '#FF0000', // Red
'punctuation' => '#000000', // Black
'builtin' => '#0000FF' // Blue
];
}
}
private static function highlightPython($code, $theme) {
$colors = self::getColors($theme);
// Python keywords
$keywords = ['def', 'class', 'if', 'elif', 'else', 'for', 'while', 'try', 'except', 'finally',
'import', 'from', 'as', 'return', 'yield', 'lambda', 'with', 'pass', 'break',
'continue', 'and', 'or', 'not', 'in', 'is', 'True', 'False', 'None'];
$builtins = ['print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'tuple', 'set'];
// Comments (avoid conflicts by doing this first)
$code = preg_replace('/^(\s*)(#(?!["\']).*?)$/m', '$1$2', $code);
// Multi-line strings first
$code = preg_replace('/(""".*?"""|\'\'\'.*?\'\'\')/s', '$0', $code);
// Single line strings
$code = preg_replace('/(?$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?)\b/', '$0', $code);
// Keywords
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/', '$0', $code);
}
// Built-ins
foreach ($builtins as $builtin) {
$code = preg_replace('/\b(' . preg_quote($builtin) . ')\b(?=\s*\()/', '$0', $code);
}
// Functions
$code = preg_replace('/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/', '$1', $code);
return $code;
}
private static function highlightJavaScript($code, $theme) {
$colors = self::getColors($theme);
$keywords = ['var', 'let', 'const', 'function', 'if', 'else', 'for', 'while', 'do', 'switch',
'case', 'default', 'break', 'continue', 'return', 'try', 'catch', 'finally',
'throw', 'new', 'this', 'typeof', 'instanceof', 'true', 'false', 'null', 'undefined'];
// Comments
$code = preg_replace('/\/\/.*$|\/\*.*?\*\//ms', '$0', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1|`(?:[^`\\\\]|\\\\.)*`/', '$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?)\b/', '$0', $code);
// Keywords
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/', '$0', $code);
}
// Functions
$code = preg_replace('/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*(?=\()/', '$1', $code);
return $code;
}
private static function highlightGo($code, $theme) {
$colors = self::getColors($theme);
$keywords = ['package', 'import', 'func', 'var', 'const', 'type', 'struct', 'interface',
'if', 'else', 'for', 'range', 'switch', 'case', 'default', 'return', 'break',
'continue', 'go', 'defer', 'select', 'chan', 'map', 'true', 'false', 'nil'];
$types = ['string', 'int', 'int8', 'int16', 'int32', 'int64', 'uint', 'uint8', 'uint16',
'uint32', 'uint64', 'float32', 'float64', 'bool', 'byte', 'rune', 'error'];
// Comments
$code = preg_replace('/\/\/.*$|\/\*.*?\*\//ms', '$0', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1|`[^`]*`/', '$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?)\b/', '$0', $code);
// Keywords
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/', '$0', $code);
}
// Types
foreach ($types as $type) {
$code = preg_replace('/\b(' . preg_quote($type) . ')\b/', '$0', $code);
}
// Functions
$code = preg_replace('/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/', '$1', $code);
return $code;
}
private static function highlightRust($code, $theme) {
$colors = self::getColors($theme);
$keywords = ['fn', 'let', 'mut', 'const', 'static', 'if', 'else', 'match', 'for', 'while',
'loop', 'break', 'continue', 'return', 'struct', 'enum', 'impl', 'trait',
'use', 'mod', 'pub', 'crate', 'super', 'self', 'Self', 'true', 'false'];
$types = ['i8', 'i16', 'i32', 'i64', 'i128', 'isize', 'u8', 'u16', 'u32', 'u64', 'u128',
'usize', 'f32', 'f64', 'bool', 'char', 'str', 'String', 'Vec', 'Option', 'Result'];
// Comments
$code = preg_replace('/\/\/.*$|\/\*.*?\*\//ms', '$0', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1/', '$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?)\b/', '$0', $code);
// Keywords
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/', '$0', $code);
}
// Types
foreach ($types as $type) {
$code = preg_replace('/\b(' . preg_quote($type) . ')\b/', '$0', $code);
}
// Functions
$code = preg_replace('/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/', '$1', $code);
return $code;
}
private static function highlightBash($code, $theme) {
$colors = self::getColors($theme);
$keywords = ['if', 'then', 'else', 'elif', 'fi', 'case', 'esac', 'for', 'while', 'until',
'do', 'done', 'function', 'return', 'exit', 'break', 'continue', 'export',
'local', 'readonly', 'declare', 'typeset', 'unset'];
// Comments
$code = preg_replace('/^(\s*)(#.*)$/m', '$1$2', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1/', '$0', $code);
// Variables
$code = preg_replace('/\$\{?[a-zA-Z_][a-zA-Z0-9_]*\}?|\$[0-9@*#?$!-]/', '$0', $code);
// Keywords
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/', '$0', $code);
}
return $code;
}
private static function highlightJSON($code, $theme) {
$colors = self::getColors($theme);
// Strings (keys and values)
$code = preg_replace('/"([^"\\\\]|\\\\.)*"/', '$0', $code);
// Numbers
$code = preg_replace('/\b(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\b/', '$0', $code);
// Booleans and null
$code = preg_replace('/\b(true|false|null)\b/', '$0', $code);
return $code;
}
private static function highlightPHP($code, $theme) {
$colors = self::getColors($theme);
$keywords = ['class', 'function', 'if', 'else', 'elseif', 'for', 'foreach', 'while', 'do',
'switch', 'case', 'default', 'break', 'continue', 'return', 'try', 'catch',
'finally', 'throw', 'new', 'public', 'private', 'protected', 'static',
'final', 'abstract', 'interface', 'extends', 'implements', 'namespace',
'use', 'as', 'true', 'false', 'null'];
// Comments
$code = preg_replace('/\/\/.*$|\/\*.*?\*\/|#.*$/ms', '$0', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1/', '$0', $code);
// Variables
$code = preg_replace('/\$[a-zA-Z_][a-zA-Z0-9_]*/', '$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?)\b/', '$0', $code);
// Keywords
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/', '$0', $code);
}
// Functions
$code = preg_replace('/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/', '$1', $code);
// PHP tags
$code = preg_replace('/<\?php|<\?|\?>/', '$0', $code);
return $code;
}
private static function highlightCSS($code, $theme) {
$colors = self::getColors($theme);
// Selectors
$code = preg_replace('/^([^{]+)\s*\{/m', '$1 {', $code);
// Properties
$code = preg_replace('/([a-zA-Z-]+)\s*:/', '$1:', $code);
// Values
$code = preg_replace('/:\s*([^;]+);/', ': $1;', $code);
// Comments
$code = preg_replace('/\/\*.*?\*\//', '$0', $code);
return $code;
}
private static function highlightHTML($code, $theme) {
$colors = self::getColors($theme);
// Tags
$code = preg_replace('/<(\/?[a-zA-Z][a-zA-Z0-9]*)[^&]*?>/', '$0', $code);
// Attributes
$code = preg_replace('/([a-zA-Z-]+)=/', '$1=', $code);
// Attribute values
$code = preg_replace('/=(["\'])([^"\']*)\1/', '=$1$2$1', $code);
// Comments
$code = preg_replace('/<!--.*?-->/', '$0', $code);
return $code;
}
private static function highlightMarkdown($code, $theme) {
$colors = self::getColors($theme);
// Headers
$code = preg_replace('/^(#{1,6})\s*(.*)$/m', '$1 $2', $code);
// Bold
$code = preg_replace('/\*\*(.*?)\*\*/', '**$1**', $code);
// Italic
$code = preg_replace('/\*(.*?)\*/', '*$1*', $code);
// Code blocks
$code = preg_replace('/`([^`]+)`/', '$0', $code);
// Links
$code = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '[$1]($2)', $code);
return $code;
}
private static function highlightYAML($code, $theme) {
$colors = self::getColors($theme);
// Comments
$code = preg_replace('/^(\s*)(#.*)$/m', '$1$2', $code);
// Keys
$code = preg_replace('/^(\s*)([a-zA-Z_][a-zA-Z0-9_-]*)\s*:/m', '$1$2:', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1/', '$0', $code);
// Booleans and null
$code = preg_replace('/\b(true|false|null|yes|no)\b/', '$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?)\b/', '$0', $code);
return $code;
}
private static function highlightSQL($code, $theme) {
$colors = self::getColors($theme);
$keywords = ['SELECT', 'FROM', 'WHERE', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'DROP',
'ALTER', 'TABLE', 'INDEX', 'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER', 'ON',
'GROUP', 'ORDER', 'BY', 'HAVING', 'LIMIT', 'OFFSET', 'AND', 'OR', 'NOT',
'IN', 'EXISTS', 'BETWEEN', 'LIKE', 'IS', 'NULL', 'TRUE', 'FALSE'];
// Comments
$code = preg_replace('/--.*$|\/\*.*?\*\//ms', '$0', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1/', '$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?)\b/', '$0', $code);
// Keywords (case insensitive)
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/i', '$0', $code);
}
return $code;
}
private static function highlightC($code, $theme) {
$colors = self::getColors($theme);
$keywords = ['if', 'else', 'for', 'while', 'do', 'switch', 'case', 'default', 'break',
'continue', 'return', 'goto', 'sizeof', 'typedef', 'struct', 'union', 'enum',
'static', 'extern', 'auto', 'register', 'const', 'volatile', 'inline'];
$types = ['void', 'char', 'short', 'int', 'long', 'float', 'double', 'signed', 'unsigned'];
// Comments
$code = preg_replace('/\/\/.*$|\/\*.*?\*\//ms', '$0', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1/', '$0', $code);
// Preprocessor directives
$code = preg_replace('/^(\s*)(#.*)$/m', '$1$2', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?[fFlL]?)\b/', '$0', $code);
// Keywords
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/', '$0', $code);
}
// Types
foreach ($types as $type) {
$code = preg_replace('/\b(' . preg_quote($type) . ')\b/', '$0', $code);
}
// Functions
$code = preg_replace('/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/', '$1', $code);
return $code;
}
private static function highlightJava($code, $theme) {
$colors = self::getColors($theme);
$keywords = ['abstract', 'assert', 'boolean', 'break', 'byte', 'case', 'catch', 'char',
'class', 'const', 'continue', 'default', 'do', 'double', 'else', 'enum',
'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', 'true', 'false', 'null'];
// Comments
$code = preg_replace('/\/\/.*$|\/\*.*?\*\//ms', '$0', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1/', '$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?[fFlL]?)\b/', '$0', $code);
// Keywords
foreach ($keywords as $keyword) {
$code = preg_replace('/\b(' . preg_quote($keyword) . ')\b/', '$0', $code);
}
// Functions
$code = preg_replace('/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/', '$1', $code);
return $code;
}
private static function highlightGeneric($code, $theme) {
$colors = self::getColors($theme);
// Basic highlighting for unknown languages
// Comments (# and //)
$code = preg_replace('/^(\s*)(#.*|\/\/.*)$/m', '$1$2', $code);
// Strings
$code = preg_replace('/(["\'])(?:(?=(\\\\?))\\2.)*?\\1/', '$0', $code);
// Numbers
$code = preg_replace('/\b(\d+(?:\.\d+)?)\b/', '$0', $code);
return $code;
}
private static function addLineNumbers($code, $theme = 'dark') {
$colors = self::getColors($theme);
$lines = explode("\n", $code);
$totalLines = count($lines);
$lineNumberWidth = strlen((string)$totalLines);
$numberedLines = [];
for ($i = 0; $i < $totalLines; $i++) {
$lineNumber = $i + 1;
$paddedNumber = str_pad($lineNumber, $lineNumberWidth, ' ', STR_PAD_LEFT);
$lineNumberSpan = '' . $paddedNumber . '';
$numberedLines[] = $lineNumberSpan . $lines[$i];
}
return implode("\n", $numberedLines);
}
public static function getStyle($theme = 'dark') {
if ($theme === 'dark') {
return '
background-color: #1A1E23;
color: #d4d4d4;
font-family: "Consolas", "Monaco", "Courier New", monospace;
font-size: 14px;
line-height: 1.4;
padding: 16px;
border-radius: 8px;
overflow-x: auto;
white-space: pre;
border: 1px solid #333;
counter-reset: line;
';
} else {
return '
background-color: #ffffff;
color: #000000;
font-family: "Consolas", "Monaco", "Courier New", monospace;
font-size: 14px;
line-height: 1.4;
padding: 16px;
border-radius: 8px;
overflow-x: auto;
white-space: pre;
border: 1px solid #ccc;
counter-reset: line;
';
}
}
}