Text_Diff
[ class tree: Text_Diff ] [ index: Text_Diff ] [ all elements ]

Source for file Diff.php

Documentation is available at Diff.php

  1. <?php
  2. /**
  3.  * Text_Diff
  4.  *
  5.  * General API for generating and formatting diffs - the differences between
  6.  * two sequences of strings.
  7.  *
  8.  * The PHP diff code used in this package was originally written by Geoffrey
  9.  * T. Dairiki and is used with his permission.
  10.  *
  11.  * $Horde: framework/Text_Diff/Diff.php,v 1.16 2005/12/27 22:15:21 jan Exp $
  12.  *
  13.  * @package Text_Diff
  14.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  15.  */
  16. class Text_Diff {
  17.  
  18.     /**
  19.      * Array of changes.
  20.      *
  21.      * @var array 
  22.      */
  23.     var $_edits;
  24.  
  25.     /**
  26.      * Computes diffs between sequences of strings.
  27.      *
  28.      * @param array $from_lines  An array of strings.  Typically these are
  29.      *                            lines from a file.
  30.      * @param array $to_lines    An array of strings.
  31.      */
  32.     function Text_Diff($engine$params)
  33.     {
  34.         // Backward compatibility workaround.
  35.         if (!is_string($engine)) {
  36.             $params = array($engine$params);
  37.             $engine 'auto';
  38.         }
  39.  
  40.         if ($engine == 'auto'{
  41.             $engine extension_loaded('xdiff''xdiff' 'native';
  42.         }
  43.         $engine basename($engine);
  44.  
  45.         require_once 'Text/Diff/Engine/' $engine '.php';
  46.         $class 'Text_Diff_Engine_' $engine;
  47.         $diff_engine &new $class();
  48.  
  49.         $this->_edits call_user_func_array(array($diff_engine'diff')$params);
  50.     }
  51.  
  52.     /**
  53.      * Returns the array of differences.
  54.      */
  55.     function getDiff()
  56.     {
  57.         return $this->_edits;
  58.     }
  59.  
  60.     /**
  61.      * Computes a reversed diff.
  62.      *
  63.      * Example:
  64.      * <code>
  65.      * $diff = &new Text_Diff($lines1, $lines2);
  66.      * $rev = $diff->reverse();
  67.      * </code>
  68.      *
  69.      * @return Text_Diff  A Diff object representing the inverse of the
  70.      *                     original diff.  Note that we purposely don't return a
  71.      *                     reference here, since this essentially is a clone()
  72.      *                     method.
  73.      */
  74.     function reverse()
  75.     {
  76.         if (version_compare(zend_version()'2''>')) {
  77.             $rev = clone($obj);
  78.         else {
  79.             $rev $this;
  80.         }
  81.         $rev->_edits = array();
  82.         foreach ($this->_edits as $edit{
  83.             $rev->_edits[$edit->reverse();
  84.         }
  85.         return $rev;
  86.     }
  87.  
  88.     /**
  89.      * Checks for an empty diff.
  90.      *
  91.      * @return boolean  True if two sequences were identical.
  92.      */
  93.     function isEmpty()
  94.     {
  95.         foreach ($this->_edits as $edit{
  96.             if (!is_a($edit'Text_Diff_Op_copy')) {
  97.                 return false;
  98.             }
  99.         }
  100.         return true;
  101.     }
  102.  
  103.     /**
  104.      * Computes the length of the Longest Common Subsequence (LCS).
  105.      *
  106.      * This is mostly for diagnostic purposes.
  107.      *
  108.      * @return integer  The length of the LCS.
  109.      */
  110.     function lcs()
  111.     {
  112.         $lcs = 0;
  113.         foreach ($this->_edits as $edit{
  114.             if (is_a($edit'Text_Diff_Op_copy')) {
  115.                 $lcs += count($edit->orig);
  116.             }
  117.         }
  118.         return $lcs;
  119.     }
  120.  
  121.     /**
  122.      * Gets the original set of lines.
  123.      *
  124.      * This reconstructs the $from_lines parameter passed to the constructor.
  125.      *
  126.      * @return array  The original sequence of strings.
  127.      */
  128.     function getOriginal()
  129.     {
  130.         $lines = array();
  131.         foreach ($this->_edits as $edit{
  132.             if ($edit->orig{
  133.                 array_splice($linescount($lines)0$edit->orig);
  134.             }
  135.         }
  136.         return $lines;
  137.     }
  138.  
  139.     /**
  140.      * Gets the final set of lines.
  141.      *
  142.      * This reconstructs the $to_lines parameter passed to the constructor.
  143.      *
  144.      * @return array  The sequence of strings.
  145.      */
  146.     function getFinal()
  147.     {
  148.         $lines = array();
  149.         foreach ($this->_edits as $edit{
  150.             if ($edit->final{
  151.                 array_splice($linescount($lines)0$edit->final);
  152.             }
  153.         }
  154.         return $lines;
  155.     }
  156.  
  157.     /**
  158.      * Removes trailing newlines from a line of text. This is meant to be used
  159.      * with array_walk().
  160.      *
  161.      * @param string $line  The line to trim.
  162.      * @param integer $key  The index of the line in the array. Not used.
  163.      */
  164.     function trimNewlines(&$line$key)
  165.     {
  166.         $line str_replace(array("\n""\r")''$line);
  167.     }
  168.  
  169.     /**
  170.      * Checks a diff for validity.
  171.      *
  172.      * This is here only for debugging purposes.
  173.      */
  174.     function _check($from_lines$to_lines)
  175.     {
  176.         if (serialize($from_lines!= serialize($this->getOriginal())) {
  177.             trigger_error("Reconstructed original doesn't match"E_USER_ERROR);
  178.         }
  179.         if (serialize($to_lines!= serialize($this->getFinal())) {
  180.             trigger_error("Reconstructed final doesn't match"E_USER_ERROR);
  181.         }
  182.  
  183.         $rev $this->reverse();
  184.         if (serialize($to_lines!= serialize($rev->getOriginal())) {
  185.             trigger_error("Reversed original doesn't match"E_USER_ERROR);
  186.         }
  187.         if (serialize($from_lines!= serialize($rev->getFinal())) {
  188.             trigger_error("Reversed final doesn't match"E_USER_ERROR);
  189.         }
  190.  
  191.         $prevtype = null;
  192.         foreach ($this->_edits as $edit{
  193.             if ($prevtype == get_class($edit)) {
  194.                 trigger_error("Edit sequence is non-optimal"E_USER_ERROR);
  195.             }
  196.             $prevtype get_class($edit);
  197.         }
  198.  
  199.         return true;
  200.     }
  201.  
  202. }
  203.  
  204. /**
  205.  * $Horde: framework/Text_Diff/Diff.php,v 1.16 2005/12/27 22:15:21 jan Exp $
  206.  *
  207.  * @package Text_Diff
  208.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  209.  */
  210. class Text_MappedDiff extends Text_Diff {
  211.  
  212.     /**
  213.      * Computes a diff between sequences of strings.
  214.      *
  215.      * This can be used to compute things like case-insensitve diffs, or diffs
  216.      * which ignore changes in white-space.
  217.      *
  218.      * @param array $from_lines         An array of strings.
  219.      * @param array $to_lines           An array of strings.
  220.      * @param array $mapped_from_lines  This array should have the same size
  221.      *                                   number of elements as $from_lines.  The
  222.      *                                   elements in $mapped_from_lines and
  223.      *                                   $mapped_to_lines are what is actually
  224.      *                                   compared when computing the diff.
  225.      * @param array $mapped_to_lines    This array should have the same number
  226.      *                                   of elements as $to_lines.
  227.      */
  228.     function Text_MappedDiff($from_lines$to_lines,
  229.                              $mapped_from_lines$mapped_to_lines)
  230.     {
  231.         assert(count($from_lines== count($mapped_from_lines));
  232.         assert(count($to_lines== count($mapped_to_lines));
  233.  
  234.         parent::Text_Diff($mapped_from_lines$mapped_to_lines);
  235.  
  236.         $xi $yi = 0;
  237.         for ($i = 0; $i count($this->_edits)$i++{
  238.             $orig &$this->_edits[$i]->orig;
  239.             if (is_array($orig)) {
  240.                 $orig array_slice($from_lines$xicount($orig));
  241.                 $xi += count($orig);
  242.             }
  243.  
  244.             $final &$this->_edits[$i]->final;
  245.             if (is_array($final)) {
  246.                 $final array_slice($to_lines$yicount($final));
  247.                 $yi += count($final);
  248.             }
  249.         }
  250.     }
  251.  
  252. }
  253.  
  254. /**
  255.  * @package Text_Diff
  256.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  257.  *
  258.  * @access private
  259.  */
  260. class Text_Diff_Op {
  261.  
  262.     var $orig;
  263.     var $final;
  264.  
  265.     function reverse()
  266.     {
  267.         trigger_error('Abstract method'E_USER_ERROR);
  268.     }
  269.  
  270.     function norig()
  271.     {
  272.         return $this->orig count($this->orig: 0;
  273.     }
  274.  
  275.     function nfinal()
  276.     {
  277.         return $this->final count($this->final: 0;
  278.     }
  279.  
  280. }
  281.  
  282. /**
  283.  * @package Text_Diff
  284.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  285.  *
  286.  * @access private
  287.  */
  288. class Text_Diff_Op_copy extends Text_Diff_Op {
  289.  
  290.     function Text_Diff_Op_copy($orig$final = false)
  291.     {
  292.         if (!is_array($final)) {
  293.             $final $orig;
  294.         }
  295.         $this->orig $orig;
  296.         $this->final $final;
  297.     }
  298.  
  299.     function &reverse()
  300.     {
  301.         $reverse &new Text_Diff_Op_copy($this->final$this->orig);
  302.         return $reverse;
  303.     }
  304.  
  305. }
  306.  
  307. /**
  308.  * @package Text_Diff
  309.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  310.  *
  311.  * @access private
  312.  */
  313. class Text_Diff_Op_delete extends Text_Diff_Op {
  314.  
  315.     function Text_Diff_Op_delete($lines)
  316.     {
  317.         $this->orig $lines;
  318.         $this->final = false;
  319.     }
  320.  
  321.     function &reverse()
  322.     {
  323.         $reverse &new Text_Diff_Op_add($this->orig);
  324.         return $reverse;
  325.     }
  326.  
  327. }
  328.  
  329. /**
  330.  * @package Text_Diff
  331.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  332.  *
  333.  * @access private
  334.  */
  335. class Text_Diff_Op_add extends Text_Diff_Op {
  336.  
  337.     function Text_Diff_Op_add($lines)
  338.     {
  339.         $this->final $lines;
  340.         $this->orig = false;
  341.     }
  342.  
  343.     function &reverse()
  344.     {
  345.         $reverse &new Text_Diff_Op_delete($this->final);
  346.         return $reverse;
  347.     }
  348.  
  349. }
  350.  
  351. /**
  352.  * @package Text_Diff
  353.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  354.  *
  355.  * @access private
  356.  */
  357. class Text_Diff_Op_change extends Text_Diff_Op {
  358.  
  359.     function Text_Diff_Op_change($orig$final)
  360.     {
  361.         $this->orig $orig;
  362.         $this->final $final;
  363.     }
  364.  
  365.     function &reverse()
  366.     {
  367.         $reverse &new Text_Diff_Op_change($this->final$this->orig);
  368.         return $reverse;
  369.     }
  370.  
  371. }

Documentation generated on Mon, 11 Mar 2019 14:32:38 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.