21
21
class HtmlArray extends RendererAbstract
22
22
{
23
23
/**
24
- * @var array Array of the default options that apply to this renderer.
24
+ * @var array Associative array containing the default options available for this renderer and their default
25
+ * value.
26
+ * - tabSize The amount of spaces to replace a tab character with.
27
+ * - title_a Title of the "old" version of text.
28
+ * - title_b Title of the "new" version of text.
25
29
*/
26
- protected $ defaultOptions = array (
30
+ protected $ defaultOptions = [
27
31
'tabSize ' => 4 ,
28
32
'title_a ' => 'Old Version ' ,
29
33
'title_b ' => 'New Version ' ,
30
- ) ;
34
+ ] ;
31
35
32
36
/**
33
- * @param string|array $changes
34
- * @param SideBySide|Inline $object
35
- * @return string
37
+ * Generate a string representation of changes between the "old and "new" sequences.
38
+ *
39
+ * This method is called by the renderers which extends this class.
40
+ *
41
+ * @param array $changes Contains the op-codes about the differences between "old and "new".
42
+ * @param SideBySide|Inline $htmlRenderer Renderer which extends this class.
43
+ *
44
+ * @return string HTML representation of the differences.
36
45
*/
37
- public function renderHtml ($ changes , $ object)
46
+ public function renderHtml (array $ changes , object $ htmlRenderer ): string
38
47
{
39
- $ html = '' ;
40
48
if (empty ($ changes )) {
41
- return $ html ;
49
+ //No changes between "old" and "new"
50
+ return 'No differences found. ' ;
42
51
}
43
52
44
- $ html . = $ object ->generateTableHeader ();
53
+ $ html = $ htmlRenderer ->generateTableHeader ();
45
54
46
55
foreach ($ changes as $ i => $ blocks ) {
47
- // If this is a separate block, we're condensing code so output ...,
48
- // indicating a significant portion of the code has been collapsed as
49
- // it is the same
56
+ if ($ i > 0 ) {
57
+ // If this is a separate block, we're condensing code so output ...,
58
+ // indicating a significant portion of the code has been collapsed as
59
+ // it is the same.
60
+ //TODO: When is $i > 0 ?
61
+ $ html .= '<span class="Skipped"><br>…<br></span> ' ;
62
+ }
50
63
51
64
foreach ($ blocks as $ change ) {
52
65
$ html .= '<tbody class="Change ' . ucfirst ($ change ['tag ' ]) . '"> ' ;
53
66
switch ($ change ['tag ' ]) {
54
67
// Equal changes should be shown on both sides of the diff
55
68
case 'equal ' :
56
- $ html .= $ object ->generateTableRowsEqual ($ change );
69
+ $ html .= $ htmlRenderer ->generateTableRowsEqual ($ change );
57
70
break ;
58
71
// Added lines only on the right side
59
72
case 'insert ' :
60
- $ html .= $ object ->generateTableRowsInsert ($ change );
73
+ $ html .= $ htmlRenderer ->generateTableRowsInsert ($ change );
61
74
break ;
62
75
// Show deleted lines only on the left side
63
76
case 'delete ' :
64
- $ html .= $ object ->generateTableRowsDelete ($ change );
77
+ $ html .= $ htmlRenderer ->generateTableRowsDelete ($ change );
65
78
break ;
66
79
// Show modified lines on both sides
67
80
case 'replace ' :
68
- $ html .= $ object ->generateTableRowsReplace ($ change );
81
+ $ html .= $ htmlRenderer ->generateTableRowsReplace ($ change );
69
82
break ;
70
83
}
84
+
71
85
$ html .= '</tbody> ' ;
72
86
}
73
87
}
88
+
74
89
$ html .= '</table> ' ;
90
+
75
91
return $ html ;
76
92
}
93
+
77
94
/**
78
- * Render and return an array structure suitable for generating HTML
79
- * based differences. Generally called by subclasses that generate a
80
- * HTML based diff and return an array of the changes to show in the diff.
95
+ * Render and return an array structure suitable for generating HTML based differences.
81
96
*
82
- * @return array|string An array of the generated changes, suitable for presentation in HTML.
97
+ * Generally called by classes which extend this class and that generate a HTML based diff by returning an array of
98
+ * the changes to show in the diff.
99
+ *
100
+ * @return array An array of the generated changes, suitable for presentation in HTML.
83
101
*/
84
102
public function render ()
85
103
{
86
- // As we'll be modifying old & new to include our change markers,
87
- // we need to get the contents and store them here. That way
88
- // we're not going to destroy the original data
104
+ // "old" & "new" are copied so change markers can be added without modifying the original sequences.
89
105
$ old = $ this ->diff ->getOld ();
90
106
$ new = $ this ->diff ->getNew ();
91
107
92
- $ changes = array () ;
108
+ $ changes = [] ;
93
109
$ opCodes = $ this ->diff ->getGroupedOpcodes ();
110
+
94
111
foreach ($ opCodes as $ group ) {
95
- $ blocks = array () ;
96
- $ lastTag = null ;
97
- $ lastBlock = 0 ;
112
+ $ blocks = [] ;
113
+ $ lastTag = null ;
114
+ $ lastBlock = 0 ;
98
115
foreach ($ group as $ code ) {
99
116
list ($ tag , $ i1 , $ i2 , $ j1 , $ j2 ) = $ code ;
100
117
101
118
if ($ tag == 'replace ' && $ i2 - $ i1 == $ j2 - $ j1 ) {
102
119
for ($ i = 0 ; $ i < ($ i2 - $ i1 ); ++$ i ) {
103
- $ fromLine = $ old [$ i1 + $ i ];
104
- $ toLine = $ new [$ j1 + $ i ];
120
+ $ fromLine = $ old [$ i1 + $ i ];
121
+ $ toLine = $ new [$ j1 + $ i ];
105
122
106
123
list ($ start , $ end ) = $ this ->getChangeExtent ($ fromLine , $ toLine );
107
124
if ($ start != 0 || $ end != 0 ) {
108
- $ realEnd = mb_strlen ($ fromLine ) + $ end ;
125
+ $ realEnd = mb_strlen ($ fromLine ) + $ end ;
126
+ $ fromLine = mb_substr ($ fromLine , 0 , $ start ) . "\0" .
127
+ mb_substr ($ fromLine , $ start , $ realEnd - $ start ) . "\1" .
128
+ mb_substr ($ fromLine , $ realEnd );
109
129
110
- $ fromLine = mb_substr ($ fromLine , 0 , $ start ) . "\0" .
111
- mb_substr ($ fromLine , $ start , $ realEnd - $ start ) . "\1" . mb_substr ($ fromLine , $ realEnd );
130
+ $ realEnd = mb_strlen ($ toLine ) + $ end ;
131
+ $ toLine = mb_substr ($ toLine , 0 , $ start ) . "\0" .
132
+ mb_substr ($ toLine , $ start , $ realEnd - $ start ) . "\1" .
133
+ mb_substr ($ toLine , $ realEnd );
112
134
113
- $ realEnd = mb_strlen ($ toLine ) + $ end ;
114
-
115
- $ toLine = mb_substr ($ toLine , 0 , $ start ) .
116
- "\0" . mb_substr ($ toLine , $ start , $ realEnd - $ start ) . "\1" .
117
- mb_substr ($ toLine , $ realEnd );
118
-
119
- $ old [$ i1 + $ i ] = $ fromLine ;
120
- $ new [$ j1 + $ i ] = $ toLine ;
135
+ $ old [$ i1 + $ i ] = $ fromLine ;
136
+ $ new [$ j1 + $ i ] = $ toLine ;
121
137
}
122
138
}
123
139
}
124
140
125
141
if ($ tag != $ lastTag ) {
126
- $ blocks [] = $ this ->getDefaultArray ($ tag , $ i1 , $ j1 );
127
- $ lastBlock = count ($ blocks ) - 1 ;
142
+ $ blocks [] = $ this ->getDefaultArray ($ tag , $ i1 , $ j1 );
143
+ $ lastBlock = count ($ blocks ) - 1 ;
128
144
}
129
145
130
146
$ lastTag = $ tag ;
131
147
132
148
if ($ tag == 'equal ' ) {
133
149
$ lines = array_slice ($ old , $ i1 , ($ i2 - $ i1 ));
134
150
$ blocks [$ lastBlock ]['base ' ]['lines ' ] += $ this ->formatLines ($ lines );
151
+
135
152
$ lines = array_slice ($ new , $ j1 , ($ j2 - $ j1 ));
136
153
$ blocks [$ lastBlock ]['changed ' ]['lines ' ] += $ this ->formatLines ($ lines );
137
154
} else {
@@ -152,113 +169,109 @@ public function render()
152
169
}
153
170
$ changes [] = $ blocks ;
154
171
}
172
+
155
173
return $ changes ;
156
174
}
157
175
158
176
/**
159
- * Given two strings, determine where the changes in the two strings
160
- * begin, and where the changes in the two strings end.
177
+ * Determine where changes in two strings begin and where they end.
178
+ *
179
+ * This returns an array.
180
+ * The first value defines the first (starting at 0) character from start of the old string which is different.
181
+ * The second value defines the last character from end of the old string which is different.
182
+ *
183
+ *
184
+ * @param string $oldString The first string to compare.
185
+ * @param string $newString The second string to compare.
161
186
*
162
- * @param string $fromLine The first string.
163
- * @param string $toLine The second string.
164
187
* @return array Array containing the starting position (0 by default) and the ending position (-1 by default)
165
188
*/
166
- private function getChangeExtent (string $ fromLine , string $ toLine )
189
+ private function getChangeExtent (string $ oldString , string $ newString ): array
167
190
{
168
191
$ start = 0 ;
169
- $ limit = min (mb_strlen ($ fromLine ), mb_strlen ($ toLine ));
170
- while ($ start < $ limit && mb_substr ($ fromLine , $ start , 1 ) == mb_substr ($ toLine , $ start , 1 )) {
192
+ $ limit = min (mb_strlen ($ oldString ), mb_strlen ($ newString ));
193
+
194
+ // Find first difference.
195
+ while ($ start < $ limit && mb_substr ($ oldString , $ start , 1 ) == mb_substr ($ newString , $ start , 1 )) {
171
196
++$ start ;
172
197
}
173
- $ end = -1 ;
174
- $ limit = $ limit - $ start ;
175
- while (-$ end <= $ limit && mb_substr ($ fromLine , $ end , 1 ) == mb_substr ($ toLine , $ end , 1 )) {
198
+
199
+ $ end = -1 ;
200
+ $ limit = $ limit - $ start ;
201
+
202
+ // Find last difference.
203
+ while (-$ end <= $ limit && mb_substr ($ oldString , $ end , 1 ) == mb_substr ($ newString , $ end , 1 )) {
176
204
--$ end ;
177
205
}
178
- return array (
206
+
207
+ return [
179
208
$ start ,
180
209
$ end + 1
181
- ) ;
210
+ ] ;
182
211
}
183
212
184
213
/**
185
- * Format a series of lines suitable for output in a HTML rendered diff.
186
- * This involves replacing tab characters with spaces, making the HTML safe
187
- * for output, ensuring that double spaces are replaced with   etc.
214
+ * Format a series of strings which are suitable for output in a HTML rendered diff.
215
+ *
216
+ * This involves replacing tab characters with spaces, making the HTML safe for output by ensuring that double
217
+ * spaces are replaced with etc.
188
218
*
189
- * @param array $lines Array of lines to format.
190
- * @return array Array of the formatted lines.
219
+ * @param array $lines Array of strings to format.
220
+ *
221
+ * @return array Array of formatted strings.
191
222
*/
192
- protected function formatLines (array $ lines ): array
223
+ protected function formatLines (array $ strings ): array
193
224
{
194
225
if ($ this ->options ['tabSize ' ] !== false ) {
195
- $ lines = array_map (
226
+ // Replace tabs with spaces.
227
+ $ strings = array_map (
196
228
function ($ item ) {
197
229
return $ this ->expandTabs ($ item );
198
230
},
199
- $ lines
231
+ $ strings
200
232
);
201
233
}
202
- $ lines = array_map (
234
+
235
+ // Convert special characters to HTML entities
236
+ $ strings = array_map (
203
237
function ($ item ) {
204
238
return $ this ->htmlSafe ($ item );
205
239
},
206
- $ lines
240
+ $ strings
207
241
);
208
- foreach ($ lines as &$ line ) {
209
- $ line = preg_replace_callback ('# ( +)|^ # ' , array ($ this , 'fixSpaces ' ), $ line );
210
- }
211
- return $ lines ;
212
- }
213
242
214
- /**
215
- * Replace a string containing spaces with a HTML representation using  .
216
- *
217
- * @param array $matches The string of spaces.
218
- * @return string The HTML representation of the string.
219
- */
220
- protected function fixSpaces (array $ matches ): string
221
- {
222
- $ buffer = '' ;
223
- $ count = 0 ;
224
- foreach ($ matches as $ spaces ) {
225
- $ count = strlen ($ spaces );
226
- if ($ count == 0 ) {
227
- continue ;
228
- }
229
- $ div = (int ) ($ count / 2 );
230
- $ mod = $ count % 2 ;
231
- $ buffer .= str_repeat ('  ' , $ div ) . str_repeat ('  ' , $ mod );
243
+ // Replace leading spaces of a line with HTML enities.
244
+ foreach ($ strings as &$ line ) {
245
+ $ line = preg_replace_callback (
246
+ '/(^[ \0\1]*)/ ' ,
247
+ function ($ matches ) {
248
+ return str_replace (' ' , " " , $ matches [0 ]);
249
+ },
250
+ $ line
251
+ );
232
252
}
253
+ unset($ line );
233
254
234
- $ div = (int ) ($ count / 2 );
235
- $ mod = $ count % 2 ;
236
- return str_repeat ('  ' , $ div ) . str_repeat ('  ' , $ mod );
255
+ return $ strings ;
237
256
}
238
257
239
258
/**
240
- * Replace tabs in a single line with a number of spaces as defined by the tabSize option.
259
+ * Replace tabs in a string with an amount of spaces as defined by the tabSize option of this class.
260
+ *
261
+ * @param string $line The string which contains tabs to convert.
241
262
*
242
- * @param string $line The containing tabs to convert.
243
263
* @return string The line with the tabs converted to spaces.
244
264
*/
245
265
private function expandTabs (string $ line ): string
246
266
{
247
- $ tabSize = $ this ->options ['tabSize ' ];
248
- while (($ pos = strpos ($ line , "\t" )) !== false ) {
249
- $ left = substr ($ line , 0 , $ pos );
250
- $ right = substr ($ line , $ pos + 1 );
251
- $ length = $ tabSize - ($ pos % $ tabSize );
252
- $ spaces = str_repeat (' ' , $ length );
253
- $ line = $ left . $ spaces . $ right ;
254
- }
255
- return $ line ;
267
+ return str_replace ("\t" , str_repeat (' ' , $ this ->options ['tabSize ' ]), $ line );
256
268
}
257
269
258
270
/**
259
- * Make a string containing HTML safe for output on a page.
271
+ * Make a string HTML safe for output on a page.
272
+ *
273
+ * @param string $string The string to make safe.
260
274
*
261
- * @param string $string The string.
262
275
* @return string The string with the HTML characters replaced by entities.
263
276
*/
264
277
private function htmlSafe (string $ string ): string
@@ -267,24 +280,26 @@ private function htmlSafe(string $string): string
267
280
}
268
281
269
282
/**
270
- * @param string $tag
271
- * @param integer $i1
272
- * @param integer $j1
283
+ * Helper function that provides an array for the renderer with default values for the changes to render.
284
+ *
285
+ * @param string $tag Kind of difference.
286
+ * @param integer $lineInOld Start of block in "old".
287
+ * @param integer $lineInNew Start of block in "new".
288
+ *
273
289
* @return array
274
290
*/
275
- private function getDefaultArray (string $ tag , int $ i1 , int $ j1 ): array
291
+ private function getDefaultArray (string $ tag , int $ lineInOld , int $ lineInNew ): array
276
292
{
277
- return array
278
- (
279
- 'tag ' => $ tag ,
280
- 'base ' => array (
281
- 'offset ' => $ i1 ,
282
- 'lines ' => array ()
283
- ),
284
- 'changed ' => array (
285
- 'offset ' => $ j1 ,
286
- 'lines ' => array ()
287
- )
288
- );
293
+ return [
294
+ 'tag ' => $ tag ,
295
+ 'base ' => [
296
+ 'offset ' => $ lineInOld ,
297
+ 'lines ' => []
298
+ ],
299
+ 'changed ' => [
300
+ 'offset ' => $ lineInNew ,
301
+ 'lines ' => []
302
+ ]
303
+ ];
289
304
}
290
305
}
0 commit comments