diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..a30621a4
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+indent_size = 4
+indent_style = space
diff --git a/lib/Autoloader.php b/lib/Autoloader.php
index 284f5048..1cc14011 100644
--- a/lib/Autoloader.php
+++ b/lib/Autoloader.php
@@ -8,24 +8,24 @@
class Autoloader
{
- /**
- * Autoloader constructor.
- */
- public function __construct()
- {
- spl_autoload_register(array($this, '__autoload'));
- }
+ /**
+ * Autoloader constructor.
+ */
+ public function __construct()
+ {
+ spl_autoload_register(array($this, '__autoload'));
+ }
- /**
- * @param string $class
- */
- private function __autoload($class)
- {
- $class = str_replace('\\', '/', $class); // revert path for old PHP on Linux
- $dir = str_replace('\\', '/', __DIR__);
- if (file_exists($dir . '/' . $class . '.php')) {
- /** @noinspection PhpIncludeInspection */
- require_once $dir . '/' . $class . '.php';
- }
- }
+ /**
+ * @param string $class
+ */
+ private function __autoload($class)
+ {
+ $class = str_replace('\\', '/', $class); // revert path for old PHP on Linux
+ $dir = str_replace('\\', '/', __DIR__);
+ if (file_exists($dir . '/' . $class . '.php')) {
+ /** @noinspection PhpIncludeInspection */
+ require_once $dir . '/' . $class . '.php';
+ }
+ }
}
diff --git a/lib/jblond/Diff.php b/lib/jblond/Diff.php
index c2d43679..e9a8747e 100644
--- a/lib/jblond/Diff.php
+++ b/lib/jblond/Diff.php
@@ -49,135 +49,135 @@
*/
class Diff
{
- /**
- * @var array The "old" sequence to use as the basis for the comparison.
- */
- private $a = null;
-
- /**
- * @var array The "new" sequence to generate the changes for.
- */
- private $b = null;
-
- /**
- * @var array Array containing the generated op codes for the differences between the two items.
- */
- private $groupedCodes = null;
-
- /**
- * @var array Associative array of the default options available for the diff class and their default value.
- */
- private $defaultOptions = array(
- 'context' => 3,
- 'ignoreNewLines' => false,
- 'ignoreWhitespace' => false,
- 'ignoreCase' => false,
- 'labelDifferences'=>'Differences'
- );
-
- /**
- * @var array Array of the options that have been applied for generating the diff.
- */
- public $options = array();
-
- /**
- * The constructor.
- *
- * @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 Array for the options
- */
- public function __construct($a, $b, $options = array())
- {
- $this->a = $a;
- $this->b = $b;
-
- if (is_array($options)) {
- $this->options = array_merge($this->defaultOptions, $options);
- } else {
- $this->options = $this->defaultOptions;
- }
- }
-
-
- /**
- * Render a diff using the supplied rendering class and return it.
- *
- * @param object $renderer object $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($renderer)
- {
- $renderer->diff = $this;
- return $renderer->render();
- }
-
- /**
- * Get a range of lines from $start to $end from the first comparison string
- * and return them as an array. If no values are supplied, the entire string
- * is returned. It's also possible to specify just one line to return only
- * that line.
- *
- * @param int $start The starting number.
- * @param int $end The ending number. If not supplied, only the item in $start will be returned.
- * @return array Array of all of the lines between the specified range.
- */
- public function getA($start = 0, $end = null) : array
- {
- if ($start == 0 && $end === null) {
- return $this->a;
- }
-
- if ($end === null) {
- $length = 1;
- } else {
- $length = $end - $start;
- }
-
- return array_slice($this->a, $start, $length);
- }
-
- /**
- * Get a range of lines from $start to $end from the second comparison string
- * and return them as an array. If no values are supplied, the entire string
- * is returned. It's also possible to specify just one line to return only
- * that line.
- *
- * @param int $start The starting number.
- * @param int $end The ending number. If not supplied, only the item in $start will be returned.
- * @return array Array of all of the lines between the specified range.
- */
- public function getB($start = 0, $end = null) : array
- {
- if ($start == 0 && $end === null) {
- return $this->b;
- }
-
- if ($end === null) {
- $length = 1;
- } else {
- $length = $end - $start;
- }
-
- return array_slice($this->b, $start, $length);
- }
-
- /**
- * Generate a list of the compiled and grouped op codes for the differences between the
- * two strings. Generally called by the renderer, this class instantiates the sequence
- * matcher and performs the actual diff generation and return an array of the op codes
- * for it. Once generated, the results are cached in the diff class instance.
- *
- * @return array Array of the grouped op codes for the generated diff.
- */
- public function getGroupedOpcodes() : array
- {
- if (!is_null($this->groupedCodes)) {
- return $this->groupedCodes;
- }
-
- $sequenceMatcher = new SequenceMatcher($this->a, $this->b, $this->options, null);
- $this->groupedCodes = $sequenceMatcher->getGroupedOpcodes($this->options['context']);
- return $this->groupedCodes;
- }
+ /**
+ * @var array The "old" sequence to use as the basis for the comparison.
+ */
+ private $a = null;
+
+ /**
+ * @var array The "new" sequence to generate the changes for.
+ */
+ private $b = null;
+
+ /**
+ * @var array Array containing the generated op codes for the differences between the two items.
+ */
+ private $groupedCodes = null;
+
+ /**
+ * @var array Associative array of the default options available for the diff class and their default value.
+ */
+ private $defaultOptions = array(
+ 'context' => 3,
+ 'ignoreNewLines' => false,
+ 'ignoreWhitespace' => false,
+ 'ignoreCase' => false,
+ 'labelDifferences'=>'Differences'
+ );
+
+ /**
+ * @var array Array of the options that have been applied for generating the diff.
+ */
+ public $options = array();
+
+ /**
+ * The constructor.
+ *
+ * @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 Array for the options
+ */
+ public function __construct($a, $b, $options = array())
+ {
+ $this->a = $a;
+ $this->b = $b;
+
+ if (is_array($options)) {
+ $this->options = array_merge($this->defaultOptions, $options);
+ } else {
+ $this->options = $this->defaultOptions;
+ }
+ }
+
+
+ /**
+ * Render a diff using the supplied rendering class and return it.
+ *
+ * @param object $renderer object $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($renderer)
+ {
+ $renderer->diff = $this;
+ return $renderer->render();
+ }
+
+ /**
+ * Get a range of lines from $start to $end from the first comparison string
+ * and return them as an array. If no values are supplied, the entire string
+ * is returned. It's also possible to specify just one line to return only
+ * that line.
+ *
+ * @param int $start The starting number.
+ * @param int $end The ending number. If not supplied, only the item in $start will be returned.
+ * @return array Array of all of the lines between the specified range.
+ */
+ public function getA($start = 0, $end = null) : array
+ {
+ if ($start == 0 && $end === null) {
+ return $this->a;
+ }
+
+ if ($end === null) {
+ $length = 1;
+ } else {
+ $length = $end - $start;
+ }
+
+ return array_slice($this->a, $start, $length);
+ }
+
+ /**
+ * Get a range of lines from $start to $end from the second comparison string
+ * and return them as an array. If no values are supplied, the entire string
+ * is returned. It's also possible to specify just one line to return only
+ * that line.
+ *
+ * @param int $start The starting number.
+ * @param int $end The ending number. If not supplied, only the item in $start will be returned.
+ * @return array Array of all of the lines between the specified range.
+ */
+ public function getB($start = 0, $end = null) : array
+ {
+ if ($start == 0 && $end === null) {
+ return $this->b;
+ }
+
+ if ($end === null) {
+ $length = 1;
+ } else {
+ $length = $end - $start;
+ }
+
+ return array_slice($this->b, $start, $length);
+ }
+
+ /**
+ * Generate a list of the compiled and grouped op codes for the differences between the
+ * two strings. Generally called by the renderer, this class instantiates the sequence
+ * matcher and performs the actual diff generation and return an array of the op codes
+ * for it. Once generated, the results are cached in the diff class instance.
+ *
+ * @return array Array of the grouped op codes for the generated diff.
+ */
+ public function getGroupedOpcodes() : array
+ {
+ if (!is_null($this->groupedCodes)) {
+ return $this->groupedCodes;
+ }
+
+ $sequenceMatcher = new SequenceMatcher($this->a, $this->b, $this->options, null);
+ $this->groupedCodes = $sequenceMatcher->getGroupedOpcodes($this->options['context']);
+ return $this->groupedCodes;
+ }
}
diff --git a/lib/jblond/Diff/Renderer/Html/HtmlArray.php b/lib/jblond/Diff/Renderer/Html/HtmlArray.php
index b788c24e..cce0d95f 100644
--- a/lib/jblond/Diff/Renderer/Html/HtmlArray.php
+++ b/lib/jblond/Diff/Renderer/Html/HtmlArray.php
@@ -50,272 +50,272 @@
*/
class HtmlArray extends RendererAbstract
{
- /**
- * @var array Array of the default options that apply to this renderer.
- */
- protected $defaultOptions = array(
- 'tabSize' => 4,
+ /**
+ * @var array Array of the default options that apply to this renderer.
+ */
+ protected $defaultOptions = array(
+ 'tabSize' => 4,
'title_a' => 'Old Version',
'title_b' => 'New Version',
- );
+ );
- /**
- * From https://gist.github.com/stemar/8287074
- * @param mixed $string The input string.
- * @param mixed $replacement The replacement string.
- * @param mixed $start If start is positive, the replacing will begin at the start'th offset into string.
- * If start is negative, the replacing will begin at the start'th character from the end of string.
- * @param mixed $length If given and is positive, it represents the length of the portion of string which is to
- * be replaced. If it is negative, it represents the number of characters from the end of string at which to
- * stop replacing. If it is not given, then it will default to strlen( string ); i.e. end the replacing at the
- * end of string. Of course, if length is zero then this function will have the effect of inserting replacement
- * into string at the given start offset.
- * @return string|array The result string is returned. If string is an array then array is returned.
- */
- public function mbSubstrReplace($string, $replacement, $start, $length = null)
- {
- if (is_array($string)) {
- $num = count($string);
- // $replacement
- if (is_array($replacement)) {
- $replacement = array_slice($replacement, 0, $num);
- } else {
- $replacement = array_pad(array($replacement), $num, $replacement);
- }
+ /**
+ * From https://gist.github.com/stemar/8287074
+ * @param mixed $string The input string.
+ * @param mixed $replacement The replacement string.
+ * @param mixed $start If start is positive, the replacing will begin at the start'th offset into string.
+ * If start is negative, the replacing will begin at the start'th character from the end of string.
+ * @param mixed $length If given and is positive, it represents the length of the portion of string which is to
+ * be replaced. If it is negative, it represents the number of characters from the end of string at which to
+ * stop replacing. If it is not given, then it will default to strlen( string ); i.e. end the replacing at the
+ * end of string. Of course, if length is zero then this function will have the effect of inserting replacement
+ * into string at the given start offset.
+ * @return string|array The result string is returned. If string is an array then array is returned.
+ */
+ public function mbSubstrReplace($string, $replacement, $start, $length = null)
+ {
+ if (is_array($string)) {
+ $num = count($string);
+ // $replacement
+ if (is_array($replacement)) {
+ $replacement = array_slice($replacement, 0, $num);
+ } else {
+ $replacement = array_pad(array($replacement), $num, $replacement);
+ }
- // $start
- if (is_array($start)) {
- $start = array_slice($start, 0, $num);
- foreach ($start as $key => $value) {
- $start[$key] = is_int($value) ? $value : 0;
- }
- } else {
- $start = array_pad(array($start), $num, $start);
- }
- // $length
- if (!isset($length)) {
- $length = array_fill(0, $num, 0);
- } elseif (is_array($length)) {
- $length = array_slice($length, 0, $num);
- foreach ($length as $key => $value) {
- $length[$key] = isset($value) ? (is_int($value) ? $value : $num) : 0;
- }
- } else {
- $length = array_pad(array($length), $num, $length);
- }
- // Recursive call
- return array_map(array($this, 'mbSubstrReplace'), $string, $replacement, $start, $length);
- }
- preg_match_all('/./us', (string)$string, $smatches);
- preg_match_all('/./us', (string)$replacement, $rmatches);
- if ($length === null) {
- $length = mb_strlen($string);
- }
- array_splice($smatches['0'], $start, $length, $rmatches[0]);
- return join($smatches['0']);
- }
+ // $start
+ if (is_array($start)) {
+ $start = array_slice($start, 0, $num);
+ foreach ($start as $key => $value) {
+ $start[$key] = is_int($value) ? $value : 0;
+ }
+ } else {
+ $start = array_pad(array($start), $num, $start);
+ }
+ // $length
+ if (!isset($length)) {
+ $length = array_fill(0, $num, 0);
+ } elseif (is_array($length)) {
+ $length = array_slice($length, 0, $num);
+ foreach ($length as $key => $value) {
+ $length[$key] = isset($value) ? (is_int($value) ? $value : $num) : 0;
+ }
+ } else {
+ $length = array_pad(array($length), $num, $length);
+ }
+ // Recursive call
+ return array_map(array($this, 'mbSubstrReplace'), $string, $replacement, $start, $length);
+ }
+ preg_match_all('/./us', (string)$string, $smatches);
+ preg_match_all('/./us', (string)$replacement, $rmatches);
+ if ($length === null) {
+ $length = mb_strlen($string);
+ }
+ array_splice($smatches['0'], $start, $length, $rmatches[0]);
+ return join($smatches['0']);
+ }
- /**
- * Render and return an array structure suitable for generating HTML
- * based differences. Generally called by subclasses that generate a
- * HTML based diff and return an array of the changes to show in the diff.
- *
- * @return array|string An array of the generated changes, suitable for presentation in HTML.
- */
- public function render()
- {
- // As we'll be modifying a & b to include our change markers,
- // we need to get the contents and store them here. That way
- // we're not going to destroy the original data
- $a = $this->diff->getA();
- $b = $this->diff->getB();
+ /**
+ * Render and return an array structure suitable for generating HTML
+ * based differences. Generally called by subclasses that generate a
+ * HTML based diff and return an array of the changes to show in the diff.
+ *
+ * @return array|string An array of the generated changes, suitable for presentation in HTML.
+ */
+ public function render()
+ {
+ // As we'll be modifying a & b to include our change markers,
+ // we need to get the contents and store them here. That way
+ // we're not going to destroy the original data
+ $a = $this->diff->getA();
+ $b = $this->diff->getB();
- $changes = array();
- $opCodes = $this->diff->getGroupedOpcodes();
- foreach ($opCodes as $group) {
- $blocks = array();
- $lastTag = null;
- $lastBlock = 0;
- foreach ($group as $code) {
- list($tag, $i1, $i2, $j1, $j2) = $code;
+ $changes = array();
+ $opCodes = $this->diff->getGroupedOpcodes();
+ foreach ($opCodes as $group) {
+ $blocks = array();
+ $lastTag = null;
+ $lastBlock = 0;
+ foreach ($group as $code) {
+ list($tag, $i1, $i2, $j1, $j2) = $code;
- if ($tag == 'replace' && $i2 - $i1 == $j2 - $j1) {
- for ($i = 0; $i < ($i2 - $i1); ++$i) {
- $fromLine = $a[$i1 + $i];
- $toLine = $b[$j1 + $i];
+ if ($tag == 'replace' && $i2 - $i1 == $j2 - $j1) {
+ for ($i = 0; $i < ($i2 - $i1); ++$i) {
+ $fromLine = $a[$i1 + $i];
+ $toLine = $b[$j1 + $i];
- list($start, $end) = $this->getChangeExtent($fromLine, $toLine);
- if ($start != 0 || $end != 0) {
- $realEnd = mb_strlen($fromLine) + $end;
+ list($start, $end) = $this->getChangeExtent($fromLine, $toLine);
+ if ($start != 0 || $end != 0) {
+ $realEnd = mb_strlen($fromLine) + $end;
- $fromLine = mb_substr($fromLine, 0, $start) . "\0" .
- mb_substr($fromLine, $start, $realEnd - $start) . "\1" . mb_substr($fromLine, $realEnd);
+ $fromLine = mb_substr($fromLine, 0, $start) . "\0" .
+ mb_substr($fromLine, $start, $realEnd - $start) . "\1" . mb_substr($fromLine, $realEnd);
- $realEnd = mb_strlen($toLine) + $end;
+ $realEnd = mb_strlen($toLine) + $end;
- $toLine = mb_substr($toLine, 0, $start) .
- "\0" . mb_substr($toLine, $start, $realEnd - $start) . "\1" .
- mb_substr($toLine, $realEnd);
+ $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;
- }
- }
- }
+ $a[$i1 + $i] = $fromLine;
+ $b[$j1 + $i] = $toLine;
+ }
+ }
+ }
- if ($tag != $lastTag) {
- $blocks[] = $this->getDefaultArray($tag, $i1, $j1);
- $lastBlock = count($blocks)-1;
- }
+ if ($tag != $lastTag) {
+ $blocks[] = $this->getDefaultArray($tag, $i1, $j1);
+ $lastBlock = count($blocks)-1;
+ }
- $lastTag = $tag;
+ $lastTag = $tag;
- if ($tag == 'equal') {
- $lines = array_slice($a, $i1, ($i2 - $i1));
- $blocks[$lastBlock]['base']['lines'] += $this->formatLines($lines);
- $lines = array_slice($b, $j1, ($j2 - $j1));
- $blocks[$lastBlock]['changed']['lines'] += $this->formatLines($lines);
- } else {
- if ($tag == 'replace' || $tag == 'delete') {
- $lines = array_slice($a, $i1, ($i2 - $i1));
- $lines = $this->formatLines($lines);
- $lines = str_replace(array("\0", "\1"), array('', ''), $lines);
- $blocks[$lastBlock]['base']['lines'] += $lines;
- }
+ if ($tag == 'equal') {
+ $lines = array_slice($a, $i1, ($i2 - $i1));
+ $blocks[$lastBlock]['base']['lines'] += $this->formatLines($lines);
+ $lines = array_slice($b, $j1, ($j2 - $j1));
+ $blocks[$lastBlock]['changed']['lines'] += $this->formatLines($lines);
+ } else {
+ if ($tag == 'replace' || $tag == 'delete') {
+ $lines = array_slice($a, $i1, ($i2 - $i1));
+ $lines = $this->formatLines($lines);
+ $lines = str_replace(array("\0", "\1"), array('', ''), $lines);
+ $blocks[$lastBlock]['base']['lines'] += $lines;
+ }
- if ($tag == 'replace' || $tag == 'insert') {
- $lines = array_slice($b, $j1, ($j2 - $j1));
- $lines = $this->formatLines($lines);
- $lines = str_replace(array("\0", "\1"), array('', ''), $lines);
- $blocks[$lastBlock]['changed']['lines'] += $lines;
- }
- }
- }
- $changes[] = $blocks;
- }
- return $changes;
- }
+ if ($tag == 'replace' || $tag == 'insert') {
+ $lines = array_slice($b, $j1, ($j2 - $j1));
+ $lines = $this->formatLines($lines);
+ $lines = str_replace(array("\0", "\1"), array('', ''), $lines);
+ $blocks[$lastBlock]['changed']['lines'] += $lines;
+ }
+ }
+ }
+ $changes[] = $blocks;
+ }
+ return $changes;
+ }
- /**
- * Given two strings, determine where the changes in the two strings
- * begin, and where the changes in the two strings end.
- *
- * @param string $fromLine The first string.
- * @param string $toLine The second string.
- * @return array Array containing the starting position (0 by default) and the ending position (-1 by default)
- */
- private function getChangeExtent($fromLine, $toLine)
- {
- $start = 0;
- $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 && mb_substr($fromLine, $end, 1) == mb_substr($toLine, $end, 1)) {
- --$end;
- }
- return array(
- $start,
- $end + 1
- );
- }
+ /**
+ * Given two strings, determine where the changes in the two strings
+ * begin, and where the changes in the two strings end.
+ *
+ * @param string $fromLine The first string.
+ * @param string $toLine The second string.
+ * @return array Array containing the starting position (0 by default) and the ending position (-1 by default)
+ */
+ private function getChangeExtent($fromLine, $toLine)
+ {
+ $start = 0;
+ $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 && mb_substr($fromLine, $end, 1) == mb_substr($toLine, $end, 1)) {
+ --$end;
+ }
+ return array(
+ $start,
+ $end + 1
+ );
+ }
- /**
- * Format a series of lines suitable for output in a HTML rendered diff.
- * This involves replacing tab characters with spaces, making the HTML safe
- * for output, ensuring that double spaces are replaced with etc.
- *
- * @param array $lines Array of lines to format.
- * @return array Array of the formatted lines.
- */
- protected function formatLines($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_callback('# ( +)|^ #', array($this, 'fixSpaces'), $line);
- }
- return $lines;
- }
+ /**
+ * Format a series of lines suitable for output in a HTML rendered diff.
+ * This involves replacing tab characters with spaces, making the HTML safe
+ * for output, ensuring that double spaces are replaced with etc.
+ *
+ * @param array $lines Array of lines to format.
+ * @return array Array of the formatted lines.
+ */
+ protected function formatLines($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_callback('# ( +)|^ #', array($this, 'fixSpaces'), $line);
+ }
+ return $lines;
+ }
- /**
- * Replace a string containing spaces with a HTML representation using .
- *
- * @param array $matches The string of spaces.
- * @return string The HTML representation of the string.
- */
- protected function fixSpaces($matches)
- {
- $buffer = '';
- $count = 0;
- foreach ($matches as $spaces) {
- $count = strlen($spaces);
- if ($count == 0) {
- continue;
- }
- $div = (int) ($count / 2);
- $mod = $count % 2;
- $buffer .= str_repeat(' ', $div).str_repeat(' ', $mod);
- }
+ /**
+ * Replace a string containing spaces with a HTML representation using .
+ *
+ * @param array $matches The string of spaces.
+ * @return string The HTML representation of the string.
+ */
+ protected function fixSpaces($matches)
+ {
+ $buffer = '';
+ $count = 0;
+ foreach ($matches as $spaces) {
+ $count = strlen($spaces);
+ if ($count == 0) {
+ continue;
+ }
+ $div = (int) ($count / 2);
+ $mod = $count % 2;
+ $buffer .= str_repeat(' ', $div).str_repeat(' ', $mod);
+ }
- $div = (int) ($count / 2);
- $mod = $count % 2;
- return str_repeat(' ', $div).str_repeat(' ', $mod);
- }
+ $div = (int) ($count / 2);
+ $mod = $count % 2;
+ return str_repeat(' ', $div).str_repeat(' ', $mod);
+ }
- /**
- * Replace tabs in a single line with a number of spaces as defined by the tabSize option.
- *
- * @param string $line The containing tabs to convert.
- * @return string The line with the tabs converted to spaces.
- */
- private function expandTabs($line)
- {
- $tabSize = $this->options['tabSize'];
- while (($pos = strpos($line, "\t")) !== false) {
- $left = substr($line, 0, $pos);
- $right = substr($line, $pos + 1);
- $length = $tabSize - ($pos % $tabSize);
- $spaces = str_repeat(' ', $length);
- $line = $left . $spaces . $right;
- }
- return $line;
- }
+ /**
+ * Replace tabs in a single line with a number of spaces as defined by the tabSize option.
+ *
+ * @param string $line The containing tabs to convert.
+ * @return string The line with the tabs converted to spaces.
+ */
+ private function expandTabs($line)
+ {
+ $tabSize = $this->options['tabSize'];
+ while (($pos = strpos($line, "\t")) !== false) {
+ $left = substr($line, 0, $pos);
+ $right = substr($line, $pos + 1);
+ $length = $tabSize - ($pos % $tabSize);
+ $spaces = str_repeat(' ', $length);
+ $line = $left . $spaces . $right;
+ }
+ return $line;
+ }
- /**
- * Make a string containing HTML safe for output on a page.
- *
- * @param string $string The string.
- * @return string The string with the HTML characters replaced by entities.
- */
- private function htmlSafe($string)
- {
- return htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8');
- }
+ /**
+ * Make a string containing HTML safe for output on a page.
+ *
+ * @param string $string The string.
+ * @return string The string with the HTML characters replaced by entities.
+ */
+ private function htmlSafe($string)
+ {
+ return htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8');
+ }
- /**
- * @param string $tag
- * @param integer $i1
- * @param integer $j1
- * @return array
- */
- private function getDefaultArray($tag, $i1, $j1)
- {
- return array
- (
- 'tag' => $tag,
- 'base' => array(
- 'offset' => $i1,
- 'lines' => array()
- ),
- 'changed' => array(
- 'offset' => $j1,
- 'lines' => array()
- )
- );
- }
+ /**
+ * @param string $tag
+ * @param integer $i1
+ * @param integer $j1
+ * @return array
+ */
+ private function getDefaultArray($tag, $i1, $j1)
+ {
+ return array
+ (
+ 'tag' => $tag,
+ 'base' => array(
+ 'offset' => $i1,
+ 'lines' => array()
+ ),
+ 'changed' => array(
+ 'offset' => $j1,
+ 'lines' => array()
+ )
+ );
+ }
}
diff --git a/lib/jblond/Diff/Renderer/Html/Inline.php b/lib/jblond/Diff/Renderer/Html/Inline.php
index c3f7835c..a46f25d4 100644
--- a/lib/jblond/Diff/Renderer/Html/Inline.php
+++ b/lib/jblond/Diff/Renderer/Html/Inline.php
@@ -48,181 +48,181 @@
*/
class Inline extends HtmlArray
{
- /**
- * Render a and return diff with changes between the two sequences
- * displayed inline (under each other)
- *
- * @return string The generated inline diff.
- */
- public function render() : string
- {
- $changes = parent::render();
- $html = '';
- if (empty($changes)) {
- return $html;
- }
+ /**
+ * Render a and return diff with changes between the two sequences
+ * displayed inline (under each other)
+ *
+ * @return string The generated inline diff.
+ */
+ public function render() : string
+ {
+ $changes = parent::render();
+ $html = '';
+ if (empty($changes)) {
+ return $html;
+ }
- $html .= $this->generateTableHeader();
+ $html .= $this->generateTableHeader();
- foreach ($changes as $i => $blocks) {
- // If this is a separate block, we're condensing code so output ...,
- // indicating a significant portion of the code has been collapsed as
- // it is the same
- if ($i > 0) {
- $html .= $this->generateSkippedTable();
- }
+ foreach ($changes as $i => $blocks) {
+ // If this is a separate block, we're condensing code so output ...,
+ // indicating a significant portion of the code has been collapsed as
+ // it is the same
+ if ($i > 0) {
+ $html .= $this->generateSkippedTable();
+ }
- foreach ($blocks as $change) {
- $html .= '
';
- switch ($change['tag']) {
- // Equal changes should be shown on both sides of the diff
- case 'equal':
- $html .= $this->generateTableRowsEqual($change);
- break;
- // Added lines only on the right side
- case 'insert':
- $html .= $this->generateTableRowsInsert($change);
- break;
- // Show deleted lines only on the left side
- case 'delete':
- $html .= $this->generateTableRowsDelete($change);
- break;
- // Show modified lines on both sides
- case 'replace':
- $html .= $this->generateTableRowsReplace($change);
- break;
- }
- $html .= '';
- }
- }
- $html .= '';
- return $html;
- }
+ foreach ($blocks as $change) {
+ $html .= '';
+ switch ($change['tag']) {
+ // Equal changes should be shown on both sides of the diff
+ case 'equal':
+ $html .= $this->generateTableRowsEqual($change);
+ break;
+ // Added lines only on the right side
+ case 'insert':
+ $html .= $this->generateTableRowsInsert($change);
+ break;
+ // Show deleted lines only on the left side
+ case 'delete':
+ $html .= $this->generateTableRowsDelete($change);
+ break;
+ // Show modified lines on both sides
+ case 'replace':
+ $html .= $this->generateTableRowsReplace($change);
+ break;
+ }
+ $html .= '';
+ }
+ }
+ $html .= '';
+ return $html;
+ }
- /**
- * Generates a string representation of a predefined table and its head with
- * titles from options.
- *
- * @return string Html code representation of the table's header.
- */
- private function generateTableHeader() : string
- {
- $html = '';
- $html .= '';
- $html .= '';
- $html .= 'Old | ';
- $html .= 'New | ';
- $html .= 'Differences | ';
- $html .= '
';
- $html .= '';
- return $html;
- }
+ /**
+ * Generates a string representation of a predefined table and its head with
+ * titles from options.
+ *
+ * @return string Html code representation of the table's header.
+ */
+ private function generateTableHeader() : string
+ {
+ $html = '';
+ $html .= '';
+ $html .= '';
+ $html .= 'Old | ';
+ $html .= 'New | ';
+ $html .= 'Differences | ';
+ $html .= '
';
+ $html .= '';
+ return $html;
+ }
- /**
- * Generates a string representation of empty table body.
- *
- * @return string Html code representing empty table body.
- */
- private function generateSkippedTable() : string
- {
- $html = '';
- $html .= '… | ';
- $html .= '… | ';
- $html .= ' | ';
- $html .= '';
- return $html;
- }
+ /**
+ * Generates a string representation of empty table body.
+ *
+ * @return string Html code representing empty table body.
+ */
+ private function generateSkippedTable() : string
+ {
+ $html = '';
+ $html .= '… | ';
+ $html .= '… | ';
+ $html .= ' | ';
+ $html .= '';
+ return $html;
+ }
- /**
- * Generates a string representation of one or more rows of a table of lines of text with no difference.
- *
- * @param array &$change Array with data about changes.
- * @return string Html code representing one or more rows of text with no difference.
- */
- private function generateTableRowsEqual(&$change) : string
- {
- $html = "";
- foreach ($change['base']['lines'] as $no => $line) {
- $fromLine = $change['base']['offset'] + $no + 1;
- $toLine = $change['changed']['offset'] + $no + 1;
- $html .= '';
- $html .= ''.$fromLine.' | ';
- $html .= ''.$toLine.' | ';
- $html .= ''.$line.' | ';
- $html .= '
';
- }
- return $html;
- }
+ /**
+ * Generates a string representation of one or more rows of a table of lines of text with no difference.
+ *
+ * @param array &$change Array with data about changes.
+ * @return string Html code representing one or more rows of text with no difference.
+ */
+ private function generateTableRowsEqual(&$change) : string
+ {
+ $html = "";
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ''.$fromLine.' | ';
+ $html .= ''.$toLine.' | ';
+ $html .= ''.$line.' | ';
+ $html .= '
';
+ }
+ return $html;
+ }
- /**
- * Generates a string representation of one or more rows of a table of lines, where new text was added.
- *
- * @param array &$change Array with data about changes.
- * @return string Html code representing one or more rows of added text.
- */
- private function generateTableRowsInsert(&$change) : string
- {
- $html = "";
- foreach ($change['changed']['lines'] as $no => $line) {
- $toLine = $change['changed']['offset'] + $no + 1;
- $html .= '';
- $html .= ' | ';
- $html .= ''.$toLine.' | ';
- $html .= ''.$line.' | ';
- $html .= '
';
- }
- return $html;
- }
+ /**
+ * Generates a string representation of one or more rows of a table of lines, where new text was added.
+ *
+ * @param array &$change Array with data about changes.
+ * @return string Html code representing one or more rows of added text.
+ */
+ private function generateTableRowsInsert(&$change) : string
+ {
+ $html = "";
+ foreach ($change['changed']['lines'] as $no => $line) {
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ' | ';
+ $html .= ''.$toLine.' | ';
+ $html .= ''.$line.' | ';
+ $html .= '
';
+ }
+ return $html;
+ }
- /**
- * Generates a string representation of one or more rows of a table of lines, where text was removed.
- *
- * @param array &$change Array with data about changes.
- * @return string Html code representing one or more rows of removed text.
- */
- private function generateTableRowsDelete(&$change) : string
- {
- $html = "";
- foreach ($change['base']['lines'] as $no => $line) {
- $fromLine = $change['base']['offset'] + $no + 1;
- $html .= '';
- $html .= ''.$fromLine.' | ';
- $html .= ' | ';
- $html .= ''.$line.' | ';
- $html .= '
';
- }
- return $html;
- }
+ /**
+ * Generates a string representation of one or more rows of a table of lines, where text was removed.
+ *
+ * @param array &$change Array with data about changes.
+ * @return string Html code representing one or more rows of removed text.
+ */
+ private function generateTableRowsDelete(&$change) : string
+ {
+ $html = "";
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ''.$fromLine.' | ';
+ $html .= ' | ';
+ $html .= ''.$line.' | ';
+ $html .= '
';
+ }
+ return $html;
+ }
- /**
- * Generates a string representation of one or more rows of a table of lines, where text was partially modified.
- *
- * @param array &$change Array with data about changes.
- * @return string Html code representing one or more rows of modified.
- */
- private function generateTableRowsReplace(&$change) : string
- {
- $html = "";
+ /**
+ * Generates a string representation of one or more rows of a table of lines, where text was partially modified.
+ *
+ * @param array &$change Array with data about changes.
+ * @return string Html code representing one or more rows of modified.
+ */
+ private function generateTableRowsReplace(&$change) : string
+ {
+ $html = "";
- foreach ($change['base']['lines'] as $no => $line) {
- $fromLine = $change['base']['offset'] + $no + 1;
- $html .= '';
- $html .= ''.$fromLine.' | ';
- $html .= ' | ';
- $html .= ''.$line.' | ';
- $html .= '
';
- }
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ''.$fromLine.' | ';
+ $html .= ' | ';
+ $html .= ''.$line.' | ';
+ $html .= '
';
+ }
- foreach ($change['changed']['lines'] as $no => $line) {
- $toLine = $change['changed']['offset'] + $no + 1;
- $html .= '';
- $html .= ' | ';
- $html .= ''.$toLine.' | ';
- $html .= ''.$line.' | ';
- $html .= '
';
- }
+ foreach ($change['changed']['lines'] as $no => $line) {
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ' | ';
+ $html .= ''.$toLine.' | ';
+ $html .= ''.$line.' | ';
+ $html .= '
';
+ }
- return $html;
- }
+ return $html;
+ }
}
diff --git a/lib/jblond/Diff/Renderer/Html/SideBySide.php b/lib/jblond/Diff/Renderer/Html/SideBySide.php
index 20c83c62..57c9fd09 100644
--- a/lib/jblond/Diff/Renderer/Html/SideBySide.php
+++ b/lib/jblond/Diff/Renderer/Html/SideBySide.php
@@ -48,197 +48,197 @@
*/
class SideBySide extends HtmlArray
{
- /**
- * Render a and return diff with changes between the two sequences
- * displayed side by side.
- *
- * @return string The generated side by side diff.
- */
- public function render() : string
- {
- $changes = parent::render();
+ /**
+ * Render a and return diff with changes between the two sequences
+ * displayed side by side.
+ *
+ * @return string The generated side by side diff.
+ */
+ public function render() : string
+ {
+ $changes = parent::render();
- $html = '';
- if (empty($changes)) {
- return $html;
- }
+ $html = '';
+ if (empty($changes)) {
+ return $html;
+ }
- $html .= $this->generateTableHeader();
+ $html .= $this->generateTableHeader();
- foreach ($changes as $i => $blocks) {
- if ($i > 0) {
- $html .= $this->generateSkippedTable();
- }
+ foreach ($changes as $i => $blocks) {
+ if ($i > 0) {
+ $html .= $this->generateSkippedTable();
+ }
- foreach ($blocks as $change) {
- $html .= '';
- switch ($change['tag']) {
- // Equal changes should be shown on both sides of the diff
- case 'equal':
- $html .= $this->generateTableRowsEqual($change);
- break;
- // Added lines only on the right side
- case 'insert':
- $html .= $this->generateTableRowsInsert($change);
- break;
- // Show deleted lines only on the left side
- case 'delete':
- $html .= $this->generateTableRowsDelete($change);
- break;
- // Show modified lines on both sides
- case 'replace':
- $html .= $this->generateTableRowsReplace($change);
- break;
- }
- $html .= '';
- }
- }
- $html .= '
';
- return $html;
- }
+ foreach ($blocks as $change) {
+ $html .= '';
+ switch ($change['tag']) {
+ // Equal changes should be shown on both sides of the diff
+ case 'equal':
+ $html .= $this->generateTableRowsEqual($change);
+ break;
+ // Added lines only on the right side
+ case 'insert':
+ $html .= $this->generateTableRowsInsert($change);
+ break;
+ // Show deleted lines only on the left side
+ case 'delete':
+ $html .= $this->generateTableRowsDelete($change);
+ break;
+ // Show modified lines on both sides
+ case 'replace':
+ $html .= $this->generateTableRowsReplace($change);
+ break;
+ }
+ $html .= '';
+ }
+ }
+ $html .= '
';
+ return $html;
+ }
- /**
- * Generates a string representation of a predefined table and its head with
- * titles from options.
- *
- * @return string Html code representation of the table's header.
- */
- private function generateTableHeader() : string
- {
- $html = '';
- $html .= '';
- $html .= '';
- $html .= ''.$this->options['title_a'].' | ';
- $html .= ''.$this->options['title_b'].' | ';
- $html .= '
';
- $html .= '';
- return $html;
- }
+ /**
+ * Generates a string representation of a predefined table and its head with
+ * titles from options.
+ *
+ * @return string Html code representation of the table's header.
+ */
+ private function generateTableHeader() : string
+ {
+ $html = '';
+ $html .= '';
+ $html .= '';
+ $html .= ''.$this->options['title_a'].' | ';
+ $html .= ''.$this->options['title_b'].' | ';
+ $html .= '
';
+ $html .= '';
+ return $html;
+ }
- /**
- * Generates a string representation of empty table body.
- *
- * @return string Html code representing empty table body.
- */
- private function generateSkippedTable() : string
- {
- $html = '';
- $html .= '… | | ';
- $html .= '… | | ';
- $html .= '';
- return $html;
- }
+ /**
+ * Generates a string representation of empty table body.
+ *
+ * @return string Html code representing empty table body.
+ */
+ private function generateSkippedTable() : string
+ {
+ $html = '';
+ $html .= '… | | ';
+ $html .= '… | | ';
+ $html .= '';
+ return $html;
+ }
- /**
- * Generates a string representation of one or more rows of a table of lines of text with no difference.
- *
- * @param array &$change Array with data about changes.
- * @return string Html code representing one or more rows of text with no difference.
- */
- private function generateTableRowsEqual(&$change) : string
- {
- $html = "";
- foreach ($change['base']['lines'] as $no => $line) {
- $fromLine = $change['base']['offset'] + $no + 1;
- $toLine = $change['changed']['offset'] + $no + 1;
- $html .= '';
- $html .= ''.$fromLine.' | ';
- $html .= ''.$line.' | ';
- $html .= ''.$toLine.' | ';
- $html .= ''.$line.' | ';
- $html .= '
';
- }
- return $html;
- }
+ /**
+ * Generates a string representation of one or more rows of a table of lines of text with no difference.
+ *
+ * @param array &$change Array with data about changes.
+ * @return string Html code representing one or more rows of text with no difference.
+ */
+ private function generateTableRowsEqual(&$change) : string
+ {
+ $html = "";
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ''.$fromLine.' | ';
+ $html .= ''.$line.' | ';
+ $html .= ''.$toLine.' | ';
+ $html .= ''.$line.' | ';
+ $html .= '
';
+ }
+ return $html;
+ }
- /**
- * Generates a string representation of one or more rows of a table of lines, where new text was added.
- *
- * @param array &$change Array with data about changes.
- * @return string Html code representing one or more rows of added text.
- */
- private function generateTableRowsInsert(&$change) : string
- {
- $html = "";
- foreach ($change['changed']['lines'] as $no => $line) {
- $toLine = $change['changed']['offset'] + $no + 1;
- $html .= '';
- $html .= ' | ';
- $html .= ' | ';
- $html .= ''.$toLine.' | ';
- $html .= ''.$line.' | ';
- $html .= '
';
- }
- return $html;
- }
+ /**
+ * Generates a string representation of one or more rows of a table of lines, where new text was added.
+ *
+ * @param array &$change Array with data about changes.
+ * @return string Html code representing one or more rows of added text.
+ */
+ private function generateTableRowsInsert(&$change) : string
+ {
+ $html = "";
+ foreach ($change['changed']['lines'] as $no => $line) {
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ' | ';
+ $html .= ' | ';
+ $html .= ''.$toLine.' | ';
+ $html .= ''.$line.' | ';
+ $html .= '
';
+ }
+ return $html;
+ }
- /**
- * Generates a string representation of one or more rows of a table of lines, where text was removed.
- *
- * @param array &$change Array with data about changes.
- * @return string Html code representing one or more rows of removed text.
- */
- private function generateTableRowsDelete(&$change) : string
- {
- $html = "";
- foreach ($change['base']['lines'] as $no => $line) {
- $fromLine = $change['base']['offset'] + $no + 1;
- $html .= '';
- $html .= ''.$fromLine.' | ';
- $html .= ''.$line.' | ';
- $html .= ' | ';
- $html .= ' | ';
- $html .= '
';
- }
- return $html;
- }
+ /**
+ * Generates a string representation of one or more rows of a table of lines, where text was removed.
+ *
+ * @param array &$change Array with data about changes.
+ * @return string Html code representing one or more rows of removed text.
+ */
+ private function generateTableRowsDelete(&$change) : string
+ {
+ $html = "";
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ''.$fromLine.' | ';
+ $html .= ''.$line.' | ';
+ $html .= ' | ';
+ $html .= ' | ';
+ $html .= '
';
+ }
+ return $html;
+ }
- /**
- * Generates a string representation of one or more rows of a table of lines, where text was partially modified.
- *
- * @param array &$change Array with data about changes.
- * @return string Html code representing one or more rows of modified.
- */
- private function generateTableRowsReplace(&$change) : string
- {
- $html = "";
+ /**
+ * Generates a string representation of one or more rows of a table of lines, where text was partially modified.
+ *
+ * @param array &$change Array with data about changes.
+ * @return string Html code representing one or more rows of modified.
+ */
+ private function generateTableRowsReplace(&$change) : string
+ {
+ $html = "";
- if (count($change['base']['lines']) >= count($change['changed']['lines'])) {
- foreach ($change['base']['lines'] as $no => $line) {
- $fromLine = $change['base']['offset'] + $no + 1;
- $html .= '';
- $html .= ''.$fromLine.' | ';
- $html .= ''.$line.' | ';
- if (!isset($change['changed']['lines'][$no])) {
- $toLine = ' ';
- $changedLine = ' ';
- } else {
- $toLine = $change['changed']['offset'] + $no + 1;
- $changedLine = ''.$change['changed']['lines'][$no].'';
- }
- $html .= ''.$toLine.' | ';
- $html .= ''.$changedLine.' | ';
- $html .= '
';
- }
- } else {
- foreach ($change['changed']['lines'] as $no => $changedLine) {
- if (!isset($change['base']['lines'][$no])) {
- $fromLine = ' ';
- $line = ' ';
- } else {
- $fromLine = $change['base']['offset'] + $no + 1;
- $line = ''.$change['base']['lines'][$no].'';
- }
- $html .= '';
- $html .= ''.$fromLine.' | ';
- $html .= ''.$line.' | ';
- $toLine = $change['changed']['offset'] + $no + 1;
- $html .= ''.$toLine.' | ';
- $html .= ''.$changedLine.' | ';
- $html .= '
';
- }
- }
+ if (count($change['base']['lines']) >= count($change['changed']['lines'])) {
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $html .= '';
+ $html .= ''.$fromLine.' | ';
+ $html .= ''.$line.' | ';
+ if (!isset($change['changed']['lines'][$no])) {
+ $toLine = ' ';
+ $changedLine = ' ';
+ } else {
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $changedLine = ''.$change['changed']['lines'][$no].'';
+ }
+ $html .= ''.$toLine.' | ';
+ $html .= ''.$changedLine.' | ';
+ $html .= '
';
+ }
+ } else {
+ foreach ($change['changed']['lines'] as $no => $changedLine) {
+ if (!isset($change['base']['lines'][$no])) {
+ $fromLine = ' ';
+ $line = ' ';
+ } else {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $line = ''.$change['base']['lines'][$no].'';
+ }
+ $html .= '';
+ $html .= ''.$fromLine.' | ';
+ $html .= ''.$line.' | ';
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= ''.$toLine.' | ';
+ $html .= ''.$changedLine.' | ';
+ $html .= '
';
+ }
+ }
- return $html;
- }
+ return $html;
+ }
}
diff --git a/lib/jblond/Diff/Renderer/RendererAbstract.php b/lib/jblond/Diff/Renderer/RendererAbstract.php
index 895e629a..401b3e53 100644
--- a/lib/jblond/Diff/Renderer/RendererAbstract.php
+++ b/lib/jblond/Diff/Renderer/RendererAbstract.php
@@ -44,44 +44,44 @@
*/
abstract class RendererAbstract
{
- /**
- * @var object Instance of the diff class that this renderer is generating the rendered diff for.
- */
- public $diff;
+ /**
+ * @var object Instance of the diff class that this renderer is generating the rendered diff for.
+ */
+ public $diff;
- /**
- * @var array Array of the default options that apply to this renderer.
- */
+ /**
+ * @var array Array of the default options that apply to this renderer.
+ */
protected $defaultOptions = array(
'title_a' => 'Old Version',
'title_b' => 'New Version',
);
- /**
- * @var array Array containing the user applied and merged default options for the renderer.
- */
- protected $options = array();
+ /**
+ * @var array Array containing the user applied and merged default options for the renderer.
+ */
+ protected $options = array();
- /**
- * The constructor. Instantiates the rendering engine and if options are passed,
- * sets the options for the renderer.
- *
- * @param array $options Optionally, an array of the options for the renderer.
- */
- public function __construct(array $options = array())
- {
- $this->setOptions($options);
- }
+ /**
+ * The constructor. Instantiates the rendering engine and if options are passed,
+ * sets the options for the renderer.
+ *
+ * @param array $options Optionally, an array of the options for the renderer.
+ */
+ public function __construct(array $options = array())
+ {
+ $this->setOptions($options);
+ }
- /**
- * Set the options of the renderer to those supplied in the passed in array.
- * Options are merged with the default to ensure that there aren't any missing
- * options.
- *
- * @param array $options Array of options to set.
- */
- public function setOptions(array $options)
- {
- $this->options = array_merge($this->defaultOptions, $options);
- }
+ /**
+ * Set the options of the renderer to those supplied in the passed in array.
+ * Options are merged with the default to ensure that there aren't any missing
+ * options.
+ *
+ * @param array $options Array of options to set.
+ */
+ public function setOptions(array $options)
+ {
+ $this->options = array_merge($this->defaultOptions, $options);
+ }
}
diff --git a/lib/jblond/Diff/Renderer/Text/Context.php b/lib/jblond/Diff/Renderer/Text/Context.php
index 65d71e9f..8865c9b6 100644
--- a/lib/jblond/Diff/Renderer/Text/Context.php
+++ b/lib/jblond/Diff/Renderer/Text/Context.php
@@ -50,85 +50,85 @@
*/
class Context extends RendererAbstract
{
- /**
- * @var array Array of the different op code tags and how they map to the context diff equivalent.
- */
- private $tagMap = array(
- 'insert' => '+',
- 'delete' => '-',
- 'replace' => '!',
- 'equal' => ' '
- );
+ /**
+ * @var array Array of the different op code tags and how they map to the context diff equivalent.
+ */
+ private $tagMap = array(
+ 'insert' => '+',
+ 'delete' => '-',
+ 'replace' => '!',
+ 'equal' => ' '
+ );
- /**
- * Render and return a context formatted (old school!) diff file.
- *
- * @return string The generated context diff.
- */
- public function render() : string
- {
- $diff = '';
- $opCodes = $this->diff->getGroupedOpcodes();
- foreach ($opCodes as $group) {
- $diff .= "***************\n";
- $lastItem = count($group)-1;
- $i1 = $group['0']['1'];
- $i2 = $group[$lastItem]['2'];
- $j1 = $group['0']['3'];
- $j2 = $group[$lastItem]['4'];
+ /**
+ * Render and return a context formatted (old school!) diff file.
+ *
+ * @return string The generated context diff.
+ */
+ public function render() : string
+ {
+ $diff = '';
+ $opCodes = $this->diff->getGroupedOpcodes();
+ foreach ($opCodes as $group) {
+ $diff .= "***************\n";
+ $lastItem = count($group)-1;
+ $i1 = $group['0']['1'];
+ $i2 = $group[$lastItem]['2'];
+ $j1 = $group['0']['3'];
+ $j2 = $group[$lastItem]['4'];
- if ($i2 - $i1 >= 2) {
- $diff .= '*** '.($group['0']['1'] + 1).','.$i2." ****\n";
- } else {
- $diff .= '*** '.$i2." ****\n";
- }
+ if ($i2 - $i1 >= 2) {
+ $diff .= '*** '.($group['0']['1'] + 1).','.$i2." ****\n";
+ } else {
+ $diff .= '*** '.$i2." ****\n";
+ }
- if ($j2 - $j1 >= 2) {
- $separator = '--- '.($j1 + 1).','.$j2." ----\n";
- } else {
- $separator = '--- '.$j2." ----\n";
- }
+ if ($j2 - $j1 >= 2) {
+ $separator = '--- '.($j1 + 1).','.$j2." ----\n";
+ } else {
+ $separator = '--- '.$j2." ----\n";
+ }
- $hasVisible = false;
- foreach ($group as $code) {
- if ($code['0'] == 'replace' || $code['0'] == 'delete') {
- $hasVisible = true;
- break;
- }
- }
+ $hasVisible = false;
+ foreach ($group as $code) {
+ if ($code['0'] == 'replace' || $code['0'] == 'delete') {
+ $hasVisible = true;
+ break;
+ }
+ }
- if ($hasVisible) {
- foreach ($group as $code) {
- list($tag, $i1, $i2, $j1, $j2) = $code;
- if ($tag == 'insert') {
- continue;
- }
- $diff .= $this->tagMap[$tag] . ' ' .
- implode("\n" . $this->tagMap[$tag] .' ', $this->diff->GetA($i1, $i2)) . "\n";
- }
- }
+ if ($hasVisible) {
+ foreach ($group as $code) {
+ list($tag, $i1, $i2, $j1, $j2) = $code;
+ if ($tag == 'insert') {
+ continue;
+ }
+ $diff .= $this->tagMap[$tag] . ' ' .
+ implode("\n" . $this->tagMap[$tag] .' ', $this->diff->GetA($i1, $i2)) . "\n";
+ }
+ }
- $hasVisible = false;
- foreach ($group as $code) {
- if ($code['0'] == 'replace' || $code['0'] == 'insert') {
- $hasVisible = true;
- break;
- }
- }
+ $hasVisible = false;
+ foreach ($group as $code) {
+ if ($code['0'] == 'replace' || $code['0'] == 'insert') {
+ $hasVisible = true;
+ break;
+ }
+ }
- $diff .= $separator;
+ $diff .= $separator;
- if ($hasVisible) {
- foreach ($group as $code) {
- list($tag, $i1, $i2, $j1, $j2) = $code;
- if ($tag == 'delete') {
- continue;
- }
- $diff .= $this->tagMap[$tag] . ' ' .
- implode("\n".$this->tagMap[$tag] . ' ', $this->diff->GetB($j1, $j2)) . "\n";
- }
- }
- }
- return $diff;
- }
+ if ($hasVisible) {
+ foreach ($group as $code) {
+ list($tag, $i1, $i2, $j1, $j2) = $code;
+ if ($tag == 'delete') {
+ continue;
+ }
+ $diff .= $this->tagMap[$tag] . ' ' .
+ implode("\n".$this->tagMap[$tag] . ' ', $this->diff->GetB($j1, $j2)) . "\n";
+ }
+ }
+ }
+ return $diff;
+ }
}
diff --git a/lib/jblond/Diff/Renderer/Text/Unified.php b/lib/jblond/Diff/Renderer/Text/Unified.php
index 295a7ad1..901a95b6 100644
--- a/lib/jblond/Diff/Renderer/Text/Unified.php
+++ b/lib/jblond/Diff/Renderer/Text/Unified.php
@@ -50,43 +50,43 @@
*/
class Unified extends RendererAbstract
{
- /**
- * Render and return a unified diff.
- *
- * @return string The unified diff.
- */
- public function render() : string
- {
- $diff = '';
- $opCodes = $this->diff->getGroupedOpcodes();
- foreach ($opCodes as $group) {
- $lastItem = count($group)-1;
- $i1 = $group['0']['1'];
- $i2 = $group[$lastItem]['2'];
- $j1 = $group['0']['3'];
- $j2 = $group[$lastItem]['4'];
+ /**
+ * Render and return a unified diff.
+ *
+ * @return string The unified diff.
+ */
+ public function render() : string
+ {
+ $diff = '';
+ $opCodes = $this->diff->getGroupedOpcodes();
+ foreach ($opCodes as $group) {
+ $lastItem = count($group)-1;
+ $i1 = $group['0']['1'];
+ $i2 = $group[$lastItem]['2'];
+ $j1 = $group['0']['3'];
+ $j2 = $group[$lastItem]['4'];
- if ($i1 == 0 && $i2 == 0) {
- $i1 = -1;
- $i2 = -1;
- }
+ if ($i1 == 0 && $i2 == 0) {
+ $i1 = -1;
+ $i2 = -1;
+ }
- $diff .= '@@ -'.($i1 + 1).','.($i2 - $i1).' +'.($j1 + 1).','.($j2 - $j1)." @@\n";
- foreach ($group as $code) {
- list($tag, $i1, $i2, $j1, $j2) = $code;
- if ($tag == 'equal') {
- $diff .= ' '.implode("\n ", $this->diff->GetA($i1, $i2))."\n";
- } else {
- if ($tag == 'replace' || $tag == 'delete') {
- $diff .= '-'.implode("\n-", $this->diff->GetA($i1, $i2))."\n";
- }
+ $diff .= '@@ -'.($i1 + 1).','.($i2 - $i1).' +'.($j1 + 1).','.($j2 - $j1)." @@\n";
+ foreach ($group as $code) {
+ list($tag, $i1, $i2, $j1, $j2) = $code;
+ if ($tag == 'equal') {
+ $diff .= ' '.implode("\n ", $this->diff->GetA($i1, $i2))."\n";
+ } else {
+ if ($tag == 'replace' || $tag == 'delete') {
+ $diff .= '-'.implode("\n-", $this->diff->GetA($i1, $i2))."\n";
+ }
- if ($tag == 'replace' || $tag == 'insert') {
- $diff .= '+'.implode("\n+", $this->diff->GetB($j1, $j2))."\n";
- }
- }
- }
- }
- return $diff;
- }
+ if ($tag == 'replace' || $tag == 'insert') {
+ $diff .= '+'.implode("\n+", $this->diff->GetB($j1, $j2))."\n";
+ }
+ }
+ }
+ }
+ return $diff;
+ }
}
diff --git a/lib/jblond/Diff/SequenceMatcher.php b/lib/jblond/Diff/SequenceMatcher.php
index 1791cfc3..5d3f5a15 100644
--- a/lib/jblond/Diff/SequenceMatcher.php
+++ b/lib/jblond/Diff/SequenceMatcher.php
@@ -44,677 +44,677 @@
*/
class SequenceMatcher
{
- /**
- * @var string|array Either a string or an array containing a callback function to determine
- * if a line is "junk" or not.
- */
- private $junkCallback = null;
-
- /**
- * @var array The first sequence to compare against.
- */
- private $a = array();
-
- /**
- * @var array The second sequence.
- */
- private $b = array();
-
- /**
- * @var array Array of characters that are considered junk from the second sequence. Characters are the array key.
- */
- private $junkDict = array();
-
- /**
- * @var array Array of indices that do not contain junk elements.
- */
- private $b2j = array();
-
- /**
- * @var array
- */
- private $options = array();
-
- /**
- * @var null|array
- */
- private $opCodes;
-
- /**
- * @var null|array
- */
- private $matchingBlocks;
-
- /**
- * @var null|array
- */
- private $fullBCount;
-
- /**
- * @var array
- */
- private $defaultOptions = array(
- 'ignoreNewLines' => false,
- 'ignoreWhitespace' => false,
- 'ignoreCase' => false
- );
-
- /**
- * The constructor. With the sequences being passed, they'll be set for the
- * sequence matcher and it will perform a basic cleanup & calculate junk
- * elements.
- *
- * @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 array $options
- * @param string|array|null $junkCallback Either an array or string that references a callback function
- * (if there is one) to determine 'junk' characters.
- */
- public function __construct($a, $b, $options, $junkCallback = null)
- {
- $this->a = array();
- $this->b = array();
- $this->junkCallback = $junkCallback;
- $this->setOptions($options);
- $this->setSequences($a, $b);
- }
-
- /**
- * @param array $options
- */
- public function setOptions($options)
- {
- $this->options = array_merge($this->defaultOptions, $options);
- }
-
- /**
- * Set the first and second sequences to use with the sequence matcher.
- *
- * @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.
- */
- public function setSequences($a, $b)
- {
- $this->setSeq1($a);
- $this->setSeq2($b);
- }
-
- /**
- * Set the first sequence ($a) and reset any internal caches to indicate that
- * when calling the calculation methods, we need to recalculate them.
- *
- * @param string|array $a The sequence to set as the first sequence.
- */
- public function setSeq1($a)
- {
- if (!is_array($a)) {
- $a = str_split($a);
- }
- if ($a == $this->a) {
- return;
- }
-
- $this->a = $a;
- $this->matchingBlocks = null;
- $this->opCodes = null;
- }
-
- /**
- * Set the second sequence ($b) and reset any internal caches to indicate that
- * when calling the calculation methods, we need to recalculate them.
- *
- * @param string|array $b The sequence to set as the second sequence.
- */
- public function setSeq2($b)
- {
- if (!is_array($b)) {
- $b = str_split($b);
- }
- if ($b == $this->b) {
- return;
- }
-
- $this->b = $b;
- $this->matchingBlocks = null;
- $this->opCodes = null;
- $this->fullBCount = null;
- $this->chainB();
- }
-
- /**
- * Generate the internal arrays containing the list of junk and non-junk
- * characters for the second ($b) sequence.
- */
- private function chainB()
- {
- $length = count($this->b);
- $this->b2j = array();
- $popularDict = array();
-
- for ($i = 0; $i < $length; ++$i) {
- $char = $this->b[$i];
- if (isset($this->b2j[$char])) {
- if ($length >= 200 && count($this->b2j[$char]) * 100 > $length) {
- $popularDict[$char] = 1;
- unset($this->b2j[$char]);
- } else {
- $this->b2j[$char][] = $i;
- }
- } else {
- $this->b2j[$char] = array(
- $i
- );
- }
- }
-
- // Remove leftovers
- foreach (array_keys($popularDict) as $char) {
- unset($this->b2j[$char]);
- }
-
- $this->junkDict = array();
- if (is_callable($this->junkCallback)) {
- foreach (array_keys($popularDict) as $char) {
- if (call_user_func($this->junkCallback, $char)) {
- $this->junkDict[$char] = 1;
- unset($popularDict[$char]);
- }
- }
-
- foreach (array_keys($this->b2j) as $char) {
- if (call_user_func($this->junkCallback, $char)) {
- $this->junkDict[$char] = 1;
- unset($this->b2j[$char]);
- }
- }
- }
- }
-
- /**
- * Checks if a particular character is in the junk dictionary
- * for the list of junk characters.
- *
- * @param string $b
- * @return bool $b True if the character is considered junk. False if not.
- */
- private function isBJunk($b) : bool
- {
- if (isset($this->junkDict[$b])) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Find the longest matching block in the two sequences, as defined by the
- * lower and upper constraints for each sequence. (for the first sequence,
- * $alo - $ahi and for the second sequence, $blo - $bhi)
- *
- * Essentially, of all of the maximal matching blocks, return the one that
- * starts earliest in $a, and all of those maximal matching blocks that
- * start earliest in $a, return the one that starts earliest in $b.
- *
- * If the junk callback is defined, do the above but with the restriction
- * that the junk element appears in the block. Extend it as far as possible
- * by matching only junk elements in both $a and $b.
- *
- * @param int $alo The lower constraint for the first sequence.
- * @param int $ahi The upper constraint for the first sequence.
- * @param int $blo The lower constraint for the second sequence.
- * @param int $bhi The upper constraint for the second sequence.
- * @return array Array containing the longest match that includes the starting position in $a,
- * start in $b and the length/size.
- */
- public function findLongestMatch($alo, $ahi, $blo, $bhi) : array
- {
- $a = $this->a;
- $b = $this->b;
-
- $bestI = $alo;
- $bestJ = $blo;
- $bestSize = 0;
-
- $j2Len = array();
- $nothing = array();
-
- for ($i = $alo; $i < $ahi; ++$i) {
- $newJ2Len = array();
- $jDict = $this->arrayGetDefault($this->b2j, $a[$i], $nothing);
- foreach ($jDict as $jKey => $j) {
- if ($j < $blo) {
- continue;
- } elseif ($j >= $bhi) {
- break;
- }
-
- $k = $this->arrayGetDefault($j2Len, $j -1, 0) + 1;
- $newJ2Len[$j] = $k;
- if ($k > $bestSize) {
- $bestI = $i - $k + 1;
- $bestJ = $j - $k + 1;
- $bestSize = $k;
- }
- }
-
- $j2Len = $newJ2Len;
- }
-
- while ($bestI > $alo &&
- $bestJ > $blo &&
- !$this->isBJunk($b[$bestJ - 1]) &&
- !$this->linesAreDifferent($bestI - 1, $bestJ - 1)
- ) {
- --$bestI;
- --$bestJ;
- ++$bestSize;
- }
-
- while ($bestI + $bestSize < $ahi &&
- ($bestJ + $bestSize) < $bhi &&
- !$this->isBJunk($b[$bestJ + $bestSize]) &&
- !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)) {
- ++$bestSize;
- }
-
- while ($bestI > $alo &&
- $bestJ > $blo &&
- $this->isBJunk($b[$bestJ - 1]) &&
- !$this->linesAreDifferent($bestI - 1, $bestJ - 1)
- ) {
- --$bestI;
- --$bestJ;
- ++$bestSize;
- }
-
- while ($bestI + $bestSize < $ahi &&
- $bestJ + $bestSize < $bhi &&
- $this->isBJunk($b[$bestJ + $bestSize]) &&
- !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)
- ) {
- ++$bestSize;
- }
-
- return array(
- $bestI,
- $bestJ,
- $bestSize
- );
- }
-
- /**
- * Check if the two lines at the given indexes are different or not.
- *
- * @param int $aIndex Line number to check against in a.
- * @param int $bIndex Line number to check against in b.
- * @return bool True if the lines are different and false if not.
- */
- public function linesAreDifferent($aIndex, $bIndex) : bool
- {
- $lineA = $this->a[$aIndex];
- $lineB = $this->b[$bIndex];
-
- if ($this->options['ignoreWhitespace']) {
- $replace = array("\t", ' ');
- $lineA = str_replace($replace, '', $lineA);
- $lineB = str_replace($replace, '', $lineB);
- }
-
- if ($this->options['ignoreCase']) {
- $lineA = strtolower($lineA);
- $lineB = strtolower($lineB);
- }
-
- if ($lineA != $lineB) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Return a nested set of arrays for all of the matching sub-sequences
- * in the strings $a and $b.
- *
- * Each block contains the lower constraint of the block in $a, the lower
- * constraint of the block in $b and finally the number of lines that the
- * block continues for.
- *
- * @return array Nested array of the matching blocks, as described by the function.
- */
- public function getMatchingBlocks() : array
- {
- if (!empty($this->matchingBlocks)) {
- return $this->matchingBlocks;
- }
-
- $aLength = count($this->a);
- $bLength = count($this->b);
-
- $queue = array(
- array(
- 0,
- $aLength,
- 0,
- $bLength
- )
- );
-
- $matchingBlocks = array();
- while (!empty($queue)) {
- list($alo, $ahi, $blo, $bhi) = array_pop($queue);
- $x = $this->findLongestMatch($alo, $ahi, $blo, $bhi);
- list($i, $j, $k) = $x;
- if ($k) {
- $matchingBlocks[] = $x;
- if ($alo < $i && $blo < $j) {
- $queue[] = array(
- $alo,
- $i,
- $blo,
- $j
- );
- }
-
- if ($i + $k < $ahi && $j + $k < $bhi) {
- $queue[] = array(
- $i + $k,
- $ahi,
- $j + $k,
- $bhi
- );
- }
- }
- }
-
- usort($matchingBlocks, array($this, 'tupleSort'));
-
- $i1 = 0;
- $j1 = 0;
- $k1 = 0;
- $nonAdjacent = array();
- foreach ($matchingBlocks as $block) {
- list($i2, $j2, $k2) = $block;
- if ($i1 + $k1 == $i2 && $j1 + $k1 == $j2) {
- $k1 += $k2;
- } else {
- if ($k1) {
- $nonAdjacent[] = array(
- $i1,
- $j1,
- $k1
- );
- }
-
- $i1 = $i2;
- $j1 = $j2;
- $k1 = $k2;
- }
- }
-
- if ($k1) {
- $nonAdjacent[] = array(
- $i1,
- $j1,
- $k1
- );
- }
-
- $nonAdjacent[] = array(
- $aLength,
- $bLength,
- 0
- );
-
- $this->matchingBlocks = $nonAdjacent;
- return $this->matchingBlocks;
- }
-
- /**
- * Return a list of all of the op codes for the differences between the
- * two strings.
- *
- * The nested array returned contains an array describing the op code
- * which includes:
- * 0 - The type of tag (as described below) for the op code.
- * 1 - The beginning line in the first sequence.
- * 2 - The end line in the first sequence.
- * 3 - The beginning line in the second sequence.
- * 4 - The end line in the second sequence.
- *
- * The different types of tags include:
- * replace - The string from $i1 to $i2 in $a should be replaced by
- * the string in $b from $j1 to $j2.
- * delete - The string in $a from $i1 to $j2 should be deleted.
- * insert - The string in $b from $j1 to $j2 should be inserted at
- * $i1 in $a.
- * equal - The two strings with the specified ranges are equal.
- *
- * @return array Array of the opcodes describing the differences between the strings.
- */
- public function getOpCodes() : array
- {
- if (!empty($this->opCodes)) {
- return $this->opCodes;
- }
-
- $i = 0;
- $j = 0;
- $this->opCodes = array();
-
- $blocks = $this->getMatchingBlocks();
- foreach ($blocks as $block) {
- list($ai, $bj, $size) = $block;
- $tag = '';
- if ($i < $ai && $j < $bj) {
- $tag = 'replace';
- } elseif ($i < $ai) {
- $tag = 'delete';
- } elseif ($j < $bj) {
- $tag = 'insert';
- }
-
- if ($tag) {
- $this->opCodes[] = array(
- $tag,
- $i,
- $ai,
- $j,
- $bj
- );
- }
-
- $i = $ai + $size;
- $j = $bj + $size;
-
- if ($size) {
- $this->opCodes[] = array(
- 'equal',
- $ai,
- $i,
- $bj,
- $j
- );
- }
- }
- return $this->opCodes;
- }
-
- /**
- * Return a series of nested arrays containing different groups of generated
- * op codes for the differences between the strings with up to $context lines
- * of surrounding content.
- *
- * Essentially what happens here is any big equal blocks of strings are stripped
- * out, the smaller subsets of changes are then arranged in to their groups.
- * This means that the sequence matcher and diffs do not need to include the full
- * content of the different files but can still provide context as to where the
- * changes are.
- *
- * @param int $context The number of lines of context to provide around the groups.
- * @return array Nested array of all of the grouped op codes.
- */
- public function getGroupedOpcodes($context = 3) : array
- {
- $opCodes = $this->getOpCodes();
- if (empty($opCodes)) {
- $opCodes = array(
- array(
- 'equal',
- 0,
- 1,
- 0,
- 1
- )
- );
- }
-
- if ($opCodes['0']['0'] == 'equal') {
- $opCodes['0'] = array(
- $opCodes['0']['0'],
- max($opCodes['0']['1'], $opCodes['0']['2'] - $context),
- $opCodes['0']['2'],
- max($opCodes['0']['3'], $opCodes['0']['4'] - $context),
- $opCodes['0']['4']
- );
- }
-
- $lastItem = count($opCodes) - 1;
- if ($opCodes[$lastItem]['0'] == 'equal') {
- list($tag, $i1, $i2, $j1, $j2) = $opCodes[$lastItem];
- $opCodes[$lastItem] = array(
- $tag,
- $i1,
- min($i2, $i1 + $context),
- $j1,
- min($j2, $j1 + $context)
- );
- }
-
- $maxRange = $context * 2;
- $groups = array();
- $group = array();
-
- foreach ($opCodes as $code) {
- list($tag, $i1, $i2, $j1, $j2) = $code;
- if ($tag == 'equal' && $i2 - $i1 > $maxRange) {
- $group[] = array(
- $tag,
- $i1,
- min($i2, $i1 + $context),
- $j1,
- min($j2, $j1 + $context)
- );
- $groups[] = $group;
- $group = array();
- $i1 = max($i1, $i2 - $context);
- $j1 = max($j1, $j2 - $context);
- }
- $group[] = array(
- $tag,
- $i1,
- $i2,
- $j1,
- $j2
- );
- }
-
- if (!empty($group) && !(count($group) == 1 && $group['0']['0'] == 'equal')) {
- $groups[] = $group;
- }
-
- return $groups;
- }
-
- /**
- * Return a measure of the similarity between the two sequences.
- * This will be a float value between 0 and 1.
- *
- * Out of all of the ratio calculation functions, this is the most
- * expensive to call if getMatchingBlocks or getOpCodes is yet to be
- * called.
- *
- * The ratio is calculated as (2 * number of matches) / total number of
- * elements in both sequences.
- *
- * @return float The calculated ratio.
- */
- public function ratio() : float
- {
- $matches = array_reduce($this->getMatchingBlocks(), array($this, 'ratioReduce'), 0);
- return $this->calculateRatio($matches, count($this->a) + count($this->b));
- }
-
- /**
- * Helper function to calculate the number of matches for Ratio().
- *
- * @param int $sum The running total for the number of matches.
- * @param array $triple Array containing the matching block triple to add to the running total.
- * @return int The new running total for the number of matches.
- */
- private function ratioReduce($sum, $triple) : int
- {
- return $sum + ($triple[count($triple) - 1]);
- }
-
- /**
- * Helper function for calculating the ratio to measure similarity for the strings.
- * The ratio is defined as being 2 * (number of matches / total length)
- *
- * @param int $matches The number of matches in the two strings.
- * @param int $length The length of the two strings.
- * @return float The calculated ratio.
- */
- private function calculateRatio($matches, $length = 0) : float
- {
- if ($length) {
- return 2 * ($matches / $length);
- }
- return 1;
- }
-
- /**
- * Helper function that provides the ability to return the value for a key
- * in an array of it exists, or if it doesn't then return a default value.
- * Essentially cleaner than doing a series of if (isset()) {} else {} calls.
- *
- * @param array $array The array to search.
- * @param string|int $key The key to check that exists.
- * @param mixed $default The value to return as the default value if the key doesn't exist.
- * @return mixed The value from the array if the key exists or otherwise the default.
- */
- private function arrayGetDefault($array, $key, $default)
- {
- if (isset($array[$key])) {
- return $array[$key];
- }
- return $default;
- }
-
- /**
- * Sort an array by the nested arrays it contains. Helper function for getMatchingBlocks
- *
- * @param array $a First array to compare.
- * @param array $b Second array to compare.
- * @return int -1, 0 or 1, as expected by the usort function.
- */
- private function tupleSort($a, $b) : int
- {
- $max = max(count($a), count($b));
- for ($i = 0; $i < $max; ++$i) {
- if ($a[$i] < $b[$i]) {
- return -1;
- } elseif ($a[$i] > $b[$i]) {
- return 1;
- }
- }
-
- if (count($a) == count($b)) {
- return 0;
- }
- if (count($a) < count($b)) {
- return -1;
- }
- return 1;
- }
+ /**
+ * @var string|array Either a string or an array containing a callback function to determine
+ * if a line is "junk" or not.
+ */
+ private $junkCallback = null;
+
+ /**
+ * @var array The first sequence to compare against.
+ */
+ private $a = array();
+
+ /**
+ * @var array The second sequence.
+ */
+ private $b = array();
+
+ /**
+ * @var array Array of characters that are considered junk from the second sequence. Characters are the array key.
+ */
+ private $junkDict = array();
+
+ /**
+ * @var array Array of indices that do not contain junk elements.
+ */
+ private $b2j = array();
+
+ /**
+ * @var array
+ */
+ private $options = array();
+
+ /**
+ * @var null|array
+ */
+ private $opCodes;
+
+ /**
+ * @var null|array
+ */
+ private $matchingBlocks;
+
+ /**
+ * @var null|array
+ */
+ private $fullBCount;
+
+ /**
+ * @var array
+ */
+ private $defaultOptions = array(
+ 'ignoreNewLines' => false,
+ 'ignoreWhitespace' => false,
+ 'ignoreCase' => false
+ );
+
+ /**
+ * The constructor. With the sequences being passed, they'll be set for the
+ * sequence matcher and it will perform a basic cleanup & calculate junk
+ * elements.
+ *
+ * @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 array $options
+ * @param string|array|null $junkCallback Either an array or string that references a callback function
+ * (if there is one) to determine 'junk' characters.
+ */
+ public function __construct($a, $b, $options, $junkCallback = null)
+ {
+ $this->a = array();
+ $this->b = array();
+ $this->junkCallback = $junkCallback;
+ $this->setOptions($options);
+ $this->setSequences($a, $b);
+ }
+
+ /**
+ * @param array $options
+ */
+ public function setOptions($options)
+ {
+ $this->options = array_merge($this->defaultOptions, $options);
+ }
+
+ /**
+ * Set the first and second sequences to use with the sequence matcher.
+ *
+ * @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.
+ */
+ public function setSequences($a, $b)
+ {
+ $this->setSeq1($a);
+ $this->setSeq2($b);
+ }
+
+ /**
+ * Set the first sequence ($a) and reset any internal caches to indicate that
+ * when calling the calculation methods, we need to recalculate them.
+ *
+ * @param string|array $a The sequence to set as the first sequence.
+ */
+ public function setSeq1($a)
+ {
+ if (!is_array($a)) {
+ $a = str_split($a);
+ }
+ if ($a == $this->a) {
+ return;
+ }
+
+ $this->a = $a;
+ $this->matchingBlocks = null;
+ $this->opCodes = null;
+ }
+
+ /**
+ * Set the second sequence ($b) and reset any internal caches to indicate that
+ * when calling the calculation methods, we need to recalculate them.
+ *
+ * @param string|array $b The sequence to set as the second sequence.
+ */
+ public function setSeq2($b)
+ {
+ if (!is_array($b)) {
+ $b = str_split($b);
+ }
+ if ($b == $this->b) {
+ return;
+ }
+
+ $this->b = $b;
+ $this->matchingBlocks = null;
+ $this->opCodes = null;
+ $this->fullBCount = null;
+ $this->chainB();
+ }
+
+ /**
+ * Generate the internal arrays containing the list of junk and non-junk
+ * characters for the second ($b) sequence.
+ */
+ private function chainB()
+ {
+ $length = count($this->b);
+ $this->b2j = array();
+ $popularDict = array();
+
+ for ($i = 0; $i < $length; ++$i) {
+ $char = $this->b[$i];
+ if (isset($this->b2j[$char])) {
+ if ($length >= 200 && count($this->b2j[$char]) * 100 > $length) {
+ $popularDict[$char] = 1;
+ unset($this->b2j[$char]);
+ } else {
+ $this->b2j[$char][] = $i;
+ }
+ } else {
+ $this->b2j[$char] = array(
+ $i
+ );
+ }
+ }
+
+ // Remove leftovers
+ foreach (array_keys($popularDict) as $char) {
+ unset($this->b2j[$char]);
+ }
+
+ $this->junkDict = array();
+ if (is_callable($this->junkCallback)) {
+ foreach (array_keys($popularDict) as $char) {
+ if (call_user_func($this->junkCallback, $char)) {
+ $this->junkDict[$char] = 1;
+ unset($popularDict[$char]);
+ }
+ }
+
+ foreach (array_keys($this->b2j) as $char) {
+ if (call_user_func($this->junkCallback, $char)) {
+ $this->junkDict[$char] = 1;
+ unset($this->b2j[$char]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if a particular character is in the junk dictionary
+ * for the list of junk characters.
+ *
+ * @param string $b
+ * @return bool $b True if the character is considered junk. False if not.
+ */
+ private function isBJunk($b) : bool
+ {
+ if (isset($this->junkDict[$b])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Find the longest matching block in the two sequences, as defined by the
+ * lower and upper constraints for each sequence. (for the first sequence,
+ * $alo - $ahi and for the second sequence, $blo - $bhi)
+ *
+ * Essentially, of all of the maximal matching blocks, return the one that
+ * starts earliest in $a, and all of those maximal matching blocks that
+ * start earliest in $a, return the one that starts earliest in $b.
+ *
+ * If the junk callback is defined, do the above but with the restriction
+ * that the junk element appears in the block. Extend it as far as possible
+ * by matching only junk elements in both $a and $b.
+ *
+ * @param int $alo The lower constraint for the first sequence.
+ * @param int $ahi The upper constraint for the first sequence.
+ * @param int $blo The lower constraint for the second sequence.
+ * @param int $bhi The upper constraint for the second sequence.
+ * @return array Array containing the longest match that includes the starting position in $a,
+ * start in $b and the length/size.
+ */
+ public function findLongestMatch($alo, $ahi, $blo, $bhi) : array
+ {
+ $a = $this->a;
+ $b = $this->b;
+
+ $bestI = $alo;
+ $bestJ = $blo;
+ $bestSize = 0;
+
+ $j2Len = array();
+ $nothing = array();
+
+ for ($i = $alo; $i < $ahi; ++$i) {
+ $newJ2Len = array();
+ $jDict = $this->arrayGetDefault($this->b2j, $a[$i], $nothing);
+ foreach ($jDict as $jKey => $j) {
+ if ($j < $blo) {
+ continue;
+ } elseif ($j >= $bhi) {
+ break;
+ }
+
+ $k = $this->arrayGetDefault($j2Len, $j -1, 0) + 1;
+ $newJ2Len[$j] = $k;
+ if ($k > $bestSize) {
+ $bestI = $i - $k + 1;
+ $bestJ = $j - $k + 1;
+ $bestSize = $k;
+ }
+ }
+
+ $j2Len = $newJ2Len;
+ }
+
+ while ($bestI > $alo &&
+ $bestJ > $blo &&
+ !$this->isBJunk($b[$bestJ - 1]) &&
+ !$this->linesAreDifferent($bestI - 1, $bestJ - 1)
+ ) {
+ --$bestI;
+ --$bestJ;
+ ++$bestSize;
+ }
+
+ while ($bestI + $bestSize < $ahi &&
+ ($bestJ + $bestSize) < $bhi &&
+ !$this->isBJunk($b[$bestJ + $bestSize]) &&
+ !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)) {
+ ++$bestSize;
+ }
+
+ while ($bestI > $alo &&
+ $bestJ > $blo &&
+ $this->isBJunk($b[$bestJ - 1]) &&
+ !$this->linesAreDifferent($bestI - 1, $bestJ - 1)
+ ) {
+ --$bestI;
+ --$bestJ;
+ ++$bestSize;
+ }
+
+ while ($bestI + $bestSize < $ahi &&
+ $bestJ + $bestSize < $bhi &&
+ $this->isBJunk($b[$bestJ + $bestSize]) &&
+ !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)
+ ) {
+ ++$bestSize;
+ }
+
+ return array(
+ $bestI,
+ $bestJ,
+ $bestSize
+ );
+ }
+
+ /**
+ * Check if the two lines at the given indexes are different or not.
+ *
+ * @param int $aIndex Line number to check against in a.
+ * @param int $bIndex Line number to check against in b.
+ * @return bool True if the lines are different and false if not.
+ */
+ public function linesAreDifferent($aIndex, $bIndex) : bool
+ {
+ $lineA = $this->a[$aIndex];
+ $lineB = $this->b[$bIndex];
+
+ if ($this->options['ignoreWhitespace']) {
+ $replace = array("\t", ' ');
+ $lineA = str_replace($replace, '', $lineA);
+ $lineB = str_replace($replace, '', $lineB);
+ }
+
+ if ($this->options['ignoreCase']) {
+ $lineA = strtolower($lineA);
+ $lineB = strtolower($lineB);
+ }
+
+ if ($lineA != $lineB) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return a nested set of arrays for all of the matching sub-sequences
+ * in the strings $a and $b.
+ *
+ * Each block contains the lower constraint of the block in $a, the lower
+ * constraint of the block in $b and finally the number of lines that the
+ * block continues for.
+ *
+ * @return array Nested array of the matching blocks, as described by the function.
+ */
+ public function getMatchingBlocks() : array
+ {
+ if (!empty($this->matchingBlocks)) {
+ return $this->matchingBlocks;
+ }
+
+ $aLength = count($this->a);
+ $bLength = count($this->b);
+
+ $queue = array(
+ array(
+ 0,
+ $aLength,
+ 0,
+ $bLength
+ )
+ );
+
+ $matchingBlocks = array();
+ while (!empty($queue)) {
+ list($alo, $ahi, $blo, $bhi) = array_pop($queue);
+ $x = $this->findLongestMatch($alo, $ahi, $blo, $bhi);
+ list($i, $j, $k) = $x;
+ if ($k) {
+ $matchingBlocks[] = $x;
+ if ($alo < $i && $blo < $j) {
+ $queue[] = array(
+ $alo,
+ $i,
+ $blo,
+ $j
+ );
+ }
+
+ if ($i + $k < $ahi && $j + $k < $bhi) {
+ $queue[] = array(
+ $i + $k,
+ $ahi,
+ $j + $k,
+ $bhi
+ );
+ }
+ }
+ }
+
+ usort($matchingBlocks, array($this, 'tupleSort'));
+
+ $i1 = 0;
+ $j1 = 0;
+ $k1 = 0;
+ $nonAdjacent = array();
+ foreach ($matchingBlocks as $block) {
+ list($i2, $j2, $k2) = $block;
+ if ($i1 + $k1 == $i2 && $j1 + $k1 == $j2) {
+ $k1 += $k2;
+ } else {
+ if ($k1) {
+ $nonAdjacent[] = array(
+ $i1,
+ $j1,
+ $k1
+ );
+ }
+
+ $i1 = $i2;
+ $j1 = $j2;
+ $k1 = $k2;
+ }
+ }
+
+ if ($k1) {
+ $nonAdjacent[] = array(
+ $i1,
+ $j1,
+ $k1
+ );
+ }
+
+ $nonAdjacent[] = array(
+ $aLength,
+ $bLength,
+ 0
+ );
+
+ $this->matchingBlocks = $nonAdjacent;
+ return $this->matchingBlocks;
+ }
+
+ /**
+ * Return a list of all of the op codes for the differences between the
+ * two strings.
+ *
+ * The nested array returned contains an array describing the op code
+ * which includes:
+ * 0 - The type of tag (as described below) for the op code.
+ * 1 - The beginning line in the first sequence.
+ * 2 - The end line in the first sequence.
+ * 3 - The beginning line in the second sequence.
+ * 4 - The end line in the second sequence.
+ *
+ * The different types of tags include:
+ * replace - The string from $i1 to $i2 in $a should be replaced by
+ * the string in $b from $j1 to $j2.
+ * delete - The string in $a from $i1 to $j2 should be deleted.
+ * insert - The string in $b from $j1 to $j2 should be inserted at
+ * $i1 in $a.
+ * equal - The two strings with the specified ranges are equal.
+ *
+ * @return array Array of the opcodes describing the differences between the strings.
+ */
+ public function getOpCodes() : array
+ {
+ if (!empty($this->opCodes)) {
+ return $this->opCodes;
+ }
+
+ $i = 0;
+ $j = 0;
+ $this->opCodes = array();
+
+ $blocks = $this->getMatchingBlocks();
+ foreach ($blocks as $block) {
+ list($ai, $bj, $size) = $block;
+ $tag = '';
+ if ($i < $ai && $j < $bj) {
+ $tag = 'replace';
+ } elseif ($i < $ai) {
+ $tag = 'delete';
+ } elseif ($j < $bj) {
+ $tag = 'insert';
+ }
+
+ if ($tag) {
+ $this->opCodes[] = array(
+ $tag,
+ $i,
+ $ai,
+ $j,
+ $bj
+ );
+ }
+
+ $i = $ai + $size;
+ $j = $bj + $size;
+
+ if ($size) {
+ $this->opCodes[] = array(
+ 'equal',
+ $ai,
+ $i,
+ $bj,
+ $j
+ );
+ }
+ }
+ return $this->opCodes;
+ }
+
+ /**
+ * Return a series of nested arrays containing different groups of generated
+ * op codes for the differences between the strings with up to $context lines
+ * of surrounding content.
+ *
+ * Essentially what happens here is any big equal blocks of strings are stripped
+ * out, the smaller subsets of changes are then arranged in to their groups.
+ * This means that the sequence matcher and diffs do not need to include the full
+ * content of the different files but can still provide context as to where the
+ * changes are.
+ *
+ * @param int $context The number of lines of context to provide around the groups.
+ * @return array Nested array of all of the grouped op codes.
+ */
+ public function getGroupedOpcodes($context = 3) : array
+ {
+ $opCodes = $this->getOpCodes();
+ if (empty($opCodes)) {
+ $opCodes = array(
+ array(
+ 'equal',
+ 0,
+ 1,
+ 0,
+ 1
+ )
+ );
+ }
+
+ if ($opCodes['0']['0'] == 'equal') {
+ $opCodes['0'] = array(
+ $opCodes['0']['0'],
+ max($opCodes['0']['1'], $opCodes['0']['2'] - $context),
+ $opCodes['0']['2'],
+ max($opCodes['0']['3'], $opCodes['0']['4'] - $context),
+ $opCodes['0']['4']
+ );
+ }
+
+ $lastItem = count($opCodes) - 1;
+ if ($opCodes[$lastItem]['0'] == 'equal') {
+ list($tag, $i1, $i2, $j1, $j2) = $opCodes[$lastItem];
+ $opCodes[$lastItem] = array(
+ $tag,
+ $i1,
+ min($i2, $i1 + $context),
+ $j1,
+ min($j2, $j1 + $context)
+ );
+ }
+
+ $maxRange = $context * 2;
+ $groups = array();
+ $group = array();
+
+ foreach ($opCodes as $code) {
+ list($tag, $i1, $i2, $j1, $j2) = $code;
+ if ($tag == 'equal' && $i2 - $i1 > $maxRange) {
+ $group[] = array(
+ $tag,
+ $i1,
+ min($i2, $i1 + $context),
+ $j1,
+ min($j2, $j1 + $context)
+ );
+ $groups[] = $group;
+ $group = array();
+ $i1 = max($i1, $i2 - $context);
+ $j1 = max($j1, $j2 - $context);
+ }
+ $group[] = array(
+ $tag,
+ $i1,
+ $i2,
+ $j1,
+ $j2
+ );
+ }
+
+ if (!empty($group) && !(count($group) == 1 && $group['0']['0'] == 'equal')) {
+ $groups[] = $group;
+ }
+
+ return $groups;
+ }
+
+ /**
+ * Return a measure of the similarity between the two sequences.
+ * This will be a float value between 0 and 1.
+ *
+ * Out of all of the ratio calculation functions, this is the most
+ * expensive to call if getMatchingBlocks or getOpCodes is yet to be
+ * called.
+ *
+ * The ratio is calculated as (2 * number of matches) / total number of
+ * elements in both sequences.
+ *
+ * @return float The calculated ratio.
+ */
+ public function ratio() : float
+ {
+ $matches = array_reduce($this->getMatchingBlocks(), array($this, 'ratioReduce'), 0);
+ return $this->calculateRatio($matches, count($this->a) + count($this->b));
+ }
+
+ /**
+ * Helper function to calculate the number of matches for Ratio().
+ *
+ * @param int $sum The running total for the number of matches.
+ * @param array $triple Array containing the matching block triple to add to the running total.
+ * @return int The new running total for the number of matches.
+ */
+ private function ratioReduce($sum, $triple) : int
+ {
+ return $sum + ($triple[count($triple) - 1]);
+ }
+
+ /**
+ * Helper function for calculating the ratio to measure similarity for the strings.
+ * The ratio is defined as being 2 * (number of matches / total length)
+ *
+ * @param int $matches The number of matches in the two strings.
+ * @param int $length The length of the two strings.
+ * @return float The calculated ratio.
+ */
+ private function calculateRatio($matches, $length = 0) : float
+ {
+ if ($length) {
+ return 2 * ($matches / $length);
+ }
+ return 1;
+ }
+
+ /**
+ * Helper function that provides the ability to return the value for a key
+ * in an array of it exists, or if it doesn't then return a default value.
+ * Essentially cleaner than doing a series of if (isset()) {} else {} calls.
+ *
+ * @param array $array The array to search.
+ * @param string|int $key The key to check that exists.
+ * @param mixed $default The value to return as the default value if the key doesn't exist.
+ * @return mixed The value from the array if the key exists or otherwise the default.
+ */
+ private function arrayGetDefault($array, $key, $default)
+ {
+ if (isset($array[$key])) {
+ return $array[$key];
+ }
+ return $default;
+ }
+
+ /**
+ * Sort an array by the nested arrays it contains. Helper function for getMatchingBlocks
+ *
+ * @param array $a First array to compare.
+ * @param array $b Second array to compare.
+ * @return int -1, 0 or 1, as expected by the usort function.
+ */
+ private function tupleSort($a, $b) : int
+ {
+ $max = max(count($a), count($b));
+ for ($i = 0; $i < $max; ++$i) {
+ if ($a[$i] < $b[$i]) {
+ return -1;
+ } elseif ($a[$i] > $b[$i]) {
+ return 1;
+ }
+ }
+
+ if (count($a) == count($b)) {
+ return 0;
+ }
+ if (count($a) < count($b)) {
+ return -1;
+ }
+ return 1;
+ }
}
diff --git a/phpcs.xml b/phpcs.xml
index 536e8906..3b6ba4eb 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,8 +1,5 @@
php-diff
-
-
-
-
+
diff --git a/tests/Diff/Renderer/ArrayTest.php b/tests/Diff/Renderer/ArrayTest.php
index 424aaa6b..f83392a8 100644
--- a/tests/Diff/Renderer/ArrayTest.php
+++ b/tests/Diff/Renderer/ArrayTest.php
@@ -12,78 +12,78 @@
class ArrayTest extends TestCase
{
- /**
- * ArrayTest constructor.
- * @param null $name
- * @param array $data
- * @param string $dataName
- */
- public function __construct($name = null, array $data = [], $dataName = '')
- {
- require "../../../lib/Autoloader.php";
- new \jblond\Autoloader();
- parent::__construct($name, $data, $dataName);
- }
+ /**
+ * ArrayTest constructor.
+ * @param null $name
+ * @param array $data
+ * @param string $dataName
+ */
+ public function __construct($name = null, array $data = [], $dataName = '')
+ {
+ require "../../../lib/Autoloader.php";
+ new \jblond\Autoloader();
+ parent::__construct($name, $data, $dataName);
+ }
- /**
- *
- */
- public function testRenderSimpleDelete()
- {
- $htmlRenderer = new HtmlArray();
- $htmlRenderer->diff = new \jblond\Diff(
- array('a'),
- array()
- );
- $result = $htmlRenderer->render();
- static::assertEquals(array(
- array(
- array(
- 'tag' => 'delete',
- 'base' => array(
- 'offset' => 0,
- 'lines' => array(
- 'a'
- )
- ),
- 'changed' => array(
- 'offset' => 0,
- 'lines' => array()
- )
- )
- )
- ), $result);
- }
+ /**
+ *
+ */
+ public function testRenderSimpleDelete()
+ {
+ $htmlRenderer = new HtmlArray();
+ $htmlRenderer->diff = new \jblond\Diff(
+ array('a'),
+ array()
+ );
+ $result = $htmlRenderer->render();
+ static::assertEquals(array(
+ array(
+ array(
+ 'tag' => 'delete',
+ 'base' => array(
+ 'offset' => 0,
+ 'lines' => array(
+ 'a'
+ )
+ ),
+ 'changed' => array(
+ 'offset' => 0,
+ 'lines' => array()
+ )
+ )
+ )
+ ), $result);
+ }
- /**
- *
- */
- public function testRenderFixesSpaces()
- {
- $htmlRenderer = new HtmlArray();
- $htmlRenderer->diff = new \jblond\Diff(
- array(' a'),
- array('a')
- );
- $result = $htmlRenderer->render();
- static::assertEquals(array(
- array(
- array(
- 'tag' => 'replace',
- 'base' => array(
- 'offset' => 0,
- 'lines' => array(
- ' a',
- )
- ),
- 'changed' => array(
- 'offset' => 0,
- 'lines' => array(
- 'a'
- )
- )
- )
- )
- ), $result);
- }
+ /**
+ *
+ */
+ public function testRenderFixesSpaces()
+ {
+ $htmlRenderer = new HtmlArray();
+ $htmlRenderer->diff = new \jblond\Diff(
+ array(' a'),
+ array('a')
+ );
+ $result = $htmlRenderer->render();
+ static::assertEquals(array(
+ array(
+ array(
+ 'tag' => 'replace',
+ 'base' => array(
+ 'offset' => 0,
+ 'lines' => array(
+ ' a',
+ )
+ ),
+ 'changed' => array(
+ 'offset' => 0,
+ 'lines' => array(
+ 'a'
+ )
+ )
+ )
+ )
+ ), $result);
+ }
}