PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/sebastian/diff/src/Differ.php

https://gitlab.com/techniconline/kmc
PHP | 257 lines | 150 code | 43 blank | 64 comment | 30 complexity | dc471bc6a1f177ea9c34fa07c6ccf1c2 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Diff package.
  4. *
  5. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace SebastianBergmann\Diff;
  11. use SebastianBergmann\Diff\LCS\LongestCommonSubsequence;
  12. use SebastianBergmann\Diff\LCS\TimeEfficientImplementation;
  13. use SebastianBergmann\Diff\LCS\MemoryEfficientImplementation;
  14. /**
  15. * Diff implementation.
  16. *
  17. * @package Diff
  18. * @author Sebastian Bergmann <sebastian@phpunit.de>
  19. * @author Kore Nordmann <mail@kore-nordmann.de>
  20. * @copyright Sebastian Bergmann <sebastian@phpunit.de>
  21. * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
  22. * @link http://www.github.com/sebastianbergmann/diff
  23. */
  24. class Differ
  25. {
  26. /**
  27. * @var string
  28. */
  29. private $header;
  30. /**
  31. * @param string $header
  32. */
  33. public function __construct($header = "--- Original\n+++ New\n")
  34. {
  35. $this->header = $header;
  36. }
  37. /**
  38. * Returns the diff between two arrays or strings as string.
  39. *
  40. * @param array|string $from
  41. * @param array|string $to
  42. * @param LongestCommonSubsequence $lcs
  43. * @return string
  44. */
  45. public function diff($from, $to, LongestCommonSubsequence $lcs = null)
  46. {
  47. if (!is_array($from) && !is_string($from)) {
  48. $from = (string)$from;
  49. }
  50. if (!is_array($to) && !is_string($to)) {
  51. $to = (string)$to;
  52. }
  53. $buffer = $this->header;
  54. $diff = $this->diffToArray($from, $to, $lcs);
  55. $inOld = false;
  56. $i = 0;
  57. $old = array();
  58. foreach ($diff as $line) {
  59. if ($line[1] === 0 /* OLD */) {
  60. if ($inOld === false) {
  61. $inOld = $i;
  62. }
  63. } elseif ($inOld !== false) {
  64. if (($i - $inOld) > 5) {
  65. $old[$inOld] = $i - 1;
  66. }
  67. $inOld = false;
  68. }
  69. ++$i;
  70. }
  71. $start = isset($old[0]) ? $old[0] : 0;
  72. $end = count($diff);
  73. if ($tmp = array_search($end, $old)) {
  74. $end = $tmp;
  75. }
  76. $newChunk = true;
  77. for ($i = $start; $i < $end; $i++) {
  78. if (isset($old[$i])) {
  79. $buffer .= "\n";
  80. $newChunk = true;
  81. $i = $old[$i];
  82. }
  83. if ($newChunk) {
  84. $buffer .= "@@ @@\n";
  85. $newChunk = false;
  86. }
  87. if ($diff[$i][1] === 1 /* ADDED */) {
  88. $buffer .= '+' . $diff[$i][0] . "\n";
  89. } elseif ($diff[$i][1] === 2 /* REMOVED */) {
  90. $buffer .= '-' . $diff[$i][0] . "\n";
  91. } else {
  92. $buffer .= ' ' . $diff[$i][0] . "\n";
  93. }
  94. }
  95. return $buffer;
  96. }
  97. /**
  98. * Returns the diff between two arrays or strings as array.
  99. *
  100. * Each array element contains two elements:
  101. * - [0] => string $token
  102. * - [1] => 2|1|0
  103. *
  104. * - 2: REMOVED: $token was removed from $from
  105. * - 1: ADDED: $token was added to $from
  106. * - 0: OLD: $token is not changed in $to
  107. *
  108. * @param array|string $from
  109. * @param array|string $to
  110. * @param LongestCommonSubsequence $lcs
  111. * @return array
  112. */
  113. public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null)
  114. {
  115. preg_match_all('(\r\n|\r|\n)', $from, $fromMatches);
  116. preg_match_all('(\r\n|\r|\n)', $to, $toMatches);
  117. if (is_string($from)) {
  118. $from = preg_split('(\r\n|\r|\n)', $from);
  119. }
  120. if (is_string($to)) {
  121. $to = preg_split('(\r\n|\r|\n)', $to);
  122. }
  123. $start = array();
  124. $end = array();
  125. $fromLength = count($from);
  126. $toLength = count($to);
  127. $length = min($fromLength, $toLength);
  128. for ($i = 0; $i < $length; ++$i) {
  129. if ($from[$i] === $to[$i]) {
  130. $start[] = $from[$i];
  131. unset($from[$i], $to[$i]);
  132. } else {
  133. break;
  134. }
  135. }
  136. $length -= $i;
  137. for ($i = 1; $i < $length; ++$i) {
  138. if ($from[$fromLength - $i] === $to[$toLength - $i]) {
  139. array_unshift($end, $from[$fromLength - $i]);
  140. unset($from[$fromLength - $i], $to[$toLength - $i]);
  141. } else {
  142. break;
  143. }
  144. }
  145. if ($lcs === null) {
  146. $lcs = $this->selectLcsImplementation($from, $to);
  147. }
  148. $common = $lcs->calculate(array_values($from), array_values($to));
  149. $diff = array();
  150. if (isset($fromMatches[0]) && $toMatches[0] &&
  151. count($fromMatches[0]) === count($toMatches[0]) &&
  152. $fromMatches[0] !== $toMatches[0]
  153. ) {
  154. $diff[] = array(
  155. '#Warning: Strings contain different line endings!', 0
  156. );
  157. }
  158. foreach ($start as $token) {
  159. $diff[] = array($token, 0 /* OLD */);
  160. }
  161. reset($from);
  162. reset($to);
  163. foreach ($common as $token) {
  164. while ((($fromToken = reset($from)) !== $token)) {
  165. $diff[] = array(array_shift($from), 2 /* REMOVED */);
  166. }
  167. while ((($toToken = reset($to)) !== $token)) {
  168. $diff[] = array(array_shift($to), 1 /* ADDED */);
  169. }
  170. $diff[] = array($token, 0 /* OLD */);
  171. array_shift($from);
  172. array_shift($to);
  173. }
  174. while (($token = array_shift($from)) !== null) {
  175. $diff[] = array($token, 2 /* REMOVED */);
  176. }
  177. while (($token = array_shift($to)) !== null) {
  178. $diff[] = array($token, 1 /* ADDED */);
  179. }
  180. foreach ($end as $token) {
  181. $diff[] = array($token, 0 /* OLD */);
  182. }
  183. return $diff;
  184. }
  185. /**
  186. * @param array $from
  187. * @param array $to
  188. * @return LongestCommonSubsequence
  189. */
  190. private function selectLcsImplementation(array $from, array $to)
  191. {
  192. // We do not want to use the time-efficient implementation if its memory
  193. // footprint will probably exceed this value. Note that the footprint
  194. // calculation is only an estimation for the matrix and the LCS method
  195. // will typically allocate a bit more memory than this.
  196. $memoryLimit = 100 * 1024 * 1024;
  197. if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) {
  198. return new MemoryEfficientImplementation;
  199. }
  200. return new TimeEfficientImplementation;
  201. }
  202. /**
  203. * Calculates the estimated memory footprint for the DP-based method.
  204. *
  205. * @param array $from
  206. * @param array $to
  207. * @return integer
  208. */
  209. private function calculateEstimatedFootprint(array $from, array $to)
  210. {
  211. $itemSize = PHP_INT_SIZE == 4 ? 76 : 144;
  212. return $itemSize * pow(min(count($from), count($to)), 2);
  213. }
  214. }