https://github.com/jfcherng/php-diff

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

https://github.com/jfcherng/php-diff

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (12.8%) to scientific vocabulary

Keywords

diff hacktoberfest psr-1 psr-12 psr-4
Last synced: 5 months ago · JSON representation

Repository

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

Basic Info
Statistics
  • Stars: 456
  • Watchers: 10
  • Forks: 51
  • Open Issues: 6
  • Releases: 100
Topics
diff hacktoberfest psr-1 psr-12 psr-4
Created about 7 years ago · Last pushed over 1 year ago
Metadata Files
Readme Changelog Funding License

README.md

php-diff

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.

bash composer require jfcherng/php-diff

Example

See files and readme in the example/ directory.

```php <?php

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

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

$oldFile = DIR . '/example/oldfile.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 Diff class options $differOptions = [ // show how many neighbor lines // Differ::CONTEXT_ALL can be used to show 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, it will just gives up (especially for char-level diff) 'lengthLimit' => 2000, // if truthy, when inputs are identical, the whole inputs will be rendered in the output 'fullContextIfIdentical' => 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 // 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, // the frontend HTML could use CSS "white-space: pre;" to visualize consecutive whitespaces // but if you want to visualize them in the backend with " ", you can set this to true 'spacesToNbsp' => false, // HTML renderer tab width (negative = do not convert into spaces) 'tabSize' => 4, // this option is currently only for the Combined renderer. // it determines whether a replace-type block should be merged or not // depending on the content changed ratio, which values between 0 and 1. 'mergeThreshold' => 0.8, // this option is currently only for the Unified and the Context renderers. // RendererConstant::CLICOLORAUTO = colorize the output if possible (default) // RendererConstant::CLICOLORENABLE = force to colorize the output // RendererConstant::CLICOLORDISABLE = force not to colorize the output 'cliColorization' => RendererConstant::CLICOLORAUTO, // this option is currently only for the Json renderer. // 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' => false, // this option is currently only for the Json renderer. // it controls how the output JSON is formatted. // see available options on https://www.php.net/manual/en/function.json-encode.php 'jsonEncodeFlags' => \JSONUNESCAPEDSLASHES | \JSONUNESCAPEDUNICODE, // this option is currently effective when the "detailLevel" is "word" // characters listed in this array can be used to make diff segments into a whole // for example, making "good-looking" into "good-looking" // this should bring better readability but set this to empty array if you do not want it 'wordGlues' => [' ', '-'], // change this value to a string as the returned diff if the two input strings are identical 'resultForIdenticals' => null, // extra HTML classes added to the DOM of the diff container '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)
Word-level Char-level

Renderer: Inline

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

Inline

Renderer: Side By Side

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

Side By Side

Renderer: Combined

php <?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

diff @@ -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

Click to expand ```diff *************** *** 1,3 **** !

Hello World!

~~~~~~~~~~~~~~~~~~~ X --- 1,4 ---- !
Hello World!
~~~~~~~~~~~~~~~~~~~ + 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 - // 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: Text JSON

This renderer has no detailed diff.

