easybook/geshi
GeSHi is a PHP syntax highlighter that converts source code into HTML with language-aware formatting. This EasyBook-maintained fork supports numerous languages and is useful for rendering code in documentation, blogs, and PHP applications.
Install with Composer:
composer require easybook/geshi
There is no service provider and no namespaced façade. The package exposes a single global class, GeSHi, autoloaded from geshi.php via a classmap. The smallest usable call is:
$geshi = new \GeSHi($source, 'php');
echo $geshi->parse_code();
Where to look first:
vendor/easybook/geshi/geshi.php — the entire public API lives in this one class. Search for function set_, function enable_, and function get_ to see your knobs.vendor/easybook/geshi/geshi/ — one PHP file per supported language (php.php, javascript.php, blade.php if present, etc.). The filenames are the language identifiers you pass to the constructor.The first real use case is rendering a user-facing code block — a blog post snippet, a README viewer, an admin panel showing config files. Create a thin wrapper immediately rather than sprinkling new \GeSHi(...) through controllers:
namespace App\Support;
class Highlighter
{
public function render(string $code, string $language = 'text'): string
{
$geshi = new \GeSHi($code, $language);
if ($geshi->error()) {
return '<pre>' . e($code) . '</pre>';
}
$geshi->enable_classes();
$geshi->set_header_type(GESHI_HEADER_PRE_VALID);
$geshi->set_tab_width(4);
return $geshi->parse_code();
}
}
Check error() before trusting the output — the constructor does not throw when the language file is missing.
Use classes, not inline styles. Out of the box GeSHi emits inline style="" attributes, which bloats HTML and makes theming impossible. Call enable_classes() early, then ship one stylesheet with rules for the standard token classes: .kw1–.kw4 (keywords), .co1–.co4 (comments), .st0–.st_h (strings), .br0, .sy0, .nu0, .me1, .re1, .sc0, .es0, .de1. The specific numbers depend on the language file, so generate or audit CSS against the languages you actually support.
Wrap it in a Blade component. Keep markup concerns in the view layer:
{{-- resources/views/components/code.blade.php --}}
@props(['code', 'language' => 'text'])
<div class="code-block">
{!! app(\App\Support\Highlighter::class)->render($code, $language) !!}
</div>
Cache the rendered HTML. GeSHi re-parses the language file for every instance and has no result cache. For content that does not change often, cache by a hash of (code, language, options):
$key = 'geshi:' . md5($language . '|' . $code);
return Cache::remember($key, now()->addDay(), fn () =>
app(\App\Support\Highlighter::class)->render($code, $language)
);
Add line numbers and highlighted lines when reading code reviews:
$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 5); // number every 5th
$geshi->highlight_lines_extra([2, 7]);
$geshi->set_highlight_lines_extra_style('background: #fff3cd;');
Register custom languages or keyword groups. Drop a language file into resources/geshi/ and call set_language_path(resource_path('geshi')) before parse_code(). For small additions use add_keyword_group($language, GESHI_KEYWORDS_CASESENSITIVE, $keywords).
Integrate with Markdown rendering. If you render Markdown through league/commonmark, register a custom fenced-code block renderer (or post-process the HTML) that delegates to your wrapper. Do not try to run GeSHi over already-rendered HTML — it expects raw source and will double-escape it.
Reuse instances when rendering many blocks. set_source() and set_language() let you reset an instance between calls, which avoids repeated constructor overhead in a loop, though the language file is still re-read.
error() is the only error channel. A typo in the language name silently produces an error comment or unstyled output depending on configuration. Always branch on $geshi->error() and fall back to e($source) in a <pre>.htmlspecialchars; passing e($code) produces visible HTML entities.geshi.php. Copying or relocating the class file without the geshi/ directory breaks every language except the built-ins. set_language_path() is the fix, and is mandatory if you ship your own language files.GESHI_HEADER_PRE_TABLE (used for line numbers) emits a <table> that fights flex/grid layouts and can break horizontal scrolling. Prefer GESHI_HEADER_PRE_VALID unless you need per-line markup, and style .linenumbers yourself..kw1 in PHP does not mean the same token as .kw1 in JavaScript. If you publish a shared stylesheet, scope it per language (.language-php .kw1 { … }) rather than assuming global consistency.set_overall_style(), set_line_style(), and keyword-URL mappings are written verbatim into the output. Never pass user-controlled strings to them.How can I help you explore Laravel packages today?