Completed
Push — 3.5 ( 1a9180...1bec8a )
by Daniel
24s
created

_DiffOp_Add::_DiffOp_Add()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @package framework
5
 * @subpackage core
6
 * A PHP diff engine
7
 */
8
9
// difflib.php
10
//
11
// A PHP diff engine for phpwiki.
12
//
13
// Copyright (C) 2000, 2001 Geoffrey T. Dairiki <[email protected]>
14
// You may copy this code freely under the conditions of the GPL.
15
//
16
17
// FIXME: possibly remove assert()'s for production version?
18
19
// PHP3 does not have assert()
20
/**
21
 */
22
define('USE_ASSERTS', function_exists('assert'));
23
24
/**
25
 * @package framework
26
 * @subpackage core
27
 * @access private
28
 */
29
class _DiffOp {
30
	var $type;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $type.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
31
	var $orig;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $orig.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
32
	var $final;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $final.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
33
34
	public function reverse() {
35
		trigger_error("pure virtual", E_USER_ERROR);
36
	}
37
38
	public function norig() {
39
		return $this->orig ? sizeof($this->orig) : 0;
40
	}
41
42
	public function nfinal() {
43
		return $this->final ? sizeof($this->final) : 0;
44
	}
45
}
46
47
/**
48
 * @package framework
49
 * @subpackage core
50
 * @access private
51
 */
52
class _DiffOp_Copy extends _DiffOp {
53
	var $type = 'copy';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $type.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
54
55
	public function _DiffOp_Copy ($orig, $final = false) {
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
56
		if (!is_array($final))
57
			$final = $orig;
58
		$this->orig = $orig;
59
		$this->final = $final;
60
	}
61
62
	public function reverse() {
63
		return new _DiffOp_Copy($this->final, $this->orig);
64
	}
65
}
66
67
/**
68
 * @package framework
69
 * @subpackage core
70
 * @access private
71
 */
72
class _DiffOp_Delete extends _DiffOp {
73
	var $type = 'delete';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $type.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
74
75
	public function _DiffOp_Delete ($lines) {
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
76
		$this->orig = $lines;
77
		$this->final = false;
78
	}
79
80
	public function reverse() {
81
		return new _DiffOp_Add($this->orig);
82
	}
83
}
84
85
/**
86
 * @package framework
87
 * @subpackage core
88
 * @access private
89
 */
90
class _DiffOp_Add extends _DiffOp {
91
	var $type = 'add';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $type.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
92
93
	public function _DiffOp_Add ($lines) {
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
94
		$this->final = $lines;
95
		$this->orig = false;
96
	}
97
98
	public function reverse() {
99
		return new _DiffOp_Delete($this->final);
100
	}
101
}
102
103
/**
104
 * @package framework
105
 * @subpackage core
106
 * @access private
107
 */
108
class _DiffOp_Change extends _DiffOp {
109
	var $type = 'change';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $type.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
110
111
	public function _DiffOp_Change ($orig, $final) {
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
112
		$this->orig = $orig;
113
		$this->final = $final;
114
	}
115
116
	public function reverse() {
117
		return new _DiffOp_Change($this->final, $this->orig);
118
	}
119
}
120
121
122
/**
123
 * Class used internally by Diff to actually compute the diffs.
124
 *
125
 * The algorithm used here is mostly lifted from the perl module
126
 * Algorithm::Diff (version 1.06) by Ned Konz, which is available at:
127
 *   http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip
128
 *
129
 * More ideas are taken from:
130
 *   http://www.ics.uci.edu/~eppstein/161/960229.html
131
 *
132
 * Some ideas are (and a bit of code) are from from analyze.c, from GNU
133
 * diffutils-2.7, which can be found at:
134
 *   ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
135
 *
136
 * Finally, some ideas (subdivision by NCHUNKS > 2, and some optimizations)
137
 * are my own.
138
 *
139
 * @author Geoffrey T. Dairiki
140
 * @access private
141
 * @package framework
142
 * @subpackage core
143
 */
