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

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