public function testHtmlDiffConfigStatic()
 {
     $oldText = '<b>text</b>';
     $newText = '<b>t3xt</b>';
     $config = new HtmlDiffConfig();
     $config->setPurifierCacheLocation('/tmp');
     $diff = HtmlDiff::create($oldText, $newText, $config);
     $diff->setHTMLPurifierConfig($this->config);
     $diff->build();
 }
Exemple #2
0
 /**
  * @param TableCell|null $oldCell
  * @param TableCell|null $newCell
  * @param bool           $usingExtraRow
  *
  * @return \DOMElement
  */
 protected function diffCells($oldCell, $newCell, $usingExtraRow = false)
 {
     $diffCell = $this->getNewCellNode($oldCell, $newCell);
     $oldContent = $oldCell ? $this->getInnerHtml($oldCell->getDomNode()) : '';
     $newContent = $newCell ? $this->getInnerHtml($newCell->getDomNode()) : '';
     $htmlDiff = HtmlDiff::create(mb_convert_encoding($oldContent, 'UTF-8', 'HTML-ENTITIES'), mb_convert_encoding($newContent, 'UTF-8', 'HTML-ENTITIES'), $this->config);
     $diff = $htmlDiff->build();
     $this->setInnerHtml($diffCell, $diff);
     if (null === $newCell) {
         $diffCell->setAttribute('class', trim($diffCell->getAttribute('class') . ' del'));
     }
     if (null === $oldCell) {
         $diffCell->setAttribute('class', trim($diffCell->getAttribute('class') . ' ins'));
     }
     if ($usingExtraRow) {
         $diffCell->setAttribute('class', trim($diffCell->getAttribute('class') . ' extra-row'));
     }
     return $diffCell;
 }
Exemple #3
0
 protected function diffLists(DiffList $oldList, DiffList $newList)
 {
     $oldMatchData = array();
     $newMatchData = array();
     $oldListIndices = array();
     $newListIndices = array();
     $oldListItems = array();
     $newListItems = array();
     foreach ($oldList->getListItems() as $oldIndex => $oldListItem) {
         if ($oldListItem instanceof DiffListItem) {
             $oldListItems[$oldIndex] = $oldListItem;
             $oldListIndices[] = $oldIndex;
             $oldMatchData[$oldIndex] = array();
             // Get match percentages
             foreach ($newList->getListItems() as $newIndex => $newListItem) {
                 if ($newListItem instanceof DiffListItem) {
                     if (!in_array($newListItem, $newListItems)) {
                         $newListItems[$newIndex] = $newListItem;
                     }
                     if (!in_array($newIndex, $newListIndices)) {
                         $newListIndices[] = $newIndex;
                     }
                     if (!array_key_exists($newIndex, $newMatchData)) {
                         $newMatchData[$newIndex] = array();
                     }
                     $oldText = implode('', $oldListItem->getText());
                     $newText = implode('', $newListItem->getText());
                     // similar_text
                     $percentage = null;
                     similar_text($oldText, $newText, $percentage);
                     $oldMatchData[$oldIndex][$newIndex] = $percentage;
                     $newMatchData[$newIndex][$oldIndex] = $percentage;
                 }
             }
         }
     }
     $currentIndexInOld = 0;
     $currentIndexInNew = 0;
     $oldCount = count($oldListIndices);
     $newCount = count($newListIndices);
     $difference = max($oldCount, $newCount) - min($oldCount, $newCount);
     $diffOutput = '';
     foreach ($newList->getListItems() as $newIndex => $newListItem) {
         if ($newListItem instanceof DiffListItem) {
             $operation = null;
             $oldListIndex = array_key_exists($currentIndexInOld, $oldListIndices) ? $oldListIndices[$currentIndexInOld] : null;
             $class = 'normal';
             if (null !== $oldListIndex && array_key_exists($oldListIndex, $oldMatchData)) {
                 // Check percentage matches of upcoming list items in old.
                 $matchPercentage = $oldMatchData[$oldListIndex][$newIndex];
                 // does the old list item match better?
                 $otherMatchBetter = false;
                 foreach ($oldMatchData[$oldListIndex] as $index => $percentage) {
                     if ($index > $newIndex && $percentage > $matchPercentage) {
                         $otherMatchBetter = $index;
                     }
                 }
                 if (false !== $otherMatchBetter && $newCount > $oldCount && $difference > 0) {
                     $diffOutput .= sprintf('%s', $newListItem->getHtml('normal new', 'ins'));
                     ++$currentIndexInNew;
                     --$difference;
                     continue;
                 }
                 $replacement = false;
                 // is there a better old list item match coming up?
                 if ($oldCount > $newCount) {
                     while ($difference > 0 && $this->hasBetterMatch($newMatchData[$newIndex], $oldListIndex)) {
                         $diffOutput .= sprintf('%s', $oldListItems[$oldListIndex]->getHtml('removed', 'del'));
                         ++$currentIndexInOld;
                         --$difference;
                         $oldListIndex = array_key_exists($currentIndexInOld, $oldListIndices) ? $oldListIndices[$currentIndexInOld] : null;
                         $matchPercentage = $oldMatchData[$oldListIndex][$newIndex];
                         $replacement = true;
                     }
                 }
                 $nextOldListIndex = array_key_exists($currentIndexInOld + 1, $oldListIndices) ? $oldListIndices[$currentIndexInOld + 1] : null;
                 if ($nextOldListIndex !== null && $oldMatchData[$nextOldListIndex][$newIndex] > $matchPercentage && $oldMatchData[$nextOldListIndex][$newIndex] > $this->config->getMatchThreshold()) {
                     // Following list item in old is better match, use that.
                     $diffOutput .= sprintf('%s', $oldListItems[$oldListIndex]->getHtml('removed', 'del'));
                     ++$currentIndexInOld;
                     $oldListIndex = $nextOldListIndex;
                     $matchPercentage = $oldMatchData[$oldListIndex][$newIndex];
                     $replacement = true;
                 }
                 if ($matchPercentage > $this->config->getMatchThreshold() || $currentIndexInNew === $currentIndexInOld) {
                     // Diff the two lists.
                     $htmlDiff = HtmlDiff::create($oldListItems[$oldListIndex]->getInnerHtml(), $newListItem->getInnerHtml(), $this->config);
                     $diffContent = $htmlDiff->build();
                     $diffOutput .= sprintf('%s%s%s', $newListItem->getStartTagWithDiffClass($replacement ? 'replacement' : 'normal'), $diffContent, $newListItem->getEndTag());
                 } else {
                     $diffOutput .= sprintf('%s', $oldListItems[$oldListIndex]->getHtml('removed', 'del'));
                     $diffOutput .= sprintf('%s', $newListItem->getHtml('replacement', 'ins'));
                 }
                 ++$currentIndexInOld;
             } else {
                 $diffOutput .= sprintf('%s', $newListItem->getHtml('normal new', 'ins'));
             }
             ++$currentIndexInNew;
         }
     }
     // Output any additional list items
     while (array_key_exists($currentIndexInOld, $oldListIndices)) {
         $oldListIndex = $oldListIndices[$currentIndexInOld];
         $diffOutput .= sprintf('%s', $oldListItems[$oldListIndex]->getHtml('removed', 'del'));
         ++$currentIndexInOld;
     }
     return sprintf('%s%s%s', $newList->getStartTagWithDiffClass(), $diffOutput, $newList->getEndTag());
 }
