jfcherng/php-diff

A comprehensive library for generating differences between two strings in multiple formats (unified, side by side HTML etc).

Maintainers

👁 jfcherng

Package info

github.com/jfcherng/php-diff

pkg:composer/jfcherng/php-diff

Fund package maintenance!

www.paypal.me/jfcherng/5usd

Statistics

Installs: 5 800 856

Dependents: 51

Suggesters: 2

Stars: 478

Open Issues: 5

7.0.1 2026-06-22 13:04 UTC

Suggests

None

Provides

None

Conflicts

None

Replaces

None

BSD-3-Clause 38bd25b2ca59e2fe0d8dd209b5557ca193b73a43

  • Jack Cherng <jfcherng.woop@gmail.com>
  • Chris Boulton <chris.boulton.woop@interspire.com>

diffudiffunified diffunidiff

This package is auto-updated.

Last update: 2026-06-22 13:33:26 UTC


README

👁 GitHub Workflow Status (branch)
👁 Codacy grade
👁 Packagist
👁 Packagist Version
👁 Project license
👁 GitHub stars
👁 Donate to this project using Paypal

A comprehensive library for generating diff between two strings.

Introduction

Generated diff can be rendered in all of the standard formats including:

Text renderers:

  • Context
  • Json (plain text)
  • Unified

HTML renderers:

  • Combined
  • Inline
  • Json (HTML)
  • Side by Side

Note that for HTML rendered results, you have to add CSS for a better visualization. You may modify one from example/diff-table.css or write your own from zero.

If you are okay with the default CSS, there is \Jfcherng\Diff\DiffHelper::getStyleSheet() which can be used to get the content of the example/diff-table.css.

Requirements

👁 php
👁 ext-iconv

Installation

This package is available on Packagist by the name of jfcherng/php-diff.

composer require jfcherng/php-diff

Example

See files and readme in the example/ directory.

<?php

include __DIR__ . '/vendor/autoload.php';

use Jfcherng\Diff\Differ;
use Jfcherng\Diff\DiffHelper;
use Jfcherng\Diff\Factory\RendererFactory;
use Jfcherng\Diff\Options\DifferOptions;
use Jfcherng\Diff\Options\RendererOptions;
use Jfcherng\Diff\Renderer\RendererConstant;

$oldFile = __DIR__ . '/example/old_file.txt';
$newFile = __DIR__ . '/example/new_file.txt';

$old = 'This is the old string.';
$new = 'And this is the new one.';

// renderer class name:
// Text renderers: Context, JsonText, Unified
// HTML renderers: Combined, Inline, JsonHtml, SideBySide
$rendererName = 'Unified';

// the Differ options
$differOptions = new DifferOptions(
 // show how many neighbor lines; Differ::CONTEXT_ALL shows the whole file
 context: 3,
 // ignore case difference
 ignoreCase: false,
 // ignore line ending difference
 ignoreLineEnding: false,
 // ignore whitespace difference
 ignoreWhitespace: false,
 // if the input sequence is too long, give up (especially for char-level diff)
 lengthLimit: 2000,
 // when inputs are identical, render the whole content rather than an empty result
 fullContextIfIdentical: false,
);

