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.20 2007/09/21 01:17:59 chuck 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 string $engine     Name of the diffing engine to use.  'auto'
  29.      *                            will automatically select the best.
  30.      * @param array $params      Parameters to pass to the diffing engine.
  31.      *                            Normally an array of two arrays, each
  32.      *                            containing the lines from a file.
  33.      */
  34.     function Text_Diff($engine$params)
  35.     {
  36.         // Backward compatibility workaround.
  37.         if (!is_string($engine)) {
  38.             $params = array($engine$params);
  39.             $engine 'auto';
  40.         }
  41.  
  42.         if ($engine == 'auto'{
  43.             $engine extension_loaded('xdiff''xdiff' 'native';
  44.         else {
  45.             $engine basename($engine);
  46.         }
  47.  
  48.         require_once 'Text/Diff/Engine/' $engine '.php';
  49.         $class 'Text_Diff_Engine_' $engine;
  50.         $diff_engine &new $class();
  51.  
  52.         $this->_edits call_user_func_array(array($diff_engine'diff')$params);
  53.     }
  54.  
  55.     /**
  56.      * Returns the array of differences.
  57.      */
  58.     function getDiff()
  59.     {
  60.         return $this->_edits;
  61.     }
  62.  
  63.     /**
  64.      * Computes a reversed diff.
  65.      *
  66.      * Example:
  67.      * <code>
  68.      * $diff = &new Text_Diff($lines1, $lines2);
  69.      * $rev = $diff->reverse();
  70.      * </code>
  71.      *
  72.      * @return Text_Diff  A Diff object representing the inverse of the
  73.      *                     original diff.  Note that we purposely don't return a
  74.      *                     reference here, since this essentially is a clone()
  75.      *                     method.
  76.      */
  77.     function reverse()
  78.     {
  79.         if (version_compare(zend_version()'2''>')) {
  80.             $rev = clone($this);
  81.         else {
  82.             $rev $this;
  83.         }
  84.         $rev->_edits = array();
  85.         foreach ($this->_edits as $edit{
  86.             $rev->_edits[$edit->reverse();
  87.         }
  88.         return $rev;
  89.     }
  90.  
  91.     /**
  92.      * Checks for an empty diff.
  93.      *
  94.      * @return boolean  True if two sequences were identical.
  95.      */
  96.     function isEmpty()
  97.     {
  98.         foreach ($this->_edits as $edit{
  99.             if (!is_a($edit'Text_Diff_Op_copy')) {
  100.                 return false;
  101.             }
  102.         }
  103.         return true;
  104.     }
  105.  
  106.     /**
  107.      * Computes the length of the Longest Common Subsequence (LCS).
  108.      *
  109.      * This is mostly for diagnostic purposes.
  110.      *
  111.      * @return integer  The length of the LCS.
  112.      */
  113.     function lcs()
  114.     {
  115.         $lcs = 0;
  116.         foreach ($this->_edits as $edit{
  117.             if (is_a($edit'Text_Diff_Op_copy')) {
  118.                 $lcs += count($edit->orig);
  119.             }
  120.         }
  121.         return $lcs;
  122.     }
  123.  
  124.     /**
  125.      * Gets the original set of lines.
  126.      *
  127.      * This reconstructs the $from_lines parameter passed to the constructor.
  128.      *
  129.      * @return array  The original sequence of strings.
  130.      */
  131.     function getOriginal()
  132.     {
  133.         $lines = array();
  134.         foreach ($this->_edits as $edit{
  135.             if ($edit->orig{
  136.                 array_splice($linescount($lines)0$edit->orig);
  137.             }
  138.         }
  139.         return $lines;
  140.     }
  141.  
  142.     /**
  143.      * Gets the final set of lines.
  144.      *
  145.      * This reconstructs the $to_lines parameter passed to the constructor.
  146.      *
  147.      * @return array  The sequence of strings.
  148.      */
  149.     function getFinal()
  150.     {
  151.         $lines = array();
  152.         foreach ($this->_edits as $edit{
  153.             if ($edit->final{
  154.                 array_splice($linescount($lines)0$edit->final);
  155.             }
  156.         }
  157.         return $lines;
  158.     }
  159.  
  160.     /**
  161.      * Removes trailing newlines from a line of text. This is meant to be used
  162.      * with array_walk().
  163.      *
  164.      * @param string $line  The line to trim.
  165.      * @param integer $key  The index of the line in the array. Not used.
  166.      */
  167.     function trimNewlines(&$line$key)
  168.     {
  169.         $line str_replace(array("\n""\r")''$line);
  170.     }
  171.  
  172.     /**
  173.      * Determines the location of the system temporary directory.
  174.      *
  175.      * @static
  176.      *
  177.      * @access protected
  178.      *
  179.      * @return string  A directory name which can be used for temp files.
  180.      *                  Returns false if one could not be found.
  181.      */
  182.     function _getTempDir()
  183.     {
  184.         $tmp_locations = array('/tmp''/var/tmp''c:\WUTemp''c:\temp',
  185.                                'c:\windows\temp''c:\winnt\temp');
  186.  
  187.         /* Try PHP's upload_tmp_dir directive. */
  188.         $tmp ini_get('upload_tmp_dir');
  189.  
  190.         /* Otherwise, try to determine the TMPDIR environment variable. */
  191.         if (!strlen($tmp)) {
  192.             $tmp getenv('TMPDIR');
  193.         }
  194.  
  195.         /* If we still cannot determine a value, then cycle through a list of
  196.          * preset possibilities. */
  197.         while (!strlen($tmp&& count($tmp_locations)) {
  198.             $tmp_check array_shift($tmp_locations);
  199.             if (@is_dir($tmp_check)) {
  200.                 $tmp $tmp_check;
  201.             }
  202.         }
  203.  
  204.         /* If it is still empty, we have failed, so return false; otherwise
  205.          * return the directory determined. */
  206.         return strlen($tmp$tmp : false;
  207.     }
  208.  
  209.     /**
  210.      * Checks a diff for validity.
  211.      *
  212.      * This is here only for debugging purposes.
  213.      */
  214.     function _check($from_lines$to_lines)
  215.     {
  216.         if (serialize($from_lines!= serialize($this->getOriginal())) {
  217.             trigger_error("Reconstructed original doesn't match"E_USER_ERROR);
  218.         }
  219.         if (serialize($to_lines!= serialize($this->getFinal())) {
  220.             trigger_error("Reconstructed final doesn't match"E_USER_ERROR);
  221.         }
  222.  
  223.         $rev $this->reverse();
  224.         if (serialize($to_lines!= serialize($rev->getOriginal())) {
  225.             trigger_error("Reversed original doesn't match"E_USER_ERROR);
  226.         }
  227.         if (serialize($from_lines!= serialize($rev->getFinal())) {
  228.             trigger_error("Reversed final doesn't match"E_USER_ERROR);
  229.         }
  230.  
  231.         $prevtype = null;
  232.         foreach ($this->_edits as $edit{
  233.             if ($prevtype == get_class($edit)) {
  234.                 trigger_error("Edit sequence is non-optimal"E_USER_ERROR);
  235.             }
  236.             $prevtype get_class($edit);
  237.         }
  238.  
  239.         return true;
  240.     }
  241.  
  242. }
  243.  
  244. /**
  245.  * $Horde: framework/Text_Diff/Diff.php,v 1.20 2007/09/21 01:17:59 chuck Exp $
  246.  *
  247.  * @package Text_Diff
  248.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  249.  */
  250. class Text_MappedDiff extends Text_Diff {
  251.  
  252.     /**
  253.      * Computes a diff between sequences of strings.
  254.      *
  255.      * This can be used to compute things like case-insensitve diffs, or diffs
  256.      * which ignore changes in white-space.
  257.      *
  258.      * @param array $from_lines         An array of strings.
  259.      * @param array $to_lines           An array of strings.
  260.      * @param array $mapped_from_lines  This array should have the same size
  261.      *                                   number of elements as $from_lines.  The
  262.      *                                   elements in $mapped_from_lines and
  263.      *                                   $mapped_to_lines are what is actually
  264.      *                                   compared when computing the diff.
  265.      * @param array $mapped_to_lines    This array should have the same number
  266.      *                                   of elements as $to_lines.
  267.      */
  268.     function Text_MappedDiff($from_lines$to_lines,
  269.                              $mapped_from_lines$mapped_to_lines)
  270.     {
  271.         assert(count($from_lines== count($mapped_from_lines));
  272.         assert(count($to_lines== count($mapped_to_lines));
  273.  
  274.         parent::Text_Diff($mapped_from_lines$mapped_to_lines);
  275.  
  276.         $xi $yi = 0;
  277.         for ($i = 0; $i count($this->_edits)$i++{
  278.             $orig &$this->_edits[$i]->orig;
  279.             if (is_array($orig)) {
  280.                 $orig array_slice($from_lines$xicount($orig));
  281.                 $xi += count($orig);
  282.             }
  283.  
  284.             $final &$this->_edits[$i]->final;
  285.             if (is_array($final)) {
  286.                 $final array_slice($to_lines$yicount($final));
  287.                 $yi += count($final);
  288.             }
  289.         }
  290.     }
  291.  
  292. }
  293.  
  294. /**
  295.  * @package Text_Diff
  296.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  297.  *
  298.  * @access private
  299.  */
  300. class Text_Diff_Op {
  301.  
  302.     var $orig;
  303.     var $final;
  304.  
  305.     function &reverse()
  306.     {
  307.         trigger_error('Abstract method'E_USER_ERROR);
  308.     }
  309.  
  310.     function norig()
  311.     {
  312.         return $this->orig count($this->orig: 0;
  313.     }
  314.  
  315.     function nfinal()
  316.     {
  317.         return $this->final count($this->final: 0;
  318.     }
  319.  
  320. }
  321.  
  322. /**
  323.  * @package Text_Diff
  324.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  325.  *
  326.  * @access private
  327.  */
  328. class Text_Diff_Op_copy extends Text_Diff_Op {
  329.  
  330.     function Text_Diff_Op_copy($orig$final = false)
  331.     {
  332.         if (!is_array($final)) {
  333.             $final $orig;
  334.         }
  335.         $this->orig $orig;
  336.         $this->final $final;
  337.     }
  338.  
  339.     function &reverse()
  340.     {
  341.         $reverse &new Text_Diff_Op_copy($this->final$this->orig);
  342.         return $reverse;
  343.     }
  344.  
  345. }
  346.  
  347. /**
  348.  * @package Text_Diff
  349.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  350.  *
  351.  * @access private
  352.  */
  353. class Text_Diff_Op_delete extends Text_Diff_Op {
  354.  
  355.     function Text_Diff_Op_delete($lines)
  356.     {
  357.         $this->orig $lines;
  358.         $this->final = false;
  359.     }
  360.  
  361.     function &reverse()
  362.     {
  363.         $reverse &new Text_Diff_Op_add($this->orig);
  364.         return $reverse;
  365.     }
  366.  
  367. }
  368.  
  369. /**
  370.  * @package Text_Diff
  371.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  372.  *
  373.  * @access private
  374.  */
  375. class Text_Diff_Op_add extends Text_Diff_Op {
  376.  
  377.     function Text_Diff_Op_add($lines)
  378.     {
  379.         $this->final $lines;
  380.         $this->orig = false;
  381.     }
  382.  
  383.     function &reverse()
  384.     {
  385.         $reverse &new Text_Diff_Op_delete($this->final);
  386.         return $reverse;
  387.     }
  388.  
  389. }
  390.  
  391. /**
  392.  * @package Text_Diff
  393.  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  394.  *
  395.  * @access private
  396.  */
  397. class Text_Diff_Op_change extends Text_Diff_Op {
  398.  
  399.     function Text_Diff_Op_change($orig$final)
  400.     {
  401.         $this->orig $orig;
  402.         $this->final $final;
  403.     }
  404.  
  405.     function &reverse()
  406.     {
  407.         $reverse &new Text_Diff_Op_change($this->final$this->orig);
  408.         return $reverse;
  409.     }
  410.  
  411. }

Documentation generated on Mon, 11 Mar 2019 15:09:49 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.