144
class _DiffEngine
145
{
146
	public function diff ($from_lines, $to_lines) {
147
		$n_from = sizeof($from_lines);
148
		$n_to = sizeof($to_lines);
149
150
		$this->xchanged = $this->ychanged = array();
0 ignored issues
show
Bug introduced by
The property xchanged does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property ychanged does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
151
		$this->xv = $this->yv = array();
0 ignored issues
show
Bug introduced by
The property xv does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property yv does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
152
		$this->xind = $this->yind = array();
0 ignored issues
show
Bug introduced by
The property xind does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property yind does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
153
		unset($this->seq);
154
		unset($this->in_seq);
155
		unset($this->lcs);
156
157
		// Skip leading common lines.
158
		for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
159
			if ($from_lines[$skip] != $to_lines[$skip])
160
				break;
161
			$this->xchanged[$skip] = $this->ychanged[$skip] = false;
162
		}
163
		// Skip trailing common lines.
164
		$xi = $n_from; $yi = $n_to;
165
		for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
166
			if ($from_lines[$xi] != $to_lines[$yi])
167
				break;
168
			$this->xchanged[$xi] = $this->ychanged[$yi] = false;
169
		}
170
171
		// Ignore lines which do not exist in both files.
172
		for ($xi = $skip; $xi < $n_from - $endskip; $xi++)
173
			$xhash[$from_lines[$xi]] = 1;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$xhash was never initialized. Although not strictly required by PHP, it is generally a good practice to add $xhash = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
174
		for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
175
			$line = $to_lines[$yi];
176
			if ( ($this->ychanged[$yi] = empty($xhash[$line])) )
177
				continue;
178
			$yhash[$line] = 1;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$yhash was never initialized. Although not strictly required by PHP, it is generally a good practice to add $yhash = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
179
			$this->yv[] = $line;
180
			$this->yind[] = $yi;
181
		}
182
		for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
183
			$line = $from_lines[$xi];
184
			if ( ($this->xchanged[$xi] = empty($yhash[$line])) )
185
				continue;
186
			$this->xv[] = $line;
187
			$this->xind[] = $xi;
188
		}
189
190
		// Find the LCS.
191
		$this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
192
193
		// Merge edits when possible
194
		$this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
195
		$this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
196
197
		// Compute the edit operations.
198
		$edits = array();
199
		$xi = $yi = 0;
200
		while ($xi < $n_from || $yi < $n_to) {
201
			USE_ASSERTS && assert($yi < $n_to || $this->xchanged[$xi]);
202
			USE_ASSERTS && assert($xi < $n_from || $this->ychanged[$yi]);
203
204
			// Skip matching "snake".
205
			$copy = array();
206
			while ( $xi < $n_from && $yi < $n_to
207
					&& !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
208
				$copy[] = $from_lines[$xi++];
209
				++$yi;
210
			}
211
			if ($copy)
0 ignored issues
show
Bug Best Practice introduced by
The expression $copy of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
212
				$edits[] = new _DiffOp_Copy($copy);
213
214
			// Find deletes & adds.
215
			$delete = array();
216
			while ($xi < $n_from && $this->xchanged[$xi])
217
				$delete[] = $from_lines[$xi++];
218
219
			$add = array();
220
			while ($yi < $n_to && $this->ychanged[$yi])
221
				$add[] = $to_lines[$yi++];
222
223
			if ($delete && $add)
0 ignored issues
show
Bug Best Practice introduced by
The expression $delete of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $add of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
224
				$edits[] = new _DiffOp_Change($delete, $add);
225
			elseif ($delete)
0 ignored issues
show
Bug Best Practice introduced by
The expression $delete of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
226
				$edits[] = new _DiffOp_Delete($delete);
227
			elseif ($add)
0 ignored issues
show
Bug Best Practice introduced by
The expression $add of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
228
				$edits[] = new _DiffOp_Add($add);
229
		}
230
		return $edits;
231
	}
