WP_Text_Diff_Renderer_Table::_changed()publicWP 2.6.0

Process changed lines to do word-by-word diffs for extra highlighting.

(TRAC style) sometimes these lines can actually be deleted or added rows. We do additional processing to figure that out

Method of the class: WP_Text_Diff_Renderer_Table{}

No Hooks.

Return

String.

Usage

$WP_Text_Diff_Renderer_Table = new WP_Text_Diff_Renderer_Table();
$WP_Text_Diff_Renderer_Table->_changed( $orig, $final );
$orig(array) (required)
-
$final(array) (required)
-

Changelog

Since 2.6.0 Introduced.

WP_Text_Diff_Renderer_Table::_changed() code WP 6.4.3

public function _changed( $orig, $final ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.finalFound
	$r = '';

	/*
	 * Does the aforementioned additional processing:
	 * *_matches tell what rows are "the same" in orig and final. Those pairs will be diffed to get word changes.
	 * - match is numeric: an index in other column.
	 * - match is 'X': no match. It is a new row.
	 * *_rows are column vectors for the orig column and the final column.
	 * - row >= 0: an index of the $orig or $final array.
	 * - row < 0: a blank row for that column.
	 */
	list($orig_matches, $final_matches, $orig_rows, $final_rows) = $this->interleave_changed_lines( $orig, $final );

	// These will hold the word changes as determined by an inline diff.
	$orig_diffs  = array();
	$final_diffs = array();

	// Compute word diffs for each matched pair using the inline diff.
	foreach ( $orig_matches as $o => $f ) {
		if ( is_numeric( $o ) && is_numeric( $f ) ) {
			$text_diff = new Text_Diff( 'auto', array( array( $orig[ $o ] ), array( $final[ $f ] ) ) );
			$renderer  = new $this->inline_diff_renderer();
			$diff      = $renderer->render( $text_diff );

			// If they're too different, don't include any <ins> or <del>'s.
			if ( preg_match_all( '!(<ins>.*?</ins>|<del>.*?</del>)!', $diff, $diff_matches ) ) {
				// Length of all text between <ins> or <del>.
				$stripped_matches = strlen( strip_tags( implode( ' ', $diff_matches[0] ) ) );
				/*
				 * Since we count length of text between <ins> or <del> (instead of picking just one),
				 * we double the length of chars not in those tags.
				 */
				$stripped_diff = strlen( strip_tags( $diff ) ) * 2 - $stripped_matches;
				$diff_ratio    = $stripped_matches / $stripped_diff;
				if ( $diff_ratio > $this->_diff_threshold ) {
					continue; // Too different. Don't save diffs.
				}
			}

			// Un-inline the diffs by removing <del> or <ins>.
			$orig_diffs[ $o ]  = preg_replace( '|<ins>.*?</ins>|', '', $diff );
			$final_diffs[ $f ] = preg_replace( '|<del>.*?</del>|', '', $diff );
		}
	}

	foreach ( array_keys( $orig_rows ) as $row ) {
		// Both columns have blanks. Ignore them.
		if ( $orig_rows[ $row ] < 0 && $final_rows[ $row ] < 0 ) {
			continue;
		}

		// If we have a word based diff, use it. Otherwise, use the normal line.
		if ( isset( $orig_diffs[ $orig_rows[ $row ] ] ) ) {
			$orig_line = $orig_diffs[ $orig_rows[ $row ] ];
		} elseif ( isset( $orig[ $orig_rows[ $row ] ] ) ) {
			$orig_line = htmlspecialchars( $orig[ $orig_rows[ $row ] ] );
		} else {
			$orig_line = '';
		}

		if ( isset( $final_diffs[ $final_rows[ $row ] ] ) ) {
			$final_line = $final_diffs[ $final_rows[ $row ] ];
		} elseif ( isset( $final[ $final_rows[ $row ] ] ) ) {
			$final_line = htmlspecialchars( $final[ $final_rows[ $row ] ] );
		} else {
			$final_line = '';
		}

		if ( $orig_rows[ $row ] < 0 ) { // Orig is blank. This is really an added row.
			$r .= $this->_added( array( $final_line ), false );
		} elseif ( $final_rows[ $row ] < 0 ) { // Final is blank. This is really a deleted row.
			$r .= $this->_deleted( array( $orig_line ), false );
		} else { // A true changed row.
			if ( $this->_show_split_view ) {
				$r .= '<tr>' . $this->deletedLine( $orig_line ) . $this->addedLine( $final_line ) . "</tr>\n";
			} else {
				$r .= '<tr>' . $this->deletedLine( $orig_line ) . '</tr><tr>' . $this->addedLine( $final_line ) . "</tr>\n";
			}
		}
	}

	return $r;
}