RevisionDeleter   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 223
Duplicated Lines 9.42 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 21
loc 223
rs 10
c 0
b 0
f 0
wmc 26
lcom 1
cbo 2

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getTypes() 0 3 1
A getCanonicalTypeName() 0 6 3
A createList() 0 8 2
A checkItem() 0 5 3
A getChanges() 0 20 3
A getRelationType() 7 7 2
A getRestriction() 7 7 2
A getRevdelConstant() 7 7 2
A suggestTarget() 0 7 2
A checkRevisionExistence() 0 16 2
A extractBitfield() 0 12 4

How to fix   Duplicated Code   

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:

1
<?php
2
/**
3
 * Revision/log/file deletion backend
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup RevisionDelete
22
 */
23
24
/**
25
 * General controller for RevDel, used by both SpecialRevisiondelete and
26
 * ApiRevisionDelete.
27
 * @ingroup RevisionDelete
28
 */
29
class RevisionDeleter {
30
	/** List of known revdel types, with their corresponding list classes */
31
	private static $allowedTypes = [
32
		'revision' => 'RevDelRevisionList',
33
		'archive' => 'RevDelArchiveList',
34
		'oldimage' => 'RevDelFileList',
35
		'filearchive' => 'RevDelArchivedFileList',
36
		'logging' => 'RevDelLogList',
37
	];
38
39
	/** Type map to support old log entries */
40
	private static $deprecatedTypeMap = [
41
		'oldid' => 'revision',
42
		'artimestamp' => 'archive',
43
		'oldimage' => 'oldimage',
44
		'fileid' => 'filearchive',
45
		'logid' => 'logging',
46
	];
47
48
	/**
49
	 * Lists the valid possible types for revision deletion.
50
	 *
51
	 * @since 1.22
52
	 * @return array
53
	 */
54
	public static function getTypes() {
55
		return array_keys( self::$allowedTypes );
56
	}
57
58
	/**
59
	 * Gets the canonical type name, if any.
60
	 *
61
	 * @since 1.22
62
	 * @param string $typeName
63
	 * @return string|null
64
	 */
65
	public static function getCanonicalTypeName( $typeName ) {
66
		if ( isset( self::$deprecatedTypeMap[$typeName] ) ) {
67
			$typeName = self::$deprecatedTypeMap[$typeName];
68
		}
69
		return isset( self::$allowedTypes[$typeName] ) ? $typeName : null;
70
	}
71
72
	/**
73
	 * Instantiate the appropriate list class for a given list of IDs.
74
	 *
75
	 * @since 1.22
76
	 * @param string $typeName RevDel type, see RevisionDeleter::getTypes()
77
	 * @param IContextSource $context
78
	 * @param Title $title
79
	 * @param array $ids
80
	 * @return RevDelList
81
	 * @throws MWException
82
	 */
83
	public static function createList( $typeName, IContextSource $context, Title $title, array $ids ) {
84
		$typeName = self::getCanonicalTypeName( $typeName );
85
		if ( !$typeName ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $typeName of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
86
			throw new MWException( __METHOD__ . ": Unknown RevDel type '$typeName'" );
87
		}
88
		$class = self::$allowedTypes[$typeName];
89
		return new $class( $context, $title, $ids );
90
	}
91
92
	/**
93
	 * Checks for a change in the bitfield for a certain option and updates the
94
	 * provided array accordingly.
95
	 *
96
	 * @param string $desc Description to add to the array if the option was
97
	 * enabled / disabled.
98
	 * @param int $field The bitmask describing the single option.
99
	 * @param int $diff The xor of the old and new bitfields.
100
	 * @param int $new The new bitfield
101
	 * @param array $arr The array to update.
102
	 */
103
	protected static function checkItem( $desc, $field, $diff, $new, &$arr ) {
104
		if ( $diff & $field ) {
105
			$arr[( $new & $field ) ? 0 : 1][] = $desc;
106
		}
107
	}
108
109
	/**
110
	 * Gets an array of message keys describing the changes made to the
111
	 * visibility of the revision.
112
	 *
113
	 * If the resulting array is $arr, then $arr[0] will contain an array of
114
	 * keys describing the items that were hidden, $arr[1] will contain
115
	 * an array of keys describing the items that were unhidden, and $arr[2]
116
	 * will contain an array with a single message key, which can be one of
117
	 * "revdelete-restricted", "revdelete-unrestricted" indicating (un)suppression
118
	 * or null to indicate nothing in particular.
119
	 * You can turn the keys in $arr[0] and $arr[1] into message keys by
120
	 * appending -hid and -unhid to the keys respectively.
121
	 *
122
	 * @param int $n The new bitfield.
123
	 * @param int $o The old bitfield.
124
	 * @return array An array as described above.
125
	 * @since 1.19 public
126
	 */
127
	public static function getChanges( $n, $o ) {
128
		$diff = $n ^ $o;
129
		$ret = [ 0 => [], 1 => [], 2 => [] ];
130
		// Build bitfield changes in language
131
		self::checkItem( 'revdelete-content',
132
			Revision::DELETED_TEXT, $diff, $n, $ret );
133
		self::checkItem( 'revdelete-summary',
134
			Revision::DELETED_COMMENT, $diff, $n, $ret );
135
		self::checkItem( 'revdelete-uname',
136
			Revision::DELETED_USER, $diff, $n, $ret );
137
		// Restriction application to sysops
138
		if ( $diff & Revision::DELETED_RESTRICTED ) {
139
			if ( $n & Revision::DELETED_RESTRICTED ) {
140
				$ret[2][] = 'revdelete-restricted';
141
			} else {
142
				$ret[2][] = 'revdelete-unrestricted';
143
			}
144
		}
145
		return $ret;
146
	}
147
148
	/** Get DB field name for URL param...
149
	 * Future code for other things may also track
150
	 * other types of revision-specific changes.
151
	 * @param string $typeName
152
	 * @return string One of log_id/rev_id/fa_id/ar_timestamp/oi_archive_name
153
	 */
154 View Code Duplication
	public static function getRelationType( $typeName ) {
155
		$typeName = self::getCanonicalTypeName( $typeName );
156
		if ( !$typeName ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $typeName of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
157
			return null;
158
		}
159
		return call_user_func( [ self::$allowedTypes[$typeName], 'getRelationType' ] );
160
	}
161
162
	/**
163
	 * Get the user right required for the RevDel type
164
	 * @since 1.22
165
	 * @param string $typeName
166
	 * @return string User right
167
	 */
168 View Code Duplication
	public static function getRestriction( $typeName ) {
169
		$typeName = self::getCanonicalTypeName( $typeName );
170
		if ( !$typeName ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $typeName of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
171
			return null;
172
		}
173
		return call_user_func( [ self::$allowedTypes[$typeName], 'getRestriction' ] );
174
	}
175
176
	/**
177
	 * Get the revision deletion constant for the RevDel type
178
	 * @since 1.22
179
	 * @param string $typeName
180
	 * @return int RevDel constant
181
	 */
182 View Code Duplication
	public static function getRevdelConstant( $typeName ) {
183
		$typeName = self::getCanonicalTypeName( $typeName );
184
		if ( !$typeName ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $typeName of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
185
			return null;
186
		}
187
		return call_user_func( [ self::$allowedTypes[$typeName], 'getRevdelConstant' ] );
188
	}
189
190
	/**
191
	 * Suggest a target for the revision deletion
192
	 * @since 1.22
193
	 * @param string $typeName
194
	 * @param Title|null $target User-supplied target
195
	 * @param array $ids
196
	 * @return Title|null
197
	 */
198
	public static function suggestTarget( $typeName, $target, array $ids ) {
199
		$typeName = self::getCanonicalTypeName( $typeName );
200
		if ( !$typeName ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $typeName of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
201
			return $target;
202
		}
203
		return call_user_func( [ self::$allowedTypes[$typeName], 'suggestTarget' ], $target, $ids );
204
	}
205
206
	/**
207
	 * Checks if a revision still exists in the revision table.
208
	 * If it doesn't, returns the corresponding ar_timestamp field
209
	 * so that this key can be used instead.
210
	 *
211
	 * @param Title $title
212
	 * @param int $revid
213
	 * @return bool|mixed
214
	 */
215
	public static function checkRevisionExistence( $title, $revid ) {
216
		$dbr = wfGetDB( DB_REPLICA );
217
		$exists = $dbr->selectField( 'revision', '1',
218
				[ 'rev_id' => $revid ], __METHOD__ );
219
220
		if ( $exists ) {
221
			return true;
222
		}
223
224
		$timestamp = $dbr->selectField( 'archive', 'ar_timestamp',
225
				[ 'ar_namespace' => $title->getNamespace(),
226
					'ar_title' => $title->getDBkey(),
227
					'ar_rev_id' => $revid ], __METHOD__ );
228
229
		return $timestamp;
230
	}
231
232
	/**
233
	 * Put together a rev_deleted bitfield
234
	 * @since 1.22
235
	 * @param array $bitPars ExtractBitParams() params
236
	 * @param int $oldfield Current bitfield
237
	 * @return integer
238
	 */
239
	public static function extractBitfield( array $bitPars, $oldfield ) {
240
		// Build the actual new rev_deleted bitfield
241
		$newBits = 0;
242
		foreach ( $bitPars as $const => $val ) {
243
			if ( $val == 1 ) {
244
				$newBits |= $const; // $const is the *_deleted const
245
			} elseif ( $val == -1 ) {
246
				$newBits |= ( $oldfield & $const ); // use existing
247
			}
248
		}
249
		return $newBits;
250
	}
251
}
252