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

Fine Diff Bundle Laravel Package

greywolfs/fine-diff-bundle

A Symfony bundle for fine-grained diffing, helping you detect and present precise changes between values or entity states. Integrates into Symfony applications to support change tracking, audit trails, and readable comparisons of before/after data.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate expectations: this is a Symfony bundle, not a Laravel package. It is a thin wrapper around Raymond Hill's FineDiff text-diff library, last tagged in October 2017, with no dependents. There is no service provider, no facade, no config/fine_diff.php, and no Laravel auto-discovery. You are effectively adopting an unmaintained Symfony DI extension and its bundled engine.

Minimal path in a Laravel app:

composer require greywolfs/fine-diff-bundle

Expect the install to fight you. The 2017 release will constrain symfony/* to ^2.8 || ^3.0 and probably PHP ^5.5 || ^7.0. If Composer resolves it at all against a modern Laravel skeleton, verify what actually landed:

composer show greywolfs/fine-diff-bundle
ls vendor/greywolfs/fine-diff-bundle

Where to look first, in this order:

  1. vendor/greywolfs/fine-diff-bundle/DependencyInjection/ — the extension class tells you the config keys and the service ID it registers. The service ID is the only thing you actually need.
  2. vendor/greywolfs/fine-diff-bundle/Resources/config/services.* — the concrete class behind the service, plus constructor args (usually a default granularity).
  3. The bundled FineDiff class itself — getDiffOpcodes(), renderDiffFromStringToText(), renderDiffToHTMLFromOpcode(), and the $characterGranularity / $wordGranularity / $lineGranularity statics. That API, not the bundle, is what you'll spend your time with.

First use case — show what changed between two revisions of a model field:

// app/Providers/AppServiceProvider.php, register()
$this->app->singleton('fine-diff', function () {
    $class = \Greywolfs\FineDiffBundle\...\Diff::class; // confirm in vendor/
    return new $class(\FineDiff::$wordGranularity);
});
$html = app('fine-diff')->render($post->revision->body, $post->body);
return view('posts.diff', ['html' => $html]);

If that feels like more ceremony than value, the honest alternative is sebastian/diff (maintained, no framework coupling, and already in most Laravel dependency trees) or jfcherng/php-diff for a richer renderer. Treat this bundle as a candidate you evaluate, not a default you adopt.

Implementation Patterns

Wrap the service ID in a typed binding. Don't scatter app('greywolfs_fine_diff...') calls. Bind an interface once and let Laravel inject it:

$this->app->singleton(DiffRenderer::class, fn () => new FineDiffRenderer(
    granularity: \FineDiff::$wordGranularity
));

Choose granularity per call site, not globally. The bundle's config gives you one default; most real apps need several:

  • $wordGranularity — prose, commit messages, comment edits. Best signal-to-noise for humans.
  • $lineGranularity — code, config files, structured text.
  • $characterGranularity — short strings (titles, slugs, names) where word-level produces one giant replace block.
  • sentence / paragraph — long documents where you want a summary-level "what changed" view.

Expose granularity as a parameter on your wrapper rather than reconfiguring the container.

Store opcodes, not just rendered HTML. FineDiff's opcode string (c5i3d2c10...) is compact, deterministic, and lets you re-render at a different granularity or with a different template later without the original inputs:

$opcodes = $differ->getOpcodes($old, $new);      // persist this
$html    = $differ->renderFromOpcodes($opcodes); // render on read

Put it in a text column on your revisions table. This is the single highest-value pattern with any FineDiff wrapper — it decouples "detect the change" from "display the change."

Keep rendering and diffing separate. Compute the diff when the edit is saved (cheap, bounded, in the request), render it lazily. For large documents, push the opcode computation to a queued job and store the result; a diff view should never compute on page load.

Render with explicit escaping. FineDiff's HTML renderer emits <ins>/<del> markup but does not escape the source text. Escape first, diff second — or post-process. In Blade:

{!! $diffHtml !!}  {{-- only if you've escaped the inputs yourself --}}

Prefer a whitelist pass (strip_tags($html, '<ins><del><br>')) over trusting the renderer.

Style with plain CSS, not a component library. The output is ins/del elements. A dozen lines of CSS gets you a GitHub-style view; don't reach for a diffing JS library to render server-side HTML.

Test the semantics, not the markup. Assert on opcode structure or on presence/absence of ins/del around known tokens. Snapshot-testing the full HTML will break on any engine or escaping change and tells you nothing.

Gotchas and Tips

  • Symfony container assumptions. The bundle's extension expects symfony/http-kernel and a compiled container. Nothing in it knows about Laravel. You cannot "enable" it; you instantiate the class yourself or bind it manually. Reading DependencyInjection/*Extension.php is the fastest way to learn the intended defaults.
  • Dependency hell is the likely blocker. A 2017 Symfony bundle in a Laravel 10/11 app usually means Composer conflicts on symfony/console, symfony/yaml, or psr/*. Don't fight it with --ignore-platform-reqs in anything you intend to deploy — that's how you get a runtime fatal in production instead of an install-time error.
  • Unmaintained means unmaintained. Zero dependents and a 2017 last release mean no security triage, no PHP 8.2+ deprecation fixes, no multibyte corrections. If the bundled engine fails on emoji or CJK input, you own the patch. Fork it into app/Support/ if you keep it at all — that's a legitimate "extension point."
  • Multibyte is the classic FineDiff weak spot. Character granularity on UTF-8 strings can split a code point and produce mojibake mid-tag. Prefer word or line granularity for anything user-authored, and test with emoji and accented text before shipping.
  • Large inputs degrade badly. The algorithm is fine for paragraphs, not for multi-megabyte documents. Cap input length, truncate around the first divergence, or bail out to a "file changed" summary above a threshold.
  • Opcode format is undocumented and engine-version-specific. It's a positional encoding (c copy, d delete, i insert + length). Never hand-parse it in application code; treat it as an opaque blob. Never compare opcodes across library versions.
  • Debugging: if the output looks wrong, dump the opcodes first — var_dump($differ->getOpcodes($a, $b)) — and check whether the problem is diff detection or HTML rendering. Nearly always it's the former, caused by an unnormalized input (trailing whitespace, CRLF vs LF, HTML entities vs raw characters). Normalize line endings before diffing.
  • Escape-before-diff, not after. If you diff an already-HTML-escaped string you'll get diffs full of &amp; fragments and unreadable output. Diff raw text, escape the segments.
  • Where to extend: the clean seam is your own DiffRenderer interface with FineDiff as one implementation and sebastian/diff as another. That single abstraction makes migrating off this bundle a one-line change later, which, given the maintenance profile, you should assume you'll want.
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.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json