Click to expand ```json [ [ { "tag": "rep", "old": { "offset": 0, "lines": ["

Hello World! Good-looking.

"] }, "new": { "offset": 0, "lines": ["
Hello World! Bad-tempered.
"] } }, { "tag": "eq", "old": { "offset": 1, "lines": ["~~~~~~~~~~~~~~~~~~~"] }, "new": { "offset": 1, "lines": ["~~~~~~~~~~~~~~~~~~~"] } }, { "tag": "ins", "old": { "offset": 2, "lines": [] }, "new": { "offset": 2, "lines": ["Let's add a new line here."] } }, { "tag": "eq", "old": { "offset": 2, "lines": ["X"] }, "new": { "offset": 3, "lines": ["X"] } } ], [ { "tag": "eq", "old": { "offset": 6, "lines": ["N"] }, "new": { "offset": 7, "lines": ["N"] } }, { "tag": "rep", "old": { "offset": 7, "lines": ["Do you know in Chinese, \"金槍魚罐頭\" means tuna can."] }, "new": { "offset": 8, "lines": ["Do you know in Japanese, \"魚の缶詰\" means fish can."] } }, { "tag": "eq", "old": { "offset": 8, "lines": ["\t \tTab visualization test.", "G"] }, "new": { "offset": 9, "lines": ["\t \tTab visualization test.", "G"] } }, { "tag": "del", "old": { "offset": 10, "lines": ["// remember to delete this line"] }, "new": { "offset": 11, "lines": [] } }, { "tag": "eq", "old": { "offset": 11, "lines": ["Say hello to my neighbors."] }, "new": { "offset": 11, "lines": ["Say hello to my neighbors."] } } ], [ { "tag": "eq", "old": { "offset": 14, "lines": ["B"] }, "new": { "offset": 14, "lines": ["B"] } }, { "tag": "rep", "old": { "offset": 15, "lines": ["Donec rutrum."] }, "new": { "offset": 15, "lines": ["Donec rutrum test.", "There is a new inserted line."] } }, { "tag": "eq", "old": { "offset": 16, "lines": ["C"] }, "new": { "offset": 17, "lines": ["C"] } }, { "tag": "rep", "old": { "offset": 17, "lines": ["Sed dictum lorem ipsum."] }, "new": { "offset": 18, "lines": ["Sed dolor lorem ipsum hendrerit."] } }, { "tag": "eq", "old": { "offset": 18, "lines": [""] }, "new": { "offset": 19, "lines": [""] } } ] ] ```

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.

Click to expand ```json [ [ { "tag": "rep", "old": { "offset": 0, "lines": ["<p>Hello World! Good-looking.</p>"] }, "new": { "offset": 0, "lines": ["<div>Hello World! Bad-tempered.</div>"] } }, { "tag": "eq", "old": { "offset": 1, "lines": ["~~~~~~~~~~~~~~~~~~~"] }, "new": { "offset": 1, "lines": ["~~~~~~~~~~~~~~~~~~~"] } }, { "tag": "ins", "old": { "offset": 2, "lines": [""] }, "new": { "offset": 2, "lines": ["Let's add a new line here."] } }, { "tag": "eq", "old": { "offset": 2, "lines": ["X"] }, "new": { "offset": 3, "lines": ["X"] } } ], [ { "tag": "eq", "old": { "offset": 6, "lines": ["N"] }, "new": { "offset": 7, "lines": ["N"] } }, { "tag": "rep", "old": { "offset": 7, "lines": ["Do you know in Chinese, \"金槍魚罐頭\" means tuna can."] }, "new": { "offset": 8, "lines": ["Do you know in Japanese, \"魚の缶詰\" means fish can."] } }, { "tag": "eq", "old": { "offset": 8, "lines": ["\t \tTab visualization test.", "G"] }, "new": { "offset": 9, "lines": ["\t \tTab visualization test.", "G"] } }, { "tag": "del", "old": { "offset": 10, "lines": ["// remember to delete this line"] }, "new": { "offset": 11, "lines": [""] } }, { "tag": "eq", "old": { "offset": 11, "lines": ["Say hello to my neighbors."] }, "new": { "offset": 11, "lines": ["Say hello to my neighbors."] } } ], [ { "tag": "eq", "old": { "offset": 14, "lines": ["B"] }, "new": { "offset": 14, "lines": ["B"] } }, { "tag": "rep", "old": { "offset": 15, "lines": ["Donec rutrum."] }, "new": { "offset": 15, "lines": ["Donec rutrum test.", "There is a new inserted line."] } }, { "tag": "eq", "old": { "offset": 16, "lines": ["C"] }, "new": { "offset": 17, "lines": ["C"] } }, { "tag": "rep", "old": { "offset": 17, "lines": ["Sed dictum lorem ipsum."] }, "new": { "offset": 18, "lines": ["Sed dolor lorem ipsum hendrerit."] } }, { "tag": "eq", "old": { "offset": 18, "lines": [""] }, "new": { "offset": 19, "lines": [""] } } ] ] ```

Custom Language

Override an Existing Language

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

php $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.

Owner

  • Name: Jack Cherng
  • Login: jfcherng
  • Kind: user
  • Location: Hsinchu, Taiwan
  • Company: Realtek Semiconductor Corp.

The creator of @Fanhuaji (繁化姬). More of a Python dev now. Sublime Text power user. Sublime Text/Merge 交流群組: https://t.me/sublime_tw

GitHub Events

Total
  • Issues event: 1
  • Watch event: 30
  • Issue comment event: 1
  • Pull request event: 1
  • Fork event: 4
  • Create event: 1
Last Year
  • Issues event: 1
  • Watch event: 30
  • Issue comment event: 1
  • Pull request event: 1
  • Fork event: 4
  • Create event: 1

Committers

Last synced: 9 months ago

All Time
  • Total Commits: 689
  • Total Committers: 26
  • Avg Commits per committer: 26.5
  • Development Distribution Score (DDS): 0.054
Past Year
  • Commits: 0
  • Committers: 0
  • Avg Commits per committer: 0.0
  • Development Distribution Score (DDS): 0.0
Top Committers
Name Email Commits
Jack Cherng j****g@g****m 652
Chris Boulton c****n@i****m 4
Hasan Özbey 4****k 4
Petr Kotek p****k@b****m 3
Brian Teeman b****n@t****t 3
Niklas Keller c****e@g****m 2
Ondrej Mirtes o****j@m****z 2
Mark Rickerby m****y@i****m 1
Truoris t****s@f****r 1
A. Krumov b****0@g****m 1
Abdiel Carrazana a****s@g****m 1
Adecap 7****p 1
Andrea Righi a****7@g****m 1
Chris Boulton c****n@m****m 1
Dylan Garrett d****t@g****m 1
Hannes Diercks x****e@g****e 1
Josh Fraser j****z@g****m 1
Michaël m****f 1
Oleh w****1@g****m 1
Petr Havel p****l@g****m 1
Sidney Lins s****v@g****m 1
The Codacy Badger b****r@c****m 1
andreypaa a****a@g****m 1
flaushi f****i@g****t 1
jens.klose j****e@g****m 1
oppara o****a@o****v 1
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 9 months ago

All Time
  • Total issues: 56
  • Total pull requests: 28
  • Average time to close issues: about 1 month
  • Average time to close pull requests: about 22 hours
  • Total issue authors: 45
  • Total pull request authors: 16
  • Average comments per issue: 4.07
  • Average comments per pull request: 2.25
  • Merged pull requests: 24
  • Bot issues: 0
  • Bot pull requests: 1
Past Year
  • Issues: 4
  • Pull requests: 1
  • Average time to close issues: about 2 hours
  • Average time to close pull requests: N/A
  • Issue authors: 4
  • Pull request authors: 1
  • Average comments per issue: 1.5
  • Average comments per pull request: 0.0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 1
Top Authors
Issue Authors
  • jfcherng (6)
  • the-turk (3)
  • mansoorkhan96 (3)
  • void285 (2)
  • infuginindia (2)
  • etaunknown (1)
  • timint (1)
  • BlackbitDevs (1)
  • sdlins (1)
  • Wulfheart (1)
  • aaronpk (1)
  • gurgenhakobyan (1)
  • xitox97 (1)
  • artem-brezutsky (1)
  • novalex (1)
Pull Request Authors
  • jfcherng (7)
  • the-turk (4)
  • brianteeman (3)
  • ondrejmirtes (2)
  • anrighi (1)
  • phavel (1)
  • codacy-badger (1)
  • sdlins (1)
  • alejandrodecap (1)
  • miclf (1)
  • woren951 (1)
  • abdielcs (1)
  • flaushi (1)
  • gullevek (1)
  • oppara (1)
Top Labels
Issue Labels
question (20) enhancement (13) bug (11) coding style (3) need more info (2) invalid (2) wontfix (2) upstream (1) awaiting response (1) help wanted (1)
Pull Request Labels
translation (11) enhancement (9) bug (4) invalid (1) coding style (1)

Dependencies

composer.json packagist
  • friendsofphp/php-cs-fixer ^3.8 development
  • liip/rmt ^1.6 development
  • phan/phan ^5 development
  • phpunit/phpunit ^9 development
  • squizlabs/php_codesniffer ^3.6 development
  • jfcherng/php-color-output ^3
  • jfcherng/php-mb-string ^1.4.6
  • jfcherng/php-sequence-matcher ^3.2.8
  • php >=7.4