232
233
234
	/* Divide the Largest Common Subsequence (LCS) of the sequences
235
	 * [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally
236
	 * sized segments.
237
	 *
238
	 * Returns (LCS, PTS).  LCS is the length of the LCS. PTS is an
239
	 * array of NCHUNKS+1 (X, Y) indexes giving the diving points between
240
	 * sub sequences.  The first sub-sequence is contained in [X0, X1),
241
	 * [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on.  Note
242
	 * that (X0, Y0) == (XOFF, YOFF) and
243
	 * (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
244
	 *
245
	 * This function assumes that the first lines of the specified portions
246
	 * of the two files do not match, and likewise that the last lines do not
247
	 * match.  The caller must trim matching lines from the beginning and end
248
	 * of the portions it is going to specify.
249
	 */
250
	public function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) {
251
	$flip = false;
252
253
	if ($xlim - $xoff > $ylim - $yoff) {
254
		// Things seems faster (I'm not sure I understand why)
255
			// when the shortest sequence in X.
256
			$flip = true;
257
		list ($xoff, $xlim, $yoff, $ylim)
258
		= array( $yoff, $ylim, $xoff, $xlim);
259
		}
260
261
	if ($flip)
262
		for ($i = $ylim - 1; $i >= $yoff; $i--)
263
		$ymatches[$this->xv[$i]][] = $i;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ymatches was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ymatches = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
264
	else
265
		for ($i = $ylim - 1; $i >= $yoff; $i--)
266
		$ymatches[$this->yv[$i]][] = $i;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ymatches was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ymatches = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
267
268
	$this->lcs = 0;
0 ignored issues
show
Bug introduced by
The property lcs does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
269
	$this->seq[0]= $yoff - 1;
0 ignored issues
show
Bug introduced by
The property seq does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
270
	$this->in_seq = array();
0 ignored issues
show
Bug introduced by
The property in_seq does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
271
	$ymids[0] = array();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ymids was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ymids = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
272
273
	$numer = $xlim - $xoff + $nchunks - 1;
274
	$x = $xoff;
275
	for ($chunk = 0; $chunk < $nchunks; $chunk++) {
276
		if ($chunk > 0)
277
		for ($i = 0; $i <= $this->lcs; $i++)
278
			$ymids[$i][$chunk-1] = $this->seq[$i];
279
280
		$x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
281
		for ( ; $x < $x1; $x++) {
282
				$line = $flip ? $this->yv[$x] : $this->xv[$x];
283
				if (empty($ymatches[$line]))
284
			continue;
285
		$matches = $ymatches[$line];
286
				reset($matches);
287
		while (list ($junk, $y) = each($matches))
0 ignored issues
show
Unused Code introduced by
The assignment to $junk is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
288
			if (empty($this->in_seq[$y])) {
289
			$k = $this->_lcs_pos($y);
290
			USE_ASSERTS && assert($k > 0);
291
			$ymids[$k] = $ymids[$k-1];
292
			break;
293
					}
294
		while (list ($junk, $y) = each($matches)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $junk is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
295
			if ($y > $this->seq[$k-1]) {
296
			USE_ASSERTS && assert($y < $this->seq[$k]);
0 ignored issues
show
Bug introduced by
The variable $k does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
297
			// Optimization: this is a common case:
298
			//  next match is just replacing previous match.
299
			$this->in_seq[$this->seq[$k]] = false;
300
			$this->seq[$k] = $y;
301
			$this->in_seq[$y] = 1;
302
					}
303
			else if (empty($this->in_seq[$y])) {
304
			$k = $this->_lcs_pos($y);
305
			USE_ASSERTS && assert($k > 0);
306
			$ymids[$k] = $ymids[$k-1];
307
					}
308
				}
309
			}
310
		}
311
312
	$seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$seps was never initialized. Although not strictly required by PHP, it is generally a good practice to add $seps = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
313
	$ymid = $ymids[$this->lcs];
314
	for ($n = 0; $n < $nchunks - 1; $n++) {
315
		$x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
316
		$y1 = $ymid[$n] + 1;
317
		$seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
318
		}
319
	$seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
320
321
	return array($this->lcs, $seps);
322
	}
