: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Return a series of nested arrays containing different groups of generated
* opcodes for the differences between the strings with up to $context lines
* of surrounding content.
* Essentially what happens here is any big equal blocks of strings are stripped
* out, the smaller subsets of changes are then arranged in to their groups.
* This means that the sequence matcher and diffs do not need to include the full
* content of the different files but can still provide context as to where the
* @param int $context The number of lines of context to provide around the groups.
* @return array Nested array of all of the grouped opcodes.
public function getGroupedOpcodes($context=3)
$opCodes = $this->getOpCodes();
if($opCodes[0][0] == 'equal') {
max($opCodes[0][1], $opCodes[0][2] - $context),
max($opCodes[0][3], $opCodes[0][4] - $context),
$lastItem = count($opCodes) - 1;
if($opCodes[$lastItem][0] == 'equal') {
list($tag, $i1, $i2, $j1, $j2) = $opCodes[$lastItem];
$opCodes[$lastItem] = array(
min($i2, $i1 + $context),
$maxRange = $context * 2;
foreach($opCodes as $code) {
list($tag, $i1, $i2, $j1, $j2) = $code;
if($tag == 'equal' && $i2 - $i1 > $maxRange) {
min($i2, $i1 + $context),
$i1 = max($i1, $i2 - $context);
$j1 = max($j1, $j2 - $context);
if(!empty($group) && !(count($group) == 1 && $group[0][0] == 'equal')) {
* Return a measure of the similarity between the two sequences.
* This will be a float value between 0 and 1.
* Out of all of the ratio calculation functions, this is the most
* expensive to call if getMatchingBlocks or getOpCodes is yet to be
* called. The other calculation methods (quickRatio and realquickRatio)
* can be used to perform quicker calculations but may be less accurate.
* The ratio is calculated as (2 * number of matches) / total number of
* elements in both sequences.
* @return float The calculated ratio.
$matches = array_reduce($this->getMatchingBlocks(), array($this, 'ratioReduce'), 0);
return $this->calculateRatio($matches, count ($this->a) + count ($this->b));
* Helper function to calculate the number of matches for Ratio().
* @param int $sum The running total for the number of matches.
* @param array $triple Array containing the matching block triple to add to the running total.
* @return int The new running total for the number of matches.
private function ratioReduce($sum, $triple)
return $sum + ($triple[count($triple) - 1]);
* Helper function for calculating the ratio to measure similarity for the strings.
* The ratio is defined as being 2 * (number of matches / total length)
* @param int $matches The number of matches in the two strings.
* @param int $length The length of the two strings.
* @return float The calculated ratio.
private function calculateRatio($matches, $length=0)
return 2 * ($matches / $length);
* Helper function that provides the ability to return the value for a key
* in an array of it exists, or if it doesn't then return a default value.
* Essentially cleaner than doing a series of if(isset()) {} else {} calls.
* @param array $array The array to search.
* @param string $key The key to check that exists.
* @param mixed $default The value to return as the default value if the key doesn't exist.
* @return mixed The value from the array if the key exists or otherwise the default.
private function arrayGetDefault($array, $key, $default)
if(isset($array[$key])) {
* Sort an array by the nested arrays it contains. Helper function for getMatchingBlocks
* @param array $a First array to compare.
* @param array $b Second array to compare.
* @return int -1, 0 or 1, as expected by the usort function.
private function tupleSort($a, $b)
$max = max(count($a), count($b));
for($i = 0; $i < $max; ++$i) {
else if($a[$i] > $b[$i]) {
if(count($a) == count($b)) {
else if(count($a) < count($b)) {