Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Highlight Bundle Laravel Package

nicodmf/highlight-bundle

Symfony bundle that integrates highlight.php to add syntax highlighting for code snippets. It provides Twig helpers and configurable themes, making it easy to render readable, colorized source code in templates with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony2 bundle, not a Laravel package. composer require nicodmf/highlight-bundle will not produce a working feature in a Laravel app — no service provider, no Blade extension, no config publish, and the code targets Symfony2-era container wiring. Do not install it. Instead, read it as a reference spec and build a ~1–2 day Laravel-native equivalent.

Read these three things first, in this order:

  1. The highlighter interface plus its concrete drivers (Pygment, Geshi, Highlight). This is the whole reusable idea: a highlight(string $code, string $lang): string seam over swappable engines.
  2. The Twig extension. It shows the intended DX — piping code into a filter with a language argument. Your equivalent is a Blade directive or anonymous component.
  3. The theme/config layer. Note how the backend is chosen — that decision leaks into output HTML, so pin it explicitly.

Then pick your engine before writing any code:

  • scrivo/highlight.php (highlight.js port) — pure PHP, no shell exec, installs with Composer. Start here for Laravel.
  • Pygments (pygmentize) — best quality, but requires Python in every container and a Process call per snippet.
  • GNU highlight — needs --out-format=xhtml; default output is ANSI and useless in HTML.
  • GeSHi (easybook/geshi) — pure PHP, dated output, viable if scrivo has a coverage gap.

First use case: a docs, blog, or changelog page rendering fenced code blocks from stored Markdown. The realistic Laravel shape is a highlight() helper + a <x-code-block :code="$snippet" language="php" /> component, backed by a cached service.

Implementation Patterns

Bind an interface, not a backend. That's the one durable lesson from the bundle:

interface Highlighter { public function highlight(string $code, string $language): string; }

Bind ScrivoHighlighter::class in a HighlighterServiceProvider. Swap engines later without touching Blade.

Blade component over directive. A directive that returns HTML bypasses escaping mental models. Use an anonymous component that returns a HtmlString explicitly, so reviewers can see the unescaped boundary.

<x-code-block language="php" :code="$snippet" />
// resources/views/components/code-block.blade.php
<pre class="hljs"><code class="language-{{ $language }}">{!! app(Highlighter::class)->highlight($code, $language) !!}</code></pre>

Cache at the edge, keyed on content. Highlighting is CPU-bound (and process-bound if shelling out). Wrap the engine in a CachedHighlighter decorator:

$key = 'hl:'.hash('xxh128', $theme.'|'.$language.'|'.$code);
return Cache::remember($key, now()->addDay(), fn () => $engine->highlight($code, $language));

If snippets come out of a model, prefer precomputing into a highlighted_html column on save and rendering that — you remove highlighting from the request path entirely. This is what the Symfony bundle never solved, and it's the difference between a snappy page and a p95 blowout.

Use class-based themes, not inline styles. Emit hljs classes and ship the theme via Vite, so output is CSP-friendly and swappable per page. Pygments-based engines tend toward inline stylesheets — strip or override them.

Normalize unknown languages to a safe fallback. Map lang ?: 'text' to a registered language; render raw-but-escaped htmlspecialchars() inside <code> when unknown. Never fall through to "just output the string" — that's how you get XSS.

Guard untrusted input. Cap input length (e.g. 20 KB), strip CRLF edge cases, and treat output as untrusted if the source is user-submitted. Even a pure-PHP port can emit markup you don't want in a <script> context.

Gotchas and Tips

  • Don't --ignore-platform-reqs your way into the bundle. Modern Laravel on PHP 8.x will at best autoload dead classes; at worst you get container or autoloader conflicts. The Symfony Bundle concept has no analogue in Laravel.
  • Never render highlighted output in a context where raw HTML is unsafe. A syntax highlighter is not a sanitizer. If snippets are user-supplied (support tickets, PR diffs, marketplace listings), run output through an allowlist (e.g. mews/purifier or a DOM-whitelist pass) or pre-render and store sanitized HTML.
  • Binary-backed engines rot silently. Pygments/highlight binaries must exist in every image — local, CI, staging, prod. A missing binary either throws a 500 or, worse, degrades to unescaped raw code. Pin the version in your Dockerfile and add a startup health check.
  • Subprocess hangs and OOMs. If you shell out, use Symfony\Component\Process\Process with a hard timeout (setTimeout(2)) and an input cap. A multi-megabyte pasted snippet will happily fork-bomb your workers at peak traffic.
  • Language detection is unreliable. scrivo and highlight.js guess from content; don't rely on it for user-facing labels or links. Always require an explicit language argument from the template, defaulting to text.
  • Escape first, then decorate. If you precompute "highlighted HTML" and later escape it, you've broken syntax coloring. If you escape and then decorate, ensure your engine escapes <, >, & before emitting spans. Verify by highlighting a snippet that literally contains <script>alert(1)</script>.
  • Watch the theme/CSS contract. Precomputed HTML assumes a specific theme class set. Store the theme identifier alongside cached output, or you'll silently render the wrong palette after a theme change. Invalidate the cache when config('highlight.theme') changes.
  • Extension point worth keeping: the driver/interface split. When a required language shows up that scrivo can't handle (some LaTeX dialects, niche DSLs), add a PygmentsHighlighter behind the same interface — don't rewire Blade.
  • Test against a fixtures corpus on day one: HTML, PHP with heredocs, a file with mixed line endings, an empty string, and a very long single line. Any of these can produce malformed markup or a hung subprocess in older engines.
  • Estimate: ~1–2 engineer-days for the service provider, component, config, and cache decorator; half a day for sanitization and the fixtures suite. That's the whole budget — far cheaper than owning a fork of the original bundle.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin