home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Text / Diff.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  10.8 KB  |  414 lines

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