Exemple #4
0
 /**
  * @param Operation[]|array     $operations
  * @param \simple_html_dom_node $oldListNode
  * @param \simple_html_dom_node $newListNode
  *
  * @return string
  */
 protected function processOperations($operations, $oldListNode, $newListNode)
 {
     $output = '';
     $indexInOld = 0;
     $indexInNew = 0;
     $lastOperation = null;
     foreach ($operations as $operation) {
         $replaced = false;
         while ($operation->startInOld > ($operation->action === Operation::ADDED ? $indexInOld : $indexInOld + 1)) {
             $li = $oldListNode->children($indexInOld);
             $matchingLi = null;
             if ($operation->startInNew > ($operation->action === Operation::DELETED ? $indexInNew : $indexInNew + 1)) {
                 $matchingLi = $newListNode->children($indexInNew);
             }
             if (null !== $matchingLi) {
                 $htmlDiff = HtmlDiff::create($li->innertext, $matchingLi->innertext, $this->config);
                 $li->innertext = $htmlDiff->build();
                 $indexInNew++;
             }
             $class = self::CLASS_LIST_ITEM_NONE;
             if ($lastOperation === Operation::DELETED && !$replaced) {
                 $class = self::CLASS_LIST_ITEM_CHANGED;
                 $replaced = true;
             }
             $li->setAttribute('class', trim($li->getAttribute('class') . ' ' . $class));
             $output .= $li->outertext;
             $indexInOld++;
         }
         switch ($operation->action) {
             case Operation::ADDED:
                 for ($i = $operation->startInNew; $i <= $operation->endInNew; $i++) {
                     $output .= $this->addListItem($newListNode->children($i - 1));
                 }
                 $indexInNew = $operation->endInNew;
                 break;
             case Operation::DELETED:
                 for ($i = $operation->startInOld; $i <= $operation->endInOld; $i++) {
                     $output .= $this->deleteListItem($oldListNode->children($i - 1));
                 }
                 $indexInOld = $operation->endInOld;
                 break;
             case Operation::CHANGED:
                 $changeDelta = 0;
                 for ($i = $operation->startInOld; $i <= $operation->endInOld; $i++) {
                     $output .= $this->deleteListItem($oldListNode->children($i - 1));
                     $changeDelta--;
                 }
                 for ($i = $operation->startInNew; $i <= $operation->endInNew; $i++) {
                     $output .= $this->addListItem($newListNode->children($i - 1), $changeDelta < 0);
                     $changeDelta++;
                 }
                 $indexInOld = $operation->endInOld;
                 $indexInNew = $operation->endInNew;
                 break;
         }
         $lastOperation = $operation->action;
     }
     $oldCount = count($oldListNode->children());
     $newCount = count($newListNode->children());
     while ($indexInOld < $oldCount) {
         $li = $oldListNode->children($indexInOld);
         $matchingLi = null;
         if ($indexInNew < $newCount) {
             $matchingLi = $newListNode->children($indexInNew);
         }
         if (null !== $matchingLi) {
             $htmlDiff = HtmlDiff::create($li->innertext(), $matchingLi->innertext(), $this->config);
             $li->innertext = $htmlDiff->build();
             $indexInNew++;
         }
         $class = self::CLASS_LIST_ITEM_NONE;
         if ($lastOperation === Operation::DELETED) {
             $class = self::CLASS_LIST_ITEM_CHANGED;
         }
         $li->setAttribute('class', trim($li->getAttribute('class') . ' ' . $class));
         $output .= $li->outertext;
         $indexInOld++;
     }
     $newListNode->innertext = $output;
     $newListNode->setAttribute('class', trim($newListNode->getAttribute('class') . ' diff-list'));
     return $newListNode->outertext;
 }