// the renderer options
$rendererOptions = new RendererOptions(
 // how detailed the rendered HTML in-line diff is? (none, line, word, char)
 detailLevel: 'line',
 // renderer language: eng, cht, chs, jpn, ...
 // or an array which has the same keys with a language file
 // check the "Custom Language" section in the readme for more advanced usage
 language: 'eng',
 // show line numbers in HTML renderers
 lineNumbers: true,
 // show a separator between different diff hunks in HTML renderers
 separateBlock: true,
 // show the (table) header
 showHeader: true,
 // render spaces/tabs as <span class="ch sp"> </span> tags (visualised via CSS)
 spaceToHtmlTag: false,
 // convert consecutive spaces to &nbsp; in HTML output
 spacesToNbsp: false,
 // HTML renderer tab width (negative = do not convert into spaces)
 tabSize: 4,
 // Combined renderer: merge replace-blocks whose changed ratio is at or below this threshold (0–1)
 mergeThreshold: 0.8,
 // Unified/Context renderers CLI colorization:
 // RendererConstant::CLI_COLOR_AUTO = colorize if possible (default)
 // RendererConstant::CLI_COLOR_ENABLE = force colorize
 // RendererConstant::CLI_COLOR_DISABLE = force no color
 cliColorization: RendererConstant::CLI_COLOR_AUTO,
 // JSON renderer: emit op tags as human-readable strings instead of ints
 outputTagAsString: false,
 // JSON renderer: flags passed to json_encode()
 // see https://www.php.net/manual/en/function.json-encode.php
 jsonEncodeFlags: \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE,
 // word-level diff: adjacent segments joined by these characters are merged into one
 // e.g. "<del>good</del>-<del>looking</del>" → "<del>good-looking</del>"
 wordGlues: ['-', ''],
 // return this string verbatim when the two inputs are identical; null = renderer default
 resultForIdenticals: null,
 // extra CSS classes added to the diff container <div> in HTML renderers
 wrapperClasses: ['diff-wrapper'],
);

// one-line simply compare two files
$result = DiffHelper::calculateFiles($oldFile, $newFile, $rendererName, $differOptions, $rendererOptions);
// one-line simply compare two strings
$result = DiffHelper::calculate($old, $new, $rendererName, $differOptions, $rendererOptions);
// or even shorter if you are happy with default options
$result = DiffHelper::calculate($old, $new, $rendererName);

// custom usage
$differ = new Differ(explode("\n", $old), explode("\n", $new), $differOptions);
$renderer = RendererFactory::make($rendererName, $rendererOptions); // or your own renderer object
$result = $renderer->render($differ);

// use the JSON result to render in HTML
$jsonResult = DiffHelper::calculate($old, $new, 'Json'); // may store the JSON result in your database
$htmlRenderer = RendererFactory::make('Inline', $rendererOptions);
$result = $htmlRenderer->renderArray(json_decode($jsonResult, true));

Rendered Results

HTML Diff In-line Detailed Rendering

None-level Line-level (Default)
👁 Image
👁 Image
Word-level Char-level
👁 Image
👁 Image

Renderer: Inline

<?php $rendererOptions = ['detailLevel' => 'line'];

👁 Inline

Renderer: Side By Side

<?php $rendererOptions = ['detailLevel' => 'line'];

👁 Side By Side

Renderer: Combined

<?php $rendererOptions = ['detailLevel' => 'word'];

This renderer is suitable for articles and always has no line number information.

👁 Combined

Renderer: Unified

About the Unified diff format: https://en.wikipedia.org/wiki/Diff#Unified_format

@@ -1,3 +1,4 @@
-<p>Hello World!</p>
+<div>Hello World!</div>
 ~~~~~~~~~~~~~~~~~~~
+Let's add a new line here.
 X
@@ -7,6 +8,5 @@
 N
-Do you know in Chinese, "é‡‘æ§é­šį―é ­" means tuna can.
+Do you know in Japanese, "魚ãŪįžķčа" means fish can.
 This is just a useless line.
 G
-// remember to delete this line
 Say hello to my neighbors.

Renderer: Context

About the Context diff format: https://en.wikipedia.org/wiki/Diff#Context_format

Renderer: Text JSON

This renderer has no detailed diff.

Renderer: HTML JSON

For a "tag": "rep" (8) block, this renderer has HTML-style detailed diff. If you don't need those detailed diff, consider using the JsonText renderer.

Custom Language

Override an Existing Language

If you just want to override some translations of an existing language...

$rendererOptions = [
 'language' => [
 // use English as the base language
 'eng',
 // your custom overrides
 [
 // use "Diff" as the new value of the "differences" key
 'differences' => 'Diff',
 ],
 // maybe more overrides if you somehow need them...
 ],
]

Acknowledgment

This package is built on the top of chrisboulton/php-diff initially. But the original repository looks like no longer maintained. Here have been quite lots of rewrites and new features since then, hence I re-started this as a new package for better visibility.