Text_Diff_Engine_string   F
last analyzed

Complexity

Total Complexity 76

Size/Duplication

Total Lines 226
Duplicated Lines 18.58 %

Coupling/Cohesion

Components 0
Dependencies 4

Importance

Changes 0
Metric Value
dl 42
loc 226
rs 2.2388
c 0
b 0
f 0
wmc 76
lcom 0
cbo 4

3 Methods

Rating   Name   Duplication   Size   Complexity  
D diff() 5 45 18
F parseContextDiff() 30 101 44
C parseUnifiedDiff() 7 47 14

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Text_Diff_Engine_string 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 Text_Diff_Engine_string, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Parses unified or context diffs output from eg. the diff utility.
4
 *
5
 * Example:
6
 * <code>
7
 * $patch = file_get_contents('example.patch');
8
 * $diff = new Text_Diff('string', array($patch));
9
 * $renderer = new Text_Diff_Renderer_inline();
10
 * echo $renderer->render($diff);
11
 * </code>
12
 *
13
 * Copyright 2005 Örjan Persson <[email protected]>
14
 * Copyright 2005-2010 The Horde Project (http://www.horde.org/)
15
 *
16
 * See the enclosed file COPYING for license information (LGPL). If you did
17
 * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
18
 *
19
 * @author  Örjan Persson <[email protected]>
20
 * @package Text_Diff
21
 * @since   0.2.0
22
 */
23
class Text_Diff_Engine_string {
24
25
    /**
26
     * Parses a unified or context diff.
27
     *
28
     * First param contains the whole diff and the second can be used to force
29
     * a specific diff type. If the second parameter is 'autodetect', the
30
     * diff will be examined to find out which type of diff this is.
31
     *
32
     * @param string $diff  The diff content.
33
     * @param string $mode  The diff mode of the content in $diff. One of
34
     *                      'context', 'unified', or 'autodetect'.
35
     *
36
     * @return array  List of all diff operations.
37
     */
38
    function diff($diff, $mode = 'autodetect')
39
    {
40
        // Detect line breaks.
41
        $lnbr = "\n";
42 View Code Duplication
        if (strpos($diff, "\r\n") !== false) {
43
            $lnbr = "\r\n";
44
        } elseif (strpos($diff, "\r") !== false) {
45
            $lnbr = "\r";
46
        }
47
48
        // Make sure we have a line break at the EOF.
49
        if (substr($diff, -strlen($lnbr)) != $lnbr) {
50
            $diff .= $lnbr;
51
        }
52
53
        if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
54
            return PEAR::raiseError('Type of diff is unsupported');
55
        }
56
57
        if ($mode == 'autodetect') {
58
            $context = strpos($diff, '***');
59
            $unified = strpos($diff, '---');
60
            if ($context === $unified) {
61
                return PEAR::raiseError('Type of diff could not be detected');
62
            } elseif ($context === false || $unified === false) {
63
                $mode = $context !== false ? 'context' : 'unified';
64
            } else {
65
                $mode = $context < $unified ? 'context' : 'unified';
66
            }
67
        }
68
69
        // Split by new line and remove the diff header, if there is one.
70
        $diff = explode($lnbr, $diff);
71
        if (($mode == 'context' && strpos($diff[0], '***') === 0) ||
72
            ($mode == 'unified' && strpos($diff[0], '---') === 0)) {
73
            array_shift($diff);
74
            array_shift($diff);
75
        }
76
77
        if ($mode == 'context') {
78
            return $this->parseContextDiff($diff);
79
        } else {
80
            return $this->parseUnifiedDiff($diff);
81
        }
82
    }
83
84
    /**
85
     * Parses an array containing the unified diff.
86
     *
87
     * @param array $diff  Array of lines.
88
     *
89
     * @return array  List of all diff operations.
90
     */
91
    function parseUnifiedDiff($diff)
92
    {
93
        $edits = array();
94
        $end = count($diff) - 1;
95
        for ($i = 0; $i < $end;) {
96
            $diff1 = array();
97
            switch (substr($diff[$i], 0, 1)) {
98
            case ' ':
99
                do {
100
                    $diff1[] = substr($diff[$i], 1);
101
                } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
102
                $edits[] = new Text_Diff_Op_copy($diff1);
103
                break;
104
105 View Code Duplication
            case '+':
106
                // get all new lines
107
                do {
108
                    $diff1[] = substr($diff[$i], 1);
109
                } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
110
                $edits[] = new Text_Diff_Op_add($diff1);
111
                break;
112
113
            case '-':
114
                // get changed or removed lines
115
                $diff2 = array();
116
                do {
117
                    $diff1[] = substr($diff[$i], 1);
118
                } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
119
120
                while ($i < $end && substr($diff[$i], 0, 1) == '+') {
121
                    $diff2[] = substr($diff[$i++], 1);
122
                }
123
                if (count($diff2) == 0) {
124
                    $edits[] = new Text_Diff_Op_delete($diff1);
125
                } else {
126
                    $edits[] = new Text_Diff_Op_change($diff1, $diff2);
127
                }
128
                break;
129
130
            default:
131
                $i++;
132
                break;
133
            }
134
        }
135
136
        return $edits;
137
    }
138
139
    /**
140
     * Parses an array containing the context diff.
141
     *
142
     * @param array $diff  Array of lines.
143
     *
144
     * @return array  List of all diff operations.
145
     */
146
    function parseContextDiff(&$diff)
147
    {
148
        $edits = array();
149
        $i = $max_i = $j = $max_j = 0;
150
        $end = count($diff) - 1;
151
        while ($i < $end && $j < $end) {
152
            while ($i >= $max_i && $j >= $max_j) {
153
                // Find the boundaries of the diff output of the two files
154
                for ($i = $j;
155
                     $i < $end && substr($diff[$i], 0, 3) == '***';
156
                     $i++);
157
                for ($max_i = $i;
158
                     $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
159
                     $max_i++);
160
                for ($j = $max_i;
161
                     $j < $end && substr($diff[$j], 0, 3) == '---';
162
                     $j++);
163
                for ($max_j = $j;
164
                     $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
165
                     $max_j++);
166
            }
167
168
            // find what hasn't been changed
169
            $array = array();
170
            while ($i < $max_i &&
171
                   $j < $max_j &&
172
                   strcmp($diff[$i], $diff[$j]) == 0) {
173
                $array[] = substr($diff[$i], 2);
174
                $i++;
175
                $j++;
176
            }
177
178 View Code Duplication
            while ($i < $max_i && ($max_j-$j) <= 1) {
179
                if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
180
                    break;
181
                }
182
                $array[] = substr($diff[$i++], 2);
183
            }
184
185 View Code Duplication
            while ($j < $max_j && ($max_i-$i) <= 1) {
186
                if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
187
                    break;
188
                }
189
                $array[] = substr($diff[$j++], 2);
190
            }
191
            if (count($array) > 0) {
192
                $edits[] = new Text_Diff_Op_copy($array);
193
            }
194
195
            if ($i < $max_i) {
196
                $diff1 = array();
197
                switch (substr($diff[$i], 0, 1)) {
198
                case '!':
199
                    $diff2 = array();
200
                    do {
201
                        $diff1[] = substr($diff[$i], 2);
202
                        if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
203
                            $diff2[] = substr($diff[$j++], 2);
204
                        }
205
                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
206
                    $edits[] = new Text_Diff_Op_change($diff1, $diff2);
207
                    break;
208
209 View Code Duplication
                case '+':
210
                    do {
211
                        $diff1[] = substr($diff[$i], 2);
212
                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
213
                    $edits[] = new Text_Diff_Op_add($diff1);
214
                    break;
215
216
                case '-':
217
                    do {
218
                        $diff1[] = substr($diff[$i], 2);
219
                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
220
                    $edits[] = new Text_Diff_Op_delete($diff1);
221
                    break;
222
                }
223
            }
224
225
            if ($j < $max_j) {
226
                $diff2 = array();
227
                switch (substr($diff[$j], 0, 1)) {
228 View Code Duplication
                case '+':
229
                    do {
230
                        $diff2[] = substr($diff[$j++], 2);
231
                    } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
232
                    $edits[] = new Text_Diff_Op_add($diff2);
233
                    break;
234
235 View Code Duplication
                case '-':
236
                    do {
237
                        $diff2[] = substr($diff[$j++], 2);
238
                    } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
239
                    $edits[] = new Text_Diff_Op_delete($diff2);
240
                    break;
241
                }
242
            }
243
        }
244
245
        return $edits;
246
    }
247
248
}
249