| | <?php |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | class Text_Diff { |
| |
|
| | |
| | |
| | |
| | |
| | |
| | var $_edits; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function __construct( $engine, $params ) |
| | { |
| | |
| | if (!is_string($engine)) { |
| | $params = array($engine, $params); |
| | $engine = 'auto'; |
| | } |
| |
|
| | if ($engine == 'auto') { |
| | $engine = extension_loaded('xdiff') ? 'xdiff' : 'native'; |
| | } else { |
| | $engine = basename($engine); |
| | } |
| |
|
| | |
| | require_once dirname(__FILE__).'/Diff/Engine/' . $engine . '.php'; |
| | $class = 'Text_Diff_Engine_' . $engine; |
| | $diff_engine = new $class(); |
| |
|
| | $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params); |
| | } |
| |
|
| | |
| | |
| | |
| | public function Text_Diff( $engine, $params ) { |
| | self::__construct( $engine, $params ); |
| | } |
| |
|
| | |
| | |
| | |
| | function getDiff() |
| | { |
| | return $this->_edits; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function countAddedLines() |
| | { |
| | $count = 0; |
| | foreach ($this->_edits as $edit) { |
| | if (is_a($edit, 'Text_Diff_Op_add') || |
| | is_a($edit, 'Text_Diff_Op_change')) { |
| | $count += $edit->nfinal(); |
| | } |
| | } |
| | return $count; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function countDeletedLines() |
| | { |
| | $count = 0; |
| | foreach ($this->_edits as $edit) { |
| | if (is_a($edit, 'Text_Diff_Op_delete') || |
| | is_a($edit, 'Text_Diff_Op_change')) { |
| | $count += $edit->norig(); |
| | } |
| | } |
| | return $count; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function reverse() |
| | { |
| | if (version_compare(zend_version(), '2', '>')) { |
| | $rev = clone($this); |
| | } else { |
| | $rev = $this; |
| | } |
| | $rev->_edits = array(); |
| | foreach ($this->_edits as $edit) { |
| | $rev->_edits[] = $edit->reverse(); |
| | } |
| | return $rev; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | function isEmpty() |
| | { |
| | foreach ($this->_edits as $edit) { |
| | if (!is_a($edit, 'Text_Diff_Op_copy')) { |
| | return false; |
| | } |
| | } |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function lcs() |
| | { |
| | $lcs = 0; |
| | foreach ($this->_edits as $edit) { |
| | if (is_a($edit, 'Text_Diff_Op_copy')) { |
| | $lcs += count($edit->orig); |
| | } |
| | } |
| | return $lcs; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function getOriginal() |
| | { |
| | $lines = array(); |
| | foreach ($this->_edits as $edit) { |
| | if ($edit->orig) { |
| | array_splice($lines, count($lines), 0, $edit->orig); |
| | } |
| | } |
| | return $lines; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function getFinal() |
| | { |
| | $lines = array(); |
| | foreach ($this->_edits as $edit) { |
| | if ($edit->final) { |
| | array_splice($lines, count($lines), 0, $edit->final); |
| | } |
| | } |
| | return $lines; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static function trimNewlines(&$line, $key) |
| | { |
| | $line = str_replace(array("\n", "\r"), '', $line); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static function _getTempDir() |
| | { |
| | $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp', |
| | 'c:\windows\temp', 'c:\winnt\temp'); |
| |
|
| | |
| | $tmp = ini_get('upload_tmp_dir'); |
| |
|
| | |
| | if (!strlen($tmp)) { |
| | $tmp = getenv('TMPDIR'); |
| | } |
| |
|
| | |
| | |
| | while (!strlen($tmp) && count($tmp_locations)) { |
| | $tmp_check = array_shift($tmp_locations); |
| | if (@is_dir($tmp_check)) { |
| | $tmp = $tmp_check; |
| | } |
| | } |
| |
|
| | |
| | |
| | return strlen($tmp) ? $tmp : false; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | function _check($from_lines, $to_lines) |
| | { |
| | if (serialize($from_lines) != serialize($this->getOriginal())) { |
| | trigger_error("Reconstructed original does not match", E_USER_ERROR); |
| | } |
| | if (serialize($to_lines) != serialize($this->getFinal())) { |
| | trigger_error("Reconstructed final does not match", E_USER_ERROR); |
| | } |
| |
|
| | $rev = $this->reverse(); |
| | if (serialize($to_lines) != serialize($rev->getOriginal())) { |
| | trigger_error("Reversed original does not match", E_USER_ERROR); |
| | } |
| | if (serialize($from_lines) != serialize($rev->getFinal())) { |
| | trigger_error("Reversed final does not match", E_USER_ERROR); |
| | } |
| |
|
| | $prevtype = null; |
| | foreach ($this->_edits as $edit) { |
| | if ($edit instanceof $prevtype) { |
| | trigger_error("Edit sequence is non-optimal", E_USER_ERROR); |
| | } |
| | $prevtype = get_class($edit); |
| | } |
| |
|
| | return true; |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | |
| | |
| | class Text_MappedDiff extends Text_Diff { |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function __construct($from_lines, $to_lines, |
| | $mapped_from_lines, $mapped_to_lines) |
| | { |
| | assert(count($from_lines) == count($mapped_from_lines)); |
| | assert(count($to_lines) == count($mapped_to_lines)); |
| |
|
| | parent::Text_Diff($mapped_from_lines, $mapped_to_lines); |
| |
|
| | $xi = $yi = 0; |
| | for ($i = 0; $i < count($this->_edits); $i++) { |
| | $orig = &$this->_edits[$i]->orig; |
| | if (is_array($orig)) { |
| | $orig = array_slice($from_lines, $xi, count($orig)); |
| | $xi += count($orig); |
| | } |
| |
|
| | $final = &$this->_edits[$i]->final; |
| | if (is_array($final)) { |
| | $final = array_slice($to_lines, $yi, count($final)); |
| | $yi += count($final); |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | public function Text_MappedDiff( $from_lines, $to_lines, |
| | $mapped_from_lines, $mapped_to_lines ) { |
| | self::__construct( $from_lines, $to_lines, |
| | $mapped_from_lines, $mapped_to_lines ); |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | class Text_Diff_Op { |
| |
|
| | var $orig; |
| | var $final; |
| |
|
| | function &reverse() |
| | { |
| | trigger_error('Abstract method', E_USER_ERROR); |
| | } |
| |
|
| | function norig() |
| | { |
| | return $this->orig ? count($this->orig) : 0; |
| | } |
| |
|
| | function nfinal() |
| | { |
| | return $this->final ? count($this->final) : 0; |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | class Text_Diff_Op_copy extends Text_Diff_Op { |
| |
|
| | |
| | |
| | |
| | function __construct( $orig, $final = false ) |
| | { |
| | if (!is_array($final)) { |
| | $final = $orig; |
| | } |
| | $this->orig = $orig; |
| | $this->final = $final; |
| | } |
| |
|
| | |
| | |
| | |
| | public function Text_Diff_Op_copy( $orig, $final = false ) { |
| | self::__construct( $orig, $final ); |
| | } |
| |
|
| | function &reverse() |
| | { |
| | $reverse = new Text_Diff_Op_copy($this->final, $this->orig); |
| | return $reverse; |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | class Text_Diff_Op_delete extends Text_Diff_Op { |
| |
|
| | |
| | |
| | |
| | function __construct( $lines ) |
| | { |
| | $this->orig = $lines; |
| | $this->final = false; |
| | } |
| |
|
| | |
| | |
| | |
| | public function Text_Diff_Op_delete( $lines ) { |
| | self::__construct( $lines ); |
| | } |
| |
|
| | function &reverse() |
| | { |
| | $reverse = new Text_Diff_Op_add($this->orig); |
| | return $reverse; |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | class Text_Diff_Op_add extends Text_Diff_Op { |
| |
|
| | |
| | |
| | |
| | function __construct( $lines ) |
| | { |
| | $this->final = $lines; |
| | $this->orig = false; |
| | } |
| |
|
| | |
| | |
| | |
| | public function Text_Diff_Op_add( $lines ) { |
| | self::__construct( $lines ); |
| | } |
| |
|
| | function &reverse() |
| | { |
| | $reverse = new Text_Diff_Op_delete($this->final); |
| | return $reverse; |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | class Text_Diff_Op_change extends Text_Diff_Op { |
| |
|
| | |
| | |
| | |
| | function __construct( $orig, $final ) |
| | { |
| | $this->orig = $orig; |
| | $this->final = $final; |
| | } |
| |
|
| | |
| | |
| | |
| | public function Text_Diff_Op_change( $orig, $final ) { |
| | self::__construct( $orig, $final ); |
| | } |
| |
|
| | function &reverse() |
| | { |
| | $reverse = new Text_Diff_Op_change($this->final, $this->orig); |
| | return $reverse; |
| | } |
| |
|
| | } |
| |
|