Skip to content
A comprehensive library for generating differences between two strings in multiple formats (unified, side by side HTML etc).
Branch: v6
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.phan
CHANGELOG
UPGRADING
example
src
tests
.gitignore
.php_cs
.rmt.yml
.travis.yml
LICENSE
README.md
composer.json
composer.lock
phpcs.xml
phpunit.xml

README.md

Build Status Codacy Badge

php-diff

A comprehensive library for generating diff between two strings.

Introduction

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

  • Unified (Text)
  • Context (Text)
  • Json (Text)
  • Inline (HTML)
  • Side by Side (HTML)

Requirements

  • PHP >= 7.1.3
  • Extension: iconv

Installation

$ composer require jfcherng/php-diff

Example

See example/demo.php and files under tests/.

<?php

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

use Jfcherng\Diff\Differ;
use Jfcherng\Diff\DiffHelper;
use Jfcherng\Diff\Factory\RendererFactory;

$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: Unified, Context, Json, Inline, SideBySide
$rendererName = 'Unified';

// the Diff class options
$differOptions = [
    // show how many neighbor lines
    'context' => 3,
    // ignore case difference
    'ignoreCase' => false,
    // ignore whitespace difference
    'ignoreWhitespace' => false,
];

// the renderer class options
$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
    'language' => 'eng',
    // show a separator between different diff hunks in HTML renderers
    'separateBlock' => true,
    // the frontend HTML could use CSS "white-space: pre;" to visualize consecutive whitespaces
    // but if you want to visualize them in the backend with "&nbsp;", you can set this to true
    'spacesToNbsp' => false,
    // HTML renderer tab width (negative = do not convert into spaces)
    'tabSize' => 4,
    // internally, ops (tags) are all int type but this is not good for human reading.
    // set this to "true" to convert them into string form before outputting.
    'outputTagAsString' => true,
];

// 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 renderers
$result = $renderer->render($differ);

Rendered Results

HTML Diff In-line Detailed Rendering

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

Renderer: Inline

Inline

Renderer: Side By Side

Side By Side

Renderer: Unified

@@ -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
-// @todo Remember to delete this line
 Say hello to my neighbors.

Renderer: Context

***************
*** 1,3 ****
! <p>Hello World!</p>
  ~~~~~~~~~~~~~~~~~~~
  X
--- 1,4 ----
! <div>Hello World!</div>
  ~~~~~~~~~~~~~~~~~~~
+ Let's add a new line here.
  X
***************
*** 7,12 ****
  N
! Do you know in Chinese, "金槍魚罐頭" means tuna can.
  This is just a useless line.
  G
- // @todo Remember to delete this line
  Say hello to my neighbors.
--- 8,12 ----
  N
! Do you know in Japanese, "魚の缶詰" means fish can.
  This is just a useless line.
  G
  Say hello to my neighbors.

Renderer: JSON

Click to expand
[
    [
        {
            "tag": 8,
            "old": {
                "offset": 0,
                "lines": [
                    "&lt;<del>p&gt;Hello World!&lt;/p</del>&gt;"
                ]
            },
            "new": {
                "offset": 0,
                "lines": [
                    "&lt;<ins>div&gt;Hello World!&lt;/div</ins>&gt;"
                ]
            }
        },
        {
            "tag": 1,
            "old": {
                "offset": 1,
                "lines": [
                    "~~~~~~~~~~~~~~~~~~~"
                ]
            },
            "new": {
                "offset": 1,
                "lines": [
                    "~~~~~~~~~~~~~~~~~~~"
                ]
            }
        },
        {
            "tag": 4,
            "old": {
                "offset": 2,
                "lines": []
            },
            "new": {
                "offset": 2,
                "lines": [
                    "Let's add a new line here."
                ]
            }
        },
        {
            "tag": 1,
            "old": {
                "offset": 2,
                "lines": [
                    "X"
                ]
            },
            "new": {
                "offset": 3,
                "lines": [
                    "X"
                ]
            }
        }
    ],
    [
        {
            "tag": 1,
            "old": {
                "offset": 6,
                "lines": [
                    "N"
                ]
            },
            "new": {
                "offset": 7,
                "lines": [
                    "N"
                ]
            }
        },
        {
            "tag": 8,
            "old": {
                "offset": 7,
                "lines": [
                    "Do you know in <del>Chinese, \"金槍魚罐頭\" means tuna</del> can."
                ]
            },
            "new": {
                "offset": 8,
                "lines": [
                    "Do you know in <ins>Japanese, \"魚の缶詰\" means fish</ins> can."
                ]
            }
        },
        {
            "tag": 1,
            "old": {
                "offset": 8,
                "lines": [
                    "This is just a useless line.",
                    "G"
                ]
            },
            "new": {
                "offset": 9,
                "lines": [
                    "This is just a useless line.",
                    "G"
                ]
            }
        },
        {
            "tag": 2,
            "old": {
                "offset": 10,
                "lines": [
                    "// @todo Remember to delete this line"
                ]
            },
            "new": {
                "offset": 11,
                "lines": []
            }
        },
        {
            "tag": 1,
            "old": {
                "offset": 11,
                "lines": [
                    "Say hello to my neighbors."
                ]
            },
            "new": {
                "offset": 11,
                "lines": [
                    "Say hello to my neighbors."
                ]
            }
        }
    ]
]

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.

Supporters

Thank you guys for sending me some cups of coffee.

You can’t perform that action at this time.