323
324
	public function _lcs_pos ($ypos) {
325
	$end = $this->lcs;
326
	if ($end == 0 || $ypos > $this->seq[$end]) {
327
		$this->seq[++$this->lcs] = $ypos;
328
		$this->in_seq[$ypos] = 1;
329
		return $this->lcs;
330
		}
331
332
	$beg = 1;
333
	while ($beg < $end) {
334
		$mid = (int)(($beg + $end) / 2);
335
		if ( $ypos > $this->seq[$mid] )
336
		$beg = $mid + 1;
337
		else
338
		$end = $mid;
339
		}
340
341
	USE_ASSERTS && assert($ypos != $this->seq[$end]);
342
343
	$this->in_seq[$this->seq[$end]] = false;
344
	$this->seq[$end] = $ypos;
345
	$this->in_seq[$ypos] = 1;
346
	return $end;
347
	}
348
349
	/* Find LCS of two sequences.
350
	 *
351
	 * The results are recorded in the vectors $this->{x,y}changed[], by
352
	 * storing a 1 in the element for each line that is an insertion
353
	 * or deletion (ie. is not in the LCS).
354
	 *
355
	 * The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
356
	 *
357
	 * Note that XLIM, YLIM are exclusive bounds.
358
	 * All line numbers are origin-0 and discarded lines are not counted.
359
	 */
360
	public function _compareseq ($xoff, $xlim, $yoff, $ylim) {
361
	// Slide down the bottom initial diagonal.
362
	while ($xoff < $xlim && $yoff < $ylim
363
			&& $this->xv[$xoff] == $this->yv[$yoff]) {
364
		++$xoff;
365
		++$yoff;
366
		}
367
368
	// Slide up the top initial diagonal.
369
	while ($xlim > $xoff && $ylim > $yoff
370
			&& $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
371
		--$xlim;
372
		--$ylim;
373
		}
374
375
	if ($xoff == $xlim || $yoff == $ylim)
376
		$lcs = 0;
377
	else {
378
		// This is ad hoc but seems to work well.
379
		//$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5);
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
380
		//$nchunks = max(2,min(8,(int)$nchunks));
0 ignored issues
show
Unused Code Comprehensibility introduced by
71% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
381
		$nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
382
		list ($lcs, $seps)
383
		= $this->_diag($xoff,$xlim,$yoff, $ylim,$nchunks);
384
		}
385
386
	if ($lcs == 0) {
387
		// X and Y sequences have no common subsequence:
388
		// mark all changed.
389
		while ($yoff < $ylim)
390
		$this->ychanged[$this->yind[$yoff++]] = 1;
391
		while ($xoff < $xlim)
392
		$this->xchanged[$this->xind[$xoff++]] = 1;
393
		}
394
	else {
395
		// Use the partitions to split this problem into subproblems.
396
		reset($seps);
397
		$pt1 = $seps[0];
398
		while ($pt2 = next($seps)) {
399
		$this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
400
		$pt1 = $pt2;
401
			}
402
		}
403
	}
404
405
	/* Adjust inserts/deletes of identical lines to join changes
406
	 * as much as possible.
407
	 *
408
	 * We do something when a run of changed lines include a
409
	 * line at one end and has an excluded, identical line at the other.
410
	 * We are free to choose which identical line is included.
411
	 * 'compareseq' usually chooses the one at the beginning,
412
	 * but usually it is cleaner to consider the following identical line
413
	 * to be the "change".
414
	 *
415
	 * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
416
	 */
