1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-14 10:45:54 +02:00

Add simple string diff markup generator to WireTextTools class, via diffMarkup() method

This commit is contained in:
Ryan Cramer
2019-10-29 06:11:10 -04:00
parent 80eb2ff3f1
commit 50e916b72f

View File

@@ -854,6 +854,117 @@ class WireTextTools extends Wire {
return $str;
}
/**
* Given two arrays, return array of the changes with 'ins' and 'del' keys
*
* Based upon Paul Butlers Simple Diff Algorithm v0.1 © 2007 (zlib/libpng) https://paulbutler.org
*
* @param array $oldArray
* @param array $newArray
* @return array
*
*/
protected function diffArray(array $oldArray, array $newArray) {
$matrix = array();
$maxLen = 0;
$oldMax = 0;
$newMax = 0;
foreach($oldArray as $oldKey => $oldValue){
$newKeys = array_keys($newArray, $oldValue);
foreach($newKeys as $newKey) {
$len = 1;
if(isset($matrix[$oldKey - 1][$newKey - 1])) {
$len = $matrix[$oldKey - 1][$newKey - 1] + 1;
}
$matrix[$oldKey][$newKey] = $len;
if($len > $maxLen) {
$maxLen = $len;
$oldMax = $oldKey + 1 - $maxLen;
$newMax = $newKey + 1 - $maxLen;
}
}
}
if($maxLen == 0) {
$result = array(
array('del' => $oldArray, 'ins' => $newArray)
);
} else {
$result = array_merge(
$this->diffArray(
array_slice($oldArray, 0, $oldMax),
array_slice($newArray, 0, $newMax)
),
array_slice($newArray, $newMax, $maxLen),
$this->diffArray(
array_slice($oldArray, $oldMax + $maxLen),
array_slice($newArray, $newMax + $maxLen)
)
);
}
return $result;
}
/**
* Given two strings ($old and $new) return a diff string in HTML markup
*
* @param string $old Old string value
* @param string $new New string value
* @param array $options Options to modify behavior:
* - `ins` (string) Markup to use for diff insertions (default: `<ins>{out}</ins>`)
* - `del` (string) Markup to use for diff deletions (default: `<del>{out}</del>`)
* - `entityEncode` (bool): Entity encode values, other than added ins/del tags? (default=true)
* - `split` (string): Regex used to split strings for parts to diff (default=`\s+`)
* @return string
*
*/
public function diffMarkup($old, $new, array $options = array()) {
$defaults = array(
'ins' => "<ins>{out}</ins>",
'del' => "<del>{out}</del>",
'entityEncode' => true,
'split' => '\s+',
);
/** @var Sanitizer $sanitizer */
$sanitizer = $this->wire('sanitizer');
list($old, $new) = array("$old", "$new"); // enforce as string
$options = array_merge($defaults, $options);
$oldArray = preg_split("!($options[split])!", $old, 0, PREG_SPLIT_DELIM_CAPTURE);
$newArray = preg_split("!($options[split])!", $new, 0, PREG_SPLIT_DELIM_CAPTURE);
$diffArray = $this->diffArray($oldArray, $newArray);
list(,$delClose) = explode('{out}', $options['del'], 2);
list($insOpen,) = explode('{out}', $options['ins'], 2);
$out = '';
foreach($diffArray as $diff) {
if(is_array($diff)) {
foreach(array('del', 'ins') as $key) {
if(empty($diff[$key])) continue;
$diffStr = implode('', $diff[$key]);
if($options['entityEncode']) $diffStr = $sanitizer->entities1($diffStr);
$out .= str_replace('{out}', $diffStr, $options[$key]);
}
} else {
$out .= ($options['entityEncode'] ? $sanitizer->entities1($diff) : $diff);
}
}
if(strpos($out, "$delClose$insOpen")) {
// put a space between '</del><ins>' so that it is '</del> <ins>'
$out = str_replace("$delClose$insOpen", "$delClose $insOpen", $out);
}
return $out;
}
/***********************************************************************************************************
* MULTIBYTE PHP STRING FUNCTIONS THAT FALLBACK WHEN MBSTRING NOT AVAILABLE