composer.lock packagist
  • composer/pcre 3.0.0 development
  • composer/semver 3.3.2 development
  • composer/xdebug-handler 3.0.3 development
  • doctrine/annotations 1.13.2 development
  • doctrine/instantiator 1.4.1 development
  • doctrine/lexer 1.2.3 development
  • felixfbecker/advanced-json-rpc v3.2.1 development
  • friendsofphp/php-cs-fixer v3.8.0 development
  • liip/rmt 1.7.0 development
  • microsoft/tolerant-php-parser v0.1.1 development
  • myclabs/deep-copy 1.11.0 development
  • netresearch/jsonmapper v4.0.0 development
  • nikic/php-parser v4.14.0 development
  • phan/phan 5.3.2 development
  • phar-io/manifest 2.0.3 development
  • phar-io/version 3.2.1 development
  • php-cs-fixer/diff v2.0.2 development
  • phpdocumentor/reflection-common 2.2.0 development
  • phpdocumentor/reflection-docblock 5.3.0 development
  • phpdocumentor/type-resolver 1.6.1 development
  • phpspec/prophecy v1.15.0 development
  • phpunit/php-code-coverage 9.2.15 development
  • phpunit/php-file-iterator 3.0.6 development
  • phpunit/php-invoker 3.1.1 development
  • phpunit/php-text-template 2.0.4 development
  • phpunit/php-timer 5.0.3 development
  • phpunit/phpunit 9.5.21 development
  • psr/cache 1.0.1 development
  • psr/container 1.1.2 development
  • psr/event-dispatcher 1.0.0 development
  • psr/log 1.1.4 development
  • sabre/event 5.1.4 development
  • sebastian/cli-parser 1.0.1 development
  • sebastian/code-unit 1.0.8 development
  • sebastian/code-unit-reverse-lookup 2.0.3 development
  • sebastian/comparator 4.0.6 development
  • sebastian/complexity 2.0.2 development
  • sebastian/diff 4.0.4 development
  • sebastian/environment 5.1.4 development
  • sebastian/exporter 4.0.4 development
  • sebastian/global-state 5.0.5 development
  • sebastian/lines-of-code 1.0.3 development
  • sebastian/object-enumerator 4.0.4 development
  • sebastian/object-reflector 2.0.4 development
  • sebastian/recursion-context 4.0.4 development
  • sebastian/resource-operations 3.0.3 development
  • sebastian/type 3.0.0 development
  • sebastian/version 3.0.2 development
  • squizlabs/php_codesniffer 3.7.1 development
  • symfony/console v5.4.9 development
  • symfony/deprecation-contracts v2.5.1 development
  • symfony/event-dispatcher v5.4.9 development
  • symfony/event-dispatcher-contracts v2.5.1 development
  • symfony/filesystem v5.4.9 development
  • symfony/finder v5.4.8 development
  • symfony/options-resolver v5.4.3 development
  • symfony/polyfill-ctype v1.26.0 development
  • symfony/polyfill-intl-grapheme v1.26.0 development
  • symfony/polyfill-intl-normalizer v1.26.0 development
  • symfony/polyfill-mbstring v1.26.0 development
  • symfony/polyfill-php73 v1.26.0 development
  • symfony/polyfill-php80 v1.26.0 development
  • symfony/polyfill-php81 v1.26.0 development
  • symfony/process v5.4.8 development
  • symfony/service-contracts v2.5.1 development
  • symfony/stopwatch v5.4.5 development
  • symfony/string v5.4.9 development
  • symfony/yaml v5.4.3 development
  • theseer/tokenizer 1.2.1 development
  • tysonandre/var_representation_polyfill 0.1.1 development
  • vierbergenlars/php-semver 3.0.2 development
  • webmozart/assert 1.11.0 development
  • jfcherng/php-color-output 3.0.0
  • jfcherng/php-mb-string 1.4.6
  • jfcherng/php-sequence-matcher 3.2.8