'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; '; } } }