417
	public function _shift_boundaries ($lines, &$changed, $other_changed) {
418
	$i = 0;
419
	$j = 0;
420
421
	USE_ASSERTS && assert('sizeof($lines) == sizeof($changed)');
422
	$len = sizeof($lines);
423
	$other_len = sizeof($other_changed);
424
425
	while (1) {
426
		/*
427
		 * Scan forwards to find beginning of another run of changes.
428
		 * Also keep track of the corresponding point in the other file.
429
		 *
430
		 * Throughout this code, $i and $j are adjusted together so that
431
		 * the first $i elements of $changed and the first $j elements
432
		 * of $other_changed both contain the same number of zeros
433
		 * (unchanged lines).
434
		 * Furthermore, $j is always kept so that $j == $other_len or
435
		 * $other_changed[$j] == false.
436
		 */
437
		while ($j < $other_len && $other_changed[$j])
438
		$j++;
439
440
		while ($i < $len && ! $changed[$i]) {
441
		USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
442
		$i++; $j++;
443
		while ($j < $other_len && $other_changed[$j])
444
			$j++;
445
			}
446
447
		if ($i == $len)
448
		break;
449
450
		$start = $i;
451
452
		// Find the end of this run of changes.
453
		while (++$i < $len && $changed[$i])
454
		continue;
455
456
		do {
457
		/*
458
		 * Record the length of this run of changes, so that
459
		 * we can later determine whether the run has grown.
460
		 */
461
		$runlength = $i - $start;
462
463
		/*
464
		 * Move the changed region back, so long as the
465
		 * previous unchanged line matches the last changed one.
466
		 * This merges with previous changed regions.
467
		 */
468
		while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
469
			$changed[--$start] = 1;
470
			$changed[--$i] = false;
471
			while ($start > 0 && $changed[$start - 1])
472
			$start--;
473
			USE_ASSERTS && assert('$j > 0');
474
			while ($other_changed[--$j])
475
			continue;
476
			USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
477
				}
478
479
		/*
480
		 * Set CORRESPONDING to the end of the changed run, at the last
481
		 * point where it corresponds to a changed run in the other file.
482
		 * CORRESPONDING == LEN means no such point has been found.
483
		 */
484
		$corresponding = $j < $other_len ? $i : $len;
485
486
		/*
487
		 * Move the changed region forward, so long as the
488
		 * first changed line matches the following unchanged one.
489
		 * This merges with following changed regions.
490
		 * Do this second, so that if there are no merges,
491
		 * the changed region is moved forward as far as possible.
492
		 */
493
		while ($i < $len && $lines[$start] == $lines[$i]) {
494
			$changed[$start++] = false;
495
			$changed[$i++] = 1;
496
			while ($i < $len && $changed[$i])
497
			$i++;
498
499
			USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
500
			$j++;
501
			if ($j < $other_len && $other_changed[$j]) {
502
			$corresponding = $i;
503
			while ($j < $other_len && $other_changed[$j])
504
				$j++;
505
					}
506
				}
507
			} while ($runlength != $i - $start);
508
509
		/*
510
		 * If possible, move the fully-merged run of changes
511
		 * back to a corresponding run in the other file.
512
		 */
513
		while ($corresponding < $i) {
514
		$changed[--$start] = 1;
515
		$changed[--$i] = 0;
516
		USE_ASSERTS && assert('$j > 0');
517
		while ($other_changed[--$j])
518
			continue;
519
		USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
520
			}
521
		}
522
	}
523
}
524
525
/**
526
 * Class representing a 'diff' between two sequences of strings.
527
 * @package framework
528
 * @subpackage core
529
 */
