diff --git a/composer.json b/composer.json index a6b043bb..145c6136 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,25 @@ { - "name": "chrisboulton/php-diff", - "type": "library", + "name": "phpspec/php-diff", + "type": "library", "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", + "license": "BSD-3-Clause", + "authors": [ { "name": "Chris Boulton", - "email": "@chrisboulton" + "homepage": "http://github.com/chrisboulton" } ], - "autoload": { - "psr-0": { - "Diff": "lib/" - } - } -} \ No newline at end of file + + "autoload": { + "psr-0": { + "Diff": "lib/" + } + }, + + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/lib/Diff.php b/lib/Diff.php index 35305c03..d6cecb79 100644 --- a/lib/Diff.php +++ b/lib/Diff.php @@ -80,6 +80,7 @@ class Diff * * @param array $a Array containing the lines of the first string to compare. * @param array $b Array containing the lines for the second string to compare. + * @param array $options */ public function __construct($a, $b, $options=array()) { @@ -92,7 +93,7 @@ public function __construct($a, $b, $options=array()) /** * Render a diff using the supplied rendering class and return it. * - * @param object $renderer An instance of the rendering object to use for generating the diff. + * @param Diff_Renderer_Abstract $renderer An instance of the rendering object to use for generating the diff. * @return mixed The generated diff. Exact return value depends on the rendered. */ public function render(Diff_Renderer_Abstract $renderer) diff --git a/lib/Diff/Renderer/Html/Array.php b/lib/Diff/Renderer/Html/Array.php index 0b3f1d50..92bf128b 100644 --- a/lib/Diff/Renderer/Html/Array.php +++ b/lib/Diff/Renderer/Html/Array.php @@ -5,10 +5,10 @@ * PHP version 5 * * Copyright (c) 2009 Chris Boulton - * + * * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without + * + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, @@ -16,20 +16,20 @@ * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * - Neither the name of the Chris Boulton nor the names of its contributors - * may be used to endorse or promote products derived from this software + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package DiffLib @@ -82,12 +82,18 @@ public function render() list($start, $end) = $this->getChangeExtent($fromLine, $toLine); if($start != 0 || $end != 0) { - $last = $end + strlen($fromLine); - $fromLine = substr_replace($fromLine, "\0", $start, 0); - $fromLine = substr_replace($fromLine, "\1", $last + 1, 0); - $last = $end + strlen($toLine); - $toLine = substr_replace($toLine, "\0", $start, 0); - $toLine = substr_replace($toLine, "\1", $last + 1, 0); + $realEnd = mb_strlen($fromLine) + $end; + $fromLine = mb_substr($fromLine, 0, $start) + . "\0" + . mb_substr($fromLine, $start, $realEnd - $start) + . "\1" + . mb_substr($fromLine, $realEnd); + $realEnd = mb_strlen($toLine) + $end; + $toLine = mb_substr($toLine, 0, $start) + . "\0" + . mb_substr($toLine, $start, $realEnd - $start) + . "\1" + . mb_substr($toLine, $realEnd); $a[$i1 + $i] = $fromLine; $b[$j1 + $i] = $toLine; } @@ -149,13 +155,13 @@ public function render() private function getChangeExtent($fromLine, $toLine) { $start = 0; - $limit = min(strlen($fromLine), strlen($toLine)); - while($start < $limit && $fromLine{$start} == $toLine{$start}) { + $limit = min(mb_strlen($fromLine), mb_strlen($toLine)); + while($start < $limit && mb_substr($fromLine, $start, 1) == mb_substr($toLine, $start, 1)) { ++$start; } $end = -1; $limit = $limit - $start; - while(-$end <= $limit && substr($fromLine, $end, 1) == substr($toLine, $end, 1)) { + while(-$end <= $limit && mb_substr($fromLine, $end, 1) == mb_substr($toLine, $end, 1)) { --$end; } return array( @@ -174,10 +180,12 @@ private function getChangeExtent($fromLine, $toLine) */ private function formatLines($lines) { - $lines = array_map(array($this, 'ExpandTabs'), $lines); + if ($this->options['tabSize'] !== false) { + $lines = array_map(array($this, 'ExpandTabs'), $lines); + } $lines = array_map(array($this, 'HtmlSafe'), $lines); foreach($lines as &$line) { - $line = preg_replace('# ( +)|^ #e', "\$this->fixSpaces('\\1')", $line); + $line = preg_replace_callback('# ( +)|^ #', __CLASS__."::fixSpaces", $line); } return $lines; } @@ -185,11 +193,12 @@ private function formatLines($lines) /** * Replace a string containing spaces with a HTML representation using  . * - * @param string $spaces The string of spaces. + * @param string $matches Regex matches array. * @return string The HTML representation of the string. */ - function fixSpaces($spaces='') + public static function fixSpaces($matches) { + $spaces = isset($matches[1]) ? $matches[1] : ''; $count = strlen($spaces); if($count == 0) { return ''; @@ -221,4 +230,4 @@ private function htmlSafe($string) { return htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8'); } -} \ No newline at end of file +} diff --git a/lib/Diff/Renderer/Html/Inline.php b/lib/Diff/Renderer/Html/Inline.php index 60e8005a..70cc9041 100644 --- a/lib/Diff/Renderer/Html/Inline.php +++ b/lib/Diff/Renderer/Html/Inline.php @@ -128,8 +128,8 @@ public function render() foreach($change['changed']['lines'] as $no => $line) { $toLine = $change['changed']['offset'] + $no + 1; $html .= ''; - $html .= ''.$toLine.''; $html .= ' '; + $html .= ''.$toLine.''; $html .= ''.$line.''; $html .= ''; } diff --git a/lib/Diff/Renderer/Html/SideBySide.php b/lib/Diff/Renderer/Html/SideBySide.php index 307af1c3..eb438483 100644 --- a/lib/Diff/Renderer/Html/SideBySide.php +++ b/lib/Diff/Renderer/Html/SideBySide.php @@ -83,9 +83,9 @@ public function render() $toLine = $change['changed']['offset'] + $no + 1; $html .= ''; $html .= ''.$fromLine.''; - $html .= ''.$line.' '; + $html .= ''.$line.' '; $html .= ''.$toLine.''; - $html .= ''.$line.' '; + $html .= ''.$line.' '; $html .= ''; } } diff --git a/lib/Diff/Renderer/Text/Context.php b/lib/Diff/Renderer/Text/Context.php index 1200b01c..241b965d 100644 --- a/lib/Diff/Renderer/Text/Context.php +++ b/lib/Diff/Renderer/Text/Context.php @@ -72,17 +72,17 @@ public function render() $j2 = $group[$lastItem][4]; if($i2 - $i1 >= 2) { - $diff .= '*** '.($group[0][1] + 1).','.$i2." ****\n"; + $diff .= '*** '.($group[0][1] + 1).','.$i2." ****".PHP_EOL; } else { $diff .= '*** '.$i2." ****\n"; } if($j2 - $j1 >= 2) { - $separator = '--- '.($j1 + 1).','.$j2." ----\n"; + $separator = '--- '.($j1 + 1).','.$j2." ----".PHP_EOL; } else { - $separator = '--- '.$j2." ----\n"; + $separator = '--- '.$j2." ----".PHP_EOL; } $hasVisible = false; @@ -99,7 +99,7 @@ public function render() if($tag == 'insert') { continue; } - $diff .= $this->tagMap[$tag].' '.implode("\n".$this->tagMap[$tag].' ', $this->diff->GetA($i1, $i2))."\n"; + $diff .= $this->tagMap[$tag].' '.implode(PHP_EOL.$this->tagMap[$tag].' ', $this->diff->GetA($i1, $i2)).PHP_EOL; } } @@ -119,7 +119,7 @@ public function render() if($tag == 'delete') { continue; } - $diff .= $this->tagMap[$tag].' '.implode("\n".$this->tagMap[$tag].' ', $this->diff->GetB($j1, $j2))."\n"; + $diff .= $this->tagMap[$tag].' '.implode(PHP_EOL.$this->tagMap[$tag].' ', $this->diff->GetB($j1, $j2)).PHP_EOL; } } } diff --git a/lib/Diff/Renderer/Text/Unified.php b/lib/Diff/Renderer/Text/Unified.php index e94d951d..084611c2 100644 --- a/lib/Diff/Renderer/Text/Unified.php +++ b/lib/Diff/Renderer/Text/Unified.php @@ -65,19 +65,19 @@ public function render() $i2 = -1; } - $diff .= '@@ -'.($i1 + 1).','.($i2 - $i1).' +'.($j1 + 1).','.($j2 - $j1)." @@\n"; + $diff .= '@@ -'.($i1 + 1).','.($i2 - $i1).' +'.($j1 + 1).','.($j2 - $j1)." @@".PHP_EOL; foreach($group as $code) { list($tag, $i1, $i2, $j1, $j2) = $code; if($tag == 'equal') { - $diff .= ' '.implode("\n ", $this->diff->GetA($i1, $i2))."\n"; + $diff .= ' '.implode(PHP_EOL." ", $this->diff->GetA($i1, $i2)).PHP_EOL; } else { if($tag == 'replace' || $tag == 'delete') { - $diff .= '-'.implode("\n-", $this->diff->GetA($i1, $i2))."\n"; + $diff .= '-'.implode(PHP_EOL."-", $this->diff->GetA($i1, $i2)).PHP_EOL; } if($tag == 'replace' || $tag == 'insert') { - $diff .= '+'.implode("\n+", $this->diff->GetB($j1, $j2))."\n"; + $diff .= '+'.implode(PHP_EOL."+", $this->diff->GetB($j1, $j2)).PHP_EOL; } } } diff --git a/lib/Diff/SequenceMatcher.php b/lib/Diff/SequenceMatcher.php index e819e810..71c9c630 100644 --- a/lib/Diff/SequenceMatcher.php +++ b/lib/Diff/SequenceMatcher.php @@ -69,6 +69,10 @@ class Diff_SequenceMatcher private $options = array(); + private $matchingBlocks = null; + private $opCodes = null; + private $fullBCount = null; + private $defaultOptions = array( 'ignoreNewLines' => false, 'ignoreWhitespace' => false, @@ -83,8 +87,9 @@ class Diff_SequenceMatcher * @param string|array $a A string or array containing the lines to compare against. * @param string|array $b A string or array containing the lines to compare. * @param string|array $junkCallback Either an array or string that references a callback function (if there is one) to determine 'junk' characters. + * @param array $options */ - public function __construct($a, $b, $junkCallback=null, $options) + public function __construct($a, $b, $junkCallback=null, $options=[]) { $this->a = null; $this->b = null; @@ -93,6 +98,11 @@ public function __construct($a, $b, $junkCallback=null, $options) $this->setSequences($a, $b); } + /** + * Set new options + * + * @param array $options + */ public function setOptions($options) { $this->options = array_merge($this->defaultOptions, $options); @@ -206,12 +216,12 @@ private function chainB() /** * Checks if a particular character is in the junk dictionary * for the list of junk characters. - * - * @return boolean $b True if the character is considered junk. False if not. + * @param $b + * @return boolean True if the character is considered junk. False if not. */ private function isBJunk($b) { - if(isset($this->juncDict[$b])) { + if(isset($this->junkDict[$b])) { return true; } @@ -252,7 +262,7 @@ public function findLongestMatch($alo, $ahi, $blo, $bhi) for($i = $alo; $i < $ahi; ++$i) { $newJ2Len = array(); $jDict = $this->arrayGetDefault($this->b2j, $a[$i], $nothing); - foreach($jDict as $jKey => $j) { + foreach($jDict as $j) { if($j < $blo) { continue; } @@ -285,7 +295,7 @@ public function findLongestMatch($alo, $ahi, $blo, $bhi) } while($bestI > $alo && $bestJ > $blo && $this->isBJunk($b[$bestJ - 1]) && - !$this->isLineDifferent($bestI - 1, $bestJ - 1)) { + !$this->linesAreDifferent($bestI - 1, $bestJ - 1)) { --$bestI; --$bestJ; ++$bestSize; @@ -631,7 +641,7 @@ private function quickRatio() { if($this->fullBCount === null) { $this->fullBCount = array(); - $bLength = count ($b); + $bLength = count ($this->b); for($i = 0; $i < $bLength; ++$i) { $char = $this->b[$i]; $this->fullBCount[$char] = $this->arrayGetDefault($this->fullBCount, $char, 0) + 1; @@ -729,7 +739,7 @@ private function tupleSort($a, $b) } } - if(count($a) == $count($b)) { + if(count($a) == count($b)) { return 0; } else if(count($a) < count($b)) { @@ -739,4 +749,4 @@ private function tupleSort($a, $b) return 1; } } -} \ No newline at end of file +}