Passed
Pull Request — master (#31)
by Josh
03:23
created

ListDiffNew   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 239
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 93.13%

Importance

Changes 2
Bugs 2 Features 0
Metric Value
wmc 49
c 2
b 2
f 0
lcom 1
cbo 4
dl 0
loc 239
ccs 149
cts 160
cp 0.9313
rs 8.5454

7 Methods

Rating   Name   Duplication   Size   Complexity  
A build() 0 9 1
C buildDiffList() 0 75 13
A isOpeningListTag() 0 5 2
A isClosingListTag() 0 6 2
A isOpeningListItemTag() 0 6 2
A isClosingListItemTag() 0 6 2
F diffLists() 0 121 27

How to fix   Complexity   

Complex Class

Complex classes like ListDiffNew often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ListDiffNew, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Caxy\HtmlDiff;
4
5
use Caxy\HtmlDiff\ListDiff\DiffList;
6
use Caxy\HtmlDiff\ListDiff\DiffListItem;
7
8
class ListDiffNew extends AbstractDiff
9
{
10
    protected static $listTypes = array('ul', 'ol', 'dl');
11
12 4
    public function build()
13
    {
14 4
        $this->splitInputsToWords();
15
16 4
        return $this->diffLists(
17 4
            $this->buildDiffList($this->oldWords),
18 4
            $this->buildDiffList($this->newWords)
19 4
        );
20
    }
21
22 4
    protected function diffLists(DiffList $oldList, DiffList $newList)
23
    {
24 4
        $oldMatchData = array();
25 4
        $newMatchData = array();
26 4
        $oldListIndices = array();
27 4
        $newListIndices = array();
28 4
        $oldListItems = array();
29 4
        $newListItems = array();
30
31 4
        foreach ($oldList->getListItems() as $oldIndex => $oldListItem) {
32 4
            if ($oldListItem instanceof DiffListItem) {
33 4
                $oldListItems[$oldIndex] = $oldListItem;
34
35 4
                $oldListIndices[] = $oldIndex;
36 4
                $oldMatchData[$oldIndex] = array();
37
38
                // Get match percentages
39 4
                foreach ($newList->getListItems() as $newIndex => $newListItem) {
40 4
                    if ($newListItem instanceof DiffListItem) {
41 4
                        if (!in_array($newListItem, $newListItems)) {
42 4
                            $newListItems[$newIndex] = $newListItem;
43 4
                        }
44 4
                        if (!in_array($newIndex, $newListIndices)) {
45 4
                            $newListIndices[] = $newIndex;
46 4
                        }
47 4
                        if (!array_key_exists($newIndex, $newMatchData)) {
48 4
                            $newMatchData[$newIndex] = array();
49 4
                        }
50
51 4
                        $oldText = implode('', $oldListItem->getText());
52 4
                        $newText = implode('', $newListItem->getText());
53
54
                        // similar_text
55 4
                        $percentage = null;
56 4
                        similar_text($oldText, $newText, $percentage);
57
58 4
                        $oldMatchData[$oldIndex][$newIndex] = $percentage;
59 4
                        $newMatchData[$newIndex][$oldIndex] = $percentage;
60 4
                    }
61 4
                }
62 4
            }
63 4
        }
64
65 4
        $currentIndexInOld = 0;
66 4
        $currentIndexInNew = 0;
67 4
        $oldCount = count($oldListIndices);
68 4
        $newCount = count($newListIndices);
69 4
        $difference = max($oldCount, $newCount) - min($oldCount, $newCount);
70
71 4
        $diffOutput = '';
72
73 4
        foreach ($newList->getListItems() as $newIndex => $newListItem) {
74 4
            if ($newListItem instanceof DiffListItem) {
75 4
                $operation = null;
0 ignored issues
show
Unused Code introduced by
$operation is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
76
77 4
                $oldListIndex = array_key_exists($currentIndexInOld, $oldListIndices) ? $oldListIndices[$currentIndexInOld] : null;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 131 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
78 4
                $class = 'normal';
0 ignored issues
show
Unused Code introduced by
$class is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
79
80 4
                if (null !== $oldListIndex && array_key_exists($oldListIndex, $oldMatchData)) {
81
                    // Check percentage matches of upcoming list items in old.
82 4
                    $matchPercentage = $oldMatchData[$oldListIndex][$newIndex];
83
84
                    // does the old list item match better?
85 4
                    $otherMatchBetter = false;
86 4
                    foreach ($oldMatchData[$oldListIndex] as $index => $percentage) {
87 4
                        if ($index > $newIndex && $percentage > $matchPercentage) {
88 2
                            $otherMatchBetter = $index;
89 2
                        }
90 4
                    }
91
92 4
                    if (false !== $otherMatchBetter && $newCount > $oldCount && $difference > 0) {
93 1
                        $diffOutput .= sprintf('%s', $newListItem->getHtml('normal new', 'ins'));
94 1
                        $currentIndexInNew++;
95 1
                        $difference--;
96
97 1
                        continue;
98
                    }
99
100 4
                    $nextOldListIndex = array_key_exists($currentIndexInOld + 1, $oldListIndices) ? $oldListIndices[$currentIndexInOld + 1] : null;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 147 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
101
102 4
                    $replacement = false;
103
104 4
                    if ($nextOldListIndex !== null && $oldMatchData[$nextOldListIndex][$newIndex] > $matchPercentage && $oldMatchData[$nextOldListIndex][$newIndex] > $this->matchThreshold) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 190 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
105
                        // Following list item in old is better match, use that.
106
                        $diffOutput .= sprintf('%s', $oldListItems[$oldListIndex]->getHtml('removed', 'del'));
107
108
                        $currentIndexInOld++;
109
                        $oldListIndex = $nextOldListIndex;
110
                        $matchPercentage = $oldMatchData[$oldListIndex];
111
                        $replacement = true;
112
                    }
113
114 4
                    if ($matchPercentage > $this->matchThreshold || $currentIndexInNew === $currentIndexInOld) {
115
                        // Diff the two lists.
116 4
                        $htmlDiff = new HtmlDiff($oldListItems[$oldListIndex]->getInnerHtml(), $newListItem->getInnerHtml(), $this->encoding, $this->specialCaseTags, $this->groupDiffs);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 185 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
117 4
                        $diffContent = $htmlDiff->build();
118
119 4
                        $diffOutput .= sprintf('%s%s%s', $newListItem->getStartTagWithDiffClass($replacement ? 'replacement' : 'normal'), $diffContent, $newListItem->getEndTag());
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 179 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
120
121 4
                    } else {
122
                        $diffOutput .= sprintf('%s', $oldListItems[$oldListIndex]->getHtml('removed', 'del'));
123
                        $diffOutput .= sprintf('%s', $newListItem->getHtml('replacement', 'ins'));
124
                    }
125 4
                    $currentIndexInOld++;
126 4
                } else {
127
                    $diffOutput .= sprintf('%s', $newListItem->getHtml('normal new', 'ins'));
128
                }
129
130 4
                $currentIndexInNew++;
131 4
            }
132 4
        }
133
134
        // Output any additional list items
135 4
        while (array_key_exists($currentIndexInOld, $oldListIndices)) {
136 2
            $oldListIndex = $oldListIndices[$currentIndexInOld];
137 2
            $diffOutput .= sprintf('%s', $oldListItems[$oldListIndex]->getHtml('removed', 'del'));
138 2
            $currentIndexInOld++;
139 2
        }
140
141 4
        return sprintf('%s%s%s', $newList->getStartTagWithDiffClass(), $diffOutput, $newList->getEndTag());
142
    }
143
144 4
    protected function buildDiffList($words)
145
    {
146 4
        $listType = null;
147 4
        $listStartTag = null;
148 4
        $listEndTag = null;
149 4
        $attributes = array();
150 4
        $openLists = 0;
151 4
        $openListItems = 0;
152 4
        $list = array();
153 4
        $currentListItem = null;
154 4
        $listItemType = null;
155 4
        $listItemStart = null;
156 4
        $listItemEnd = null;
0 ignored issues
show
Unused Code introduced by
$listItemEnd is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
157
158 4
        foreach ($words as $i => $word) {
159 4
            if ($this->isOpeningListTag($word, $listType)) {
160 4
                if ($openLists > 0) {
161 1
                    if ($openListItems > 0) {
162 1
                        $currentListItem[] = $word;
163 1
                    } else {
164
                        $list[] = $word;
165
                    }
166 1
                } else {
167 4
                    $listType = substr($word, 1, 2);
168 4
                    $listStartTag = $word;
169
                }
170
171 4
                $openLists++;
172 4
            } elseif ($this->isClosingListTag($word, $listType)) {
173 4
                if ($openLists > 1) {
174 1
                    if ($openListItems > 0) {
175 1
                        $currentListItem[] = $word;
176 1
                    } else {
177
                        $list[] = $word;
178
                    }
179 1
                } else {
180 4
                    $listEndTag = $word;
181
                }
182
183 4
                $openLists--;
184 4
            } elseif ($this->isOpeningListItemTag($word, $listItemType)) {
185 4
                if ($openListItems === 0) {
186
                    // New top-level list item
187 4
                    $currentListItem = array();
188 4
                    $listItemType = substr($word, 1, 2);
189 4
                    $listItemStart = $word;
190 4
                } else {
191 1
                    $currentListItem[] = $word;
192
                }
193
194 4
                $openListItems++;
195 4
            } elseif ($this->isClosingListItemTag($word, $listItemType)) {
196 4
                if ($openListItems === 1) {
197 4
                    $listItemEnd = $word;
198 4
                    $listItem = new DiffListItem($currentListItem, array(), $listItemStart, $listItemEnd);
199 4
                    $list[] = $listItem;
200 4
                    $currentListItem = null;
201 4
                } else {
202 1
                    $currentListItem[] = $word;
203
                }
204
205 4
                $openListItems--;
206 4
            } else {
207 4
                if ($openListItems > 0) {
208 4
                    $currentListItem[] = $word;
209 4
                } else {
210 4
                    $list[] = $word;
211
                }
212
            }
213 4
        }
214
215 4
        $diffList = new DiffList($listType, $listStartTag, $listEndTag, $list, $attributes);
216
217 4
        return $diffList;
218
    }
219
220 4
    protected function isOpeningListTag($word, $type = null)
221
    {
222 4
        $filter = $type !== null ? array('<' . $type) : array('<ul', '<ol', '<dl');
223 4
        return in_array(substr($word, 0, 3), $filter);
224
    }
225
226 4
    protected function isClosingListTag($word, $type = null)
227
    {
228 4
        $filter = $type !== null ? array('</' . $type) : array('</ul', '</ol', '</dl');
229
230 4
        return in_array(substr($word, 0, 4), $filter);
231
    }
232
233 4
    protected function isOpeningListItemTag($word, $type = null)
234
    {
235 4
        $filter = $type !== null ? array('<' . $type) : array('<li', '<dd', '<dt');
236
237 4
        return in_array(substr($word, 0, 3), $filter);
238
    }
239
240 4
    protected function isClosingListItemTag($word, $type = null)
241
    {
242 4
        $filter = $type !== null ? array('</' . $type) : array('</li', '</dd', '</dt');
243
244 4
        return in_array(substr($word, 0, 4), $filter);
245
    }
246
}
247