530
class Diff
531
{
532
	public static $html_cleaner_class = null;
533
534
	var $edits;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $edits.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
535
536
	/**
537
	 * Constructor.
538
	 * Computes diff between sequences of strings.
539
	 *
540
	 * @param $from_lines array An array of strings.
541
	 *        (Typically these are lines from a file.)
542
	 * @param $to_lines array An array of strings.
543
	 */
544
	public function Diff($from_lines, $to_lines) {
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
545
		$eng = new _DiffEngine;
546
		$this->edits = $eng->diff($from_lines, $to_lines);
547
		//$this->_check($from_lines, $to_lines);
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
548
	}
549
550
	/**
551
	 * Compute reversed Diff.
552
	 *
553
	 * SYNOPSIS:
554
	 *
555
	 *  $diff = new Diff($lines1, $lines2);
556
	 *  $rev = $diff->reverse();
557
	 * @return object A Diff object representing the inverse of the
558
	 *                original diff.
559
	 */
560
	public function reverse () {
561
	$rev = $this;
562
		$rev->edits = array();
563
		foreach ($this->edits as $edit) {
564
			$rev->edits[] = $edit->reverse();
565
		}
566
	return $rev;
567
	}
568
569
	/**
570
	 * Check for empty diff.
571
	 *
572
	 * @return bool True iff two sequences were identical.
573
	 */
574
	public function isEmpty () {
575
		foreach ($this->edits as $edit) {
576
			if ($edit->type != 'copy')
577
				return false;
578
		}
579
		return true;
580
	}
581
582
	/**
583
	 * Compute the length of the Longest Common Subsequence (LCS).
584
	 *
585
	 * This is mostly for diagnostic purposed.
586
	 *
587
	 * @return int The length of the LCS.
588
	 */
589
	public function lcs () {
590
	$lcs = 0;
591
		foreach ($this->edits as $edit) {
592
			if ($edit->type == 'copy')
593
				$lcs += sizeof($edit->orig);
594
		}
595
	return $lcs;
596
	}
597
598
	/**
599
	 * Get the original set of lines.
600
	 *
601
	 * This reconstructs the $from_lines parameter passed to the
602
	 * constructor.
603
	 *
604
	 * @return array The original sequence of strings.
605
	 */
606
	public function orig() {
607
		$lines = array();
608
609
		foreach ($this->edits as $edit) {
610
			if ($edit->orig)
611
				array_splice($lines, sizeof($lines), 0, $edit->orig);
612
		}
613
		return $lines;
614
	}
615
616
	/**
617
	 * Get the final set of lines.
618
	 *
619
	 * This reconstructs the $to_lines parameter passed to the
620
	 * constructor.
621
	 *
622
	 * @return array The sequence of strings.
623
	 */
624
	public function finaltext() {
625
		$lines = array();
626
627
		foreach ($this->edits as $edit) {
628
			if ($edit->final)
629
				array_splice($lines, sizeof($lines), 0, $edit->final);
630
		}
631
		return $lines;
632
	}
633
634
	/**
635
	 * Check a Diff for validity.
636
	 *
637
	 * This is here only for debugging purposes.
638
	 */
639
	public function _check ($from_lines, $to_lines) {
640
		if (serialize($from_lines) != serialize($this->orig()))
641
			trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
642
		if (serialize($to_lines) != serialize($this->finaltext()))
643
			trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
644
645
		$rev = $this->reverse();
646
		if (serialize($to_lines) != serialize($rev->orig()))
647
			trigger_error("Reversed original doesn't match", E_USER_ERROR);
648
		if (serialize($from_lines) != serialize($rev->finaltext()))
649
			trigger_error("Reversed final doesn't match", E_USER_ERROR);
650
651
652
		$prevtype = 'none';
653
		foreach ($this->edits as $edit) {
654
			if ( $prevtype == $edit->type )
655
				trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
656
			$prevtype = $edit->type;
657
		}
658
659
		$lcs = $this->lcs();
660
		trigger_error("Diff okay: LCS = $lcs", E_USER_NOTICE);
661
	}
662
663
664
665
	/**
666
	 *  Attempt to clean invalid HTML, which messes up diffs.
667
	 *  This cleans code if possible, using an instance of HTMLCleaner
668
	 *
669
	 *  NB: By default, only extremely simple tidying is performed,
670
	 *  by passing through DomDocument::loadHTML and saveXML
671
	 *
672
	 * @param string $content HTML content
673
	 * @param object $cleaner Optional instance of a HTMLCleaner class to
674
	 * 	use, overriding self::$html_cleaner_class
675
	 */
676
	public static function cleanHTML($content, $cleaner=null) {
677
		if (!$cleaner) {
678
			if (class_exists(self::$html_cleaner_class)) {
679
				$cleaner = new self::$html_cleaner_class;
680
			} else {
681
				$cleaner = HTMLCleaner::inst();    //load cleaner if the dependent class is available
682
			}
683
		}
684
685
		if ($cleaner) {
686
			$content = $cleaner->cleanHTML($content);
687
		} else {
688
			// At most basic level of cleaning, use DOMDocument to save valid XML.
689
			$doc = Injector::inst()->create('HTMLValue', $content);
690
			$content = $doc->getContent();
691
		}
692
693
		// Remove empty <ins /> and <del /> tags because browsers hate them
694
		$content = preg_replace('/<(ins|del)[^>]*\/>/','', $content);
695
696
		return $content;
697
	}
698
699
	/**
700
	 * @param String
701
	 * @param String
702
	 * @param Boolean
703
	 * @return String
704
	 */
705
	public static function compareHTML($from, $to, $escape = false) {
706
		// First split up the content into words and tags
707
		$set1 = self::getHTMLChunks($from);
708
		$set2 = self::getHTMLChunks($to);
709
710
		// Diff that
711
		$diff = new Diff($set1, $set2);
712
713
		$tagStack[1] = $tagStack[2] = 0;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tagStack was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tagStack = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
714
		$rechunked[1] = $rechunked[2] = array();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$rechunked was never initialized. Although not strictly required by PHP, it is generally a good practice to add $rechunked = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
715
716
		// Go through everything, converting edited tags (and their content) into single chunks.  Otherwise
717
		// the generated HTML gets crusty
718
		foreach($diff->edits as $edit) {
719
			switch($edit->type) {
720
				case 'copy':
721
					$lookForTag = false;
722
					$stuffFor[1] = $edit->orig;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$stuffFor was never initialized. Although not strictly required by PHP, it is generally a good practice to add $stuffFor = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
723
					$stuffFor[2] = $edit->orig;
0 ignored issues
show
Bug introduced by
The variable $stuffFor does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
724
					break;
725
726
				case 'change':
727
					$lookForTag = true;
728
					$stuffFor[1] = $edit->orig;
729
					$stuffFor[2] = $edit->final;
730
					break;
731
732
				case 'add':
733
					$lookForTag = true;
734
					$stuffFor[1] = null;
735
					$stuffFor[2] = $edit->final;
736
					break;
737
738
				case 'delete':
739
					$lookForTag = true;
740
					$stuffFor[1] = $edit->orig;
741
					$stuffFor[2] = null;
742
					break;
743
			}
744
745
			foreach($stuffFor as $listName => $chunks) {
746
				if($chunks) {
747
					foreach($chunks as $item) {
748
						// $tagStack > 0 indicates that we should be tag-building
749
						if($tagStack[$listName]) $rechunked[$listName][sizeof($rechunked[$listName])-1] .= ' ' . $item;
750
						else $rechunked[$listName][] = $item;
751
752
						if($lookForTag && !$tagStack[$listName] && isset($item[0]) && $item[0] == "<"
0 ignored issues
show
Bug introduced by
The variable $lookForTag does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
753
								&& substr($item,0,2) != "</") {
754
							$tagStack[$listName] = 1;
755
						} else if($tagStack[$listName]) {
756
							if(substr($item,0,2) == "</") $tagStack[$listName]--;
757
							else if(isset($item[0]) && $item[0] == "<") $tagStack[$listName]++;
758
						}
759
					}
760
				}
761
			}
762
		}
763
764
		// Diff the re-chunked data, turning it into maked up HTML
765
		$diff = new Diff($rechunked[1], $rechunked[2]);
766
		$content = '';
767
		foreach($diff->edits as $edit) {
768
			$orig = ($escape) ? Convert::raw2xml($edit->orig) : $edit->orig;
769
			$final = ($escape) ? Convert::raw2xml($edit->final) : $edit->final;
770
771
			switch($edit->type) {
772
				case 'copy':
773
					$content .= " " . implode(" ", $orig) . " ";
774
					break;
775
776
				case 'change':
777
					$content .= " <ins>" . implode(" ", $final) . "</ins> ";
778
					$content .= " <del>" . implode(" ", $orig) . "</del> ";
779
					break;
780
781
				case 'add':
782
					$content .= " <ins>" . implode(" ", $final) . "</ins> ";
783
					break;
784
785
				case 'delete':
786
					$content .= " <del>" . implode(" ", $orig) . "</del> ";
787
					break;
788
			}
789
		}
790
791
		return self::cleanHTML($content);
792
	}
793
794
	/**
795
	 * @param string|array If passed as an array, values will be concatenated with a comma.
796
	 */
797
	public static function getHTMLChunks($content) {
798
		if($content && !is_string($content) && !is_array($content) && !is_numeric($content)) {
799
			throw new InvalidArgumentException('$content parameter needs to be a string or array');
800
		}
801
		if(is_array($content)) $content = implode(',', $content);
802
803
		$content = str_replace(array("&nbsp;","<", ">"),array(" "," <", "> "),$content);
804
		$candidateChunks = preg_split("/[\t\r\n ]+/", $content);
805
		while(list($i,$item) = each($candidateChunks)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $i is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
806
			if(isset($item[0]) && $item[0] == "<") {
807
				$newChunk = $item;
808
				while($item[strlen($item)-1] != ">") {
809
					list($i,$item) = each($candidateChunks);
0 ignored issues
show
Unused Code introduced by
The assignment to $i is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
810
					$newChunk .= ' ' . $item;
811
				}
812
				$chunks[] = $newChunk;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$chunks was never initialized. Although not strictly required by PHP, it is generally a good practice to add $chunks = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
813
			} else {
814
				$chunks[] = $item;
0 ignored issues
show
Bug introduced by
The variable $chunks does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
815
			}
816
		}
817
		return $chunks;
818
	}
819
820
}
821
822
823
824
825
/**
826
 * Computes diff between sequences of strings.
827
 * @package framework
828
 * @subpackage core
829
 */
830
class MappedDiff
831
extends Diff
0 ignored issues
show
Coding Style introduced by
The extends keyword must be on the same line as the class name
Loading history...
832
{
833
	/**
834
	 * Constructor.
835
	 *
836
	 * Computes diff between sequences of strings.
837
	 *
838
	 * This can be used to compute things like
839
	 * case-insensitve diffs, or diffs which ignore
840
	 * changes in white-space.
841
	 *
842
	 * @param $from_lines array An array of strings.
843
	 *  (Typically these are lines from a file.)
844
	 *
845
	 * @param $to_lines array An array of strings.
846
	 *
847
	 * @param $mapped_from_lines array This array should
848
	 *  have the same size number of elements as $from_lines.
849
	 *  The elements in $mapped_from_lines and
850
	 *  $mapped_to_lines are what is actually compared
851
	 *  when computing the diff.
852
	 *
853
	 * @param $mapped_to_lines array This array should
854
	 *  have the same number of elements as $to_lines.
855
	 */
856
	public function MappedDiff($from_lines, $to_lines,
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
857
						$mapped_from_lines, $mapped_to_lines) {
858
859
		assert(sizeof($from_lines) == sizeof($mapped_from_lines));
860
		assert(sizeof($to_lines) == sizeof($mapped_to_lines));
861
862
		$this->Diff($mapped_from_lines, $mapped_to_lines);
863
864
		$xi = $yi = 0;
865
		// Optimizing loop invariants:
866
		// http://phplens.com/lens/php-book/optimizing-debugging-php.php
867
		for ($i = 0, $max = sizeof($this->edits); $i < $max; $i++) {
868
			$orig = &$this->edits[$i]->orig;
869
			if (is_array($orig)) {
870
				$orig = array_slice($from_lines, $xi, sizeof($orig));
871
				$xi += sizeof($orig);
872
			}
873
874
			$final = &$this->edits[$i]->final;
875
			if (is_array($final)) {
876
				$final = array_slice($to_lines, $yi, sizeof($final));
877
				$yi += sizeof($final);
878
			}
879
		}
880
	}
881
}
882
883
884