SWLGroup   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 495
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 52
lcom 1
cbo 5
dl 0
loc 495
ccs 0
cts 140
cp 0
rs 7.44
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A newFromDBResult() 0 11 6
B __construct() 0 24 6
A writeToDB() 0 8 2
A updateInDB() 0 15 1
A insertIntoDB() 0 19 1
A getCategories() 0 3 1
A getNamespaces() 0 3 1
A getProperties() 0 3 1
A getPropertyObjects() 0 9 2
A getConcepts() 0 3 1
A getCustomTexts() 0 3 1
A getSerializedCustomTexts() 0 7 2
A unserializedCustomTexts() 0 7 2
A getId() 0 3 1
A getName() 0 3 1
A coversPage() 0 5 3
A namespacesCoversPage() 0 9 3
A categoriesCoverPage() 0 26 5
B conceptsCoverPage() 0 29 6
A getWatchingUsers() 0 25 3
A isWatchedByUser() 0 3 1
A notifyWatchingUsers() 0 7 2

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/**
4
 * Static class with functions interact with watchlist groups.
5
 *
6
 * @since 0.1
7
 *
8
 * @file SWL_Groups.php
9
 * @ingroup SemanticWatchlist
10
 *
11
 * @licence GNU GPL v3 or later
12
 * @author Jeroen De Dauw < [email protected] >
13
 */
14
class SWLGroup {
15
16
	/**
17
	 * The ID of the group; the group_id field in swl_groups.
18
	 * When creating a new group, this will be null, and
19
	 * automatically set after writing the group to the DB.
20
	 *
21
	 * @since 0.1
22
	 *
23
	 * @var integer or null
24
	 */
25
	private $id;
26
27
	/**
28
	 * Name of the group.
29
	 *
30
	 * @since 0.1
31
	 *
32
	 * @var string
33
	 */
34
	private $name;
35
36
	/**
37
	 * List of categories this group covers.
38
	 *
39
	 * @since 0.1
40
	 *
41
	 * @var array of string
42
	 */
43
	private $categories;
44
45
	/**
46
	 * List of namespaces IDs of namespaces this group covers.
47
	 *
48
	 * @since 0.1
49
	 *
50
	 * @var array of integer
51
	 */
52
	private $namespaces = array();
53
54
	/**
55
	 * List of SMW properties this group covers.
56
	 *
57
	 * @since 0.1
58
	 *
59
	 * @var array of string
60
	 */
61
	private $properties;
62
63
	/**
64
	 * List of custom texts this group covers.
65
	 *
66
	 * @var array
67
	 */
68
	private $customTexts;
69
70
	/**
71
	 * List of SMW concepts this group covers.
72
	 *
73
	 * @since 0.1
74
	 *
75
	 * @var array of string
76
	 */
77
	private $concepts;
78
79
	/**
80
	 * Cached list of IDs of users that are watching this group,
81
	 * or false if this data has not been obtained yet.
82
	 *
83
	 * @since 0.1
84
	 *
85
	 * @var array of integer or false
86
	 */
87
	private $watchingUsers = false;
88
89
	/**
90
	 * Creates a new instance of SWLGroup from a DB result.
91
	 *
92
	 * @since 0.1
93
	 *
94
	 * @param $group
95
	 *
96
	 * @return SWLGroup
97
	 */
98
	public static function newFromDBResult( $group ) {
99
		return new SWLGroup(
100
			$group->group_id,
101
			$group->group_name,
102
			$group->group_categories == '' ? array() : explode( '|', $group->group_categories ),
103
			$group->group_namespaces == '' ? array() : explode( '|', $group->group_namespaces ),
104
			$group->group_properties == '' ? array() : explode( '|', $group->group_properties ),
105
			$group->group_concepts == '' ? array() : explode( '|', $group->group_concepts ),
106
			$group->group_custom_texts == '' ? array() : self::unserializedCustomTexts( explode( '|', $group->group_custom_texts ) )
107
		);
108
	}
109
110
	/**
111
	 * Constructor.
112
	 *
113
	 * @since 0.1
114
	 *
115
	 * @param integer $id Set to null when creating a new group.
116
	 * @param string $name
117
	 * @param array $categories List of category names
118
	 * @param array $namespaces List of namespace names or IDs
119
	 * @param array $properties List of property names
120
	 * @param array $concepts List of concept names
121
	 * @param array $customTexts List of custom texts
122
	 */
123
	public function __construct( $id, $name, array $categories, array $namespaces, array $properties, array $concepts, array $customTexts ) {
124
		$this->id = $id;
125
		$this->name = $name;
126
		$this->categories = $categories;
127
		$this->properties = $properties;
128
		$this->concepts = $concepts;
129
		$this->customTexts = $customTexts;
130
131
		foreach ( $namespaces as $ns ) {
132
			if ( preg_match( "/^-?([0-9])+$/", $ns ) ) {
133
				$this->namespaces[] = $ns;
134
			}
135
			elseif ( $ns == '' || strtolower( $ns ) == 'main' ) {
136
				$this->namespaces[] = 0;
137
			}
138
			else {
139
				$ns = MWNamespace::getCanonicalIndex( strtolower( $ns ) );
140
141
				if ( !is_null( $ns ) ) {
142
					$this->namespaces[] = $ns;
143
				}
144
			}
145
		}
146
	}
147
148
	/**
149
	 * Writes the group to the database, either updating it
150
	 * when it already exists, or inserting it when it doesn't.
151
	 *
152
	 * @since 0.1
153
	 *
154
	 * @return boolean Success indicator
155
	 */
156
	public function writeToDB() {
157
		if ( is_null( $this->id ) ) {
158
			return $this->insertIntoDB();
159
		}
160
		else {
161
			return  $this->updateInDB();
162
		}
163
	}
164
165
	/**
166
	 * Updates the group in the database.
167
	 *
168
	 * @since 0.1
169
	 *
170
	 * @return boolean Success indicator
171
	 */
172
	private function updateInDB() {
173
		$dbr = wfGetDB( DB_MASTER );
174
		return  $dbr->update(
175
			'swl_groups',
176
			array(
177
				'group_name' => $this->name,
178
				'group_properties' => implode( '|', $this->properties ),
179
				'group_categories' => implode( '|', $this->categories ),
180
				'group_namespaces' => implode( '|', $this->namespaces ),
181
				'group_concepts' => implode( '|', $this->concepts ),
182
				'group_custom_texts' => implode( '|', $this->getSerializedCustomTexts() ),
183
			),
184
			array( 'group_id' => $this->id )
185
		);
186
	}
187
188
	/**
189
	 * Inserts the group into the database.
190
	 *
191
	 * @since 0.1
192
	 *
193
	 * @return boolean Success indicator
194
	 */
195
	private function insertIntoDB() {
196
		$dbr = wfGetDB( DB_MASTER );
197
198
		$result = $dbr->insert(
199
			'swl_groups',
200
			array(
201
				'group_name' => $this->name,
202
				'group_properties' => implode( '|', $this->properties ),
203
				'group_categories' => implode( '|', $this->categories ),
204
				'group_namespaces' => implode( '|', $this->namespaces ),
205
				'group_concepts' => implode( '|', $this->concepts ),
206
				'group_custom_texts' => implode( '|', $this->getSerializedCustomTexts() ),
207
			)
208
		);
209
210
		$this->id = $dbr->insertId();
211
212
		return $result;
213
	}
214
215
	/**
216
	 * Returns the categories specified by the group.
217
	 *
218
	 * @since 0.1
219
	 *
220
	 * @return array[string]
0 ignored issues
show
Documentation introduced by
The doc-type array[string] could not be parsed: Expected "]" at position 2, but found "string". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
221
	 */
222
	public function getCategories() {
223
		return $this->categories;
224
	}
225
226
	/**
227
	 * Returns the namespaces specified by the group.
228
	 *
229
	 * @since 0.1
230
	 *
231
	 * @return array[integer]
0 ignored issues
show
Documentation introduced by
The doc-type array[integer] could not be parsed: Expected "]" at position 2, but found "integer". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
232
	 */
233
	public function getNamespaces() {
234
		return $this->namespaces;
235
	}
236
237
	/**
238
	 * Returns the properties specified by the group as strings (serializations of SMWDIProperty).
239
	 *
240
	 * @since 0.1
241
	 *
242
	 * @return array[string]
0 ignored issues
show
Documentation introduced by
The doc-type array[string] could not be parsed: Expected "]" at position 2, but found "string". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
243
	 */
244
	public function getProperties() {
245
		return $this->properties;
246
	}
247
248
	/**
249
	 * Returns the properties specified by the group as SMWDIProperty objects.
250
	 *
251
	 * @since 0.1
252
	 *
253
	 * @return array[SMWDIProperty]
0 ignored issues
show
Documentation introduced by
The doc-type array[SMWDIProperty] could not be parsed: Expected "]" at position 2, but found "SMWDIProperty". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
254
	 */
255
	public function getPropertyObjects() {
256
		$properties = array();
257
258
		foreach ( $this->properties as $property ) {
259
			$properties[] = SMWDIProperty::newFromSerialization( $property );
0 ignored issues
show
Bug introduced by
The call to newFromSerialization() misses a required argument $serialization.

This check looks for function calls that miss required arguments.

Loading history...
260
		}
261
262
		return $properties;
263
	}
264
265
	/**
266
	 * Returns the concepts specified by the group.
267
	 *
268
	 * @since 0.1
269
	 *
270
	 * @return array[string]
0 ignored issues
show
Documentation introduced by
The doc-type array[string] could not be parsed: Expected "]" at position 2, but found "string". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
271
	 */
272
	public function getConcepts() {
273
		return $this->concepts;
274
	}
275
276
	/**
277
	 * Returns the custom Texts specified for this group.
278
	 *
279
	 * @since 0.2
280
	 *
281
	 * @return array
282
	 */
283
	public function getCustomTexts() {
284
		return $this->customTexts;
285
	}
286
287
	/**
288
	 * Returns the serialized version of custom Texts specified for this group.
289
	 *
290
	 * @since 0.2
291
	 *
292
	 * @return array
293
	 */
294
	public function getSerializedCustomTexts() {
295
		$serializedCustomTexts = array();
296
		foreach( $this->customTexts as $customText ) {
297
			$serializedCustomTexts[] = implode( '~', array_values( $customText ) );
298
		}
299
		return $serializedCustomTexts;
300
	}
301
302
	/**
303
	 * Returns the unserialized version of custom Texts specified for this group.
304
	 *
305
	 * @return array
306
	 */
307
	public static function unserializedCustomTexts( $customTexts ) {
308
		$unSerializedCustomTexts = array();
309
		foreach( $customTexts as $customText ) {
310
			$unSerializedCustomTexts[] = explode( '~', $customText );
311
		}
312
		return $unSerializedCustomTexts;
313
	}
314
315
	/**
316
	 * Returns the group database id.
317
	 *
318
	 * @since 0.1
319
	 *
320
	 * @return integer
321
	 */
322
	public function getId() {
323
		return $this->id;
324
	}
325
326
	/**
327
	 * Returns the group name.
328
	 *
329
	 * @since 0.1
330
	 *
331
	 * @return string
332
	 */
333
	public function getName() {
334
		return $this->name;
335
	}
336
337
	/**
338
	 * Returns whether the group contains the specified page.
339
	 *
340
	 * @since 0.1
341
	 *
342
	 * @param Title $title
343
	 *
344
	 * @return boolean
345
	 */
346
	public function coversPage( Title $title ) {
347
		return $this->categoriesCoverPage( $title )
348
			|| $this->namespacesCoversPage( $title )
349
			|| $this->conceptsCoverPage( $title );
350
	}
351
352
	/**
353
	 * Returns whether the namespaces of the group cover the specified page.
354
	 *
355
	 * @since 0.1
356
	 *
357
	 * @param Title $title
358
	 *
359
	 * @return boolean
360
	 */
361
	public function namespacesCoversPage( Title $title ) {
362
		if ( count( $this->namespaces ) > 0 ) {
363
			if ( !in_array( $title->getNamespace(), $this->namespaces ) ) {
364
				return false;
365
			}
366
		}
367
368
		return true;
369
	}
370
371
	/**
372
	 * Returns whether the catgeories of the group cover the specified page.
373
	 *
374
	 * @since 0.1
375
	 *
376
	 * @param Title $title
377
	 *
378
	 * @return boolean
379
	 */
380
	public function categoriesCoverPage( Title $title ) {
381
		if ( count( $this->categories ) == 0 ) {
382
			return true;
383
		}
384
385
		$foundMatch = false;
386
387
		$cats = array_keys( $title->getParentCategories() );
388
389
		if ( count( $cats ) == 0 ) {
390
			return false;
391
		}
392
393
		global $wgContLang;
394
		$catPrefix = $wgContLang->getNSText( NS_CATEGORY ) . ':';
395
396
		foreach ( $this->categories as $groupCategory ) {
397
			$foundMatch = in_array( $catPrefix . $groupCategory, $cats );
398
399
			if ( $foundMatch ) {
400
				break;
401
			}
402
		}
403
404
		return $foundMatch;
405
	}
406
407
	/**
408
	 * Returns whether the concepts of the group cover the specified page.
409
	 *
410
	 * @since 0.1
411
	 *
412
	 * @param Title $title
413
	 *
414
	 * @return boolean
415
	 */
416
	public function conceptsCoverPage( Title $title ) {
417
		if ( count( $this->concepts ) == 0 ) {
418
			return true;
419
		}
420
421
		$foundMatch = false;
422
423
		foreach ( $this->concepts as $groupConcept ) {
424
			$queryDescription = new SMWConjunction();
425
426
			$conceptTitle = Title::newFromText( $groupConcept, SMW_NS_CONCEPT );
427
			if ( !$conceptTitle->exists() ) continue;
428
429
			$queryDescription->addDescription( new SMWConceptDescription( SMWDIWikiPage::newFromTitle( $conceptTitle ) ) );
430
			$queryDescription->addDescription( new SMWValueDescription( SMWDIWikiPage::newFromTitle( $title ) ) );
431
432
			$query = new SMWQuery( $queryDescription );
433
			$query->querymode = SMWQuery::MODE_COUNT;
434
435
			/* SMWQueryResult */ $result = smwfGetStore()->getQueryResult( $query );
436
			$foundMatch = $result instanceof SMWQueryResult ? $result->getCount() > 0 : $result > 0;
0 ignored issues
show
Bug introduced by
The class SMWQueryResult does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
437
438
			if ( $foundMatch ) {
439
				break;
440
			}
441
		}
442
443
		return $foundMatch;
444
	}
445
446
	/**
447
	 * Returns the IDs of the users watching the group.
448
	 *
449
	 * @since 0.1
450
	 *
451
	 * @return array of integer
452
	 */
453
	public function getWatchingUsers() {
454
		if ( $this->watchingUsers == false ) {
455
			$dbr = wfGetDB( DB_REPLICA );
456
457
			$users = $dbr->select(
458
				'swl_users_per_group',
459
				array(
460
					'upg_user_id'
461
				),
462
				array(
463
					'upg_group_id' => $this->getId()
464
				)
465
			);
466
467
			$userIds = array();
468
469
			foreach ( $users as $user ) {
470
				$userIds[] = $user->upg_user_id;
471
			}
472
473
			$this->watchingUsers = $userIds;
474
		}
475
476
		return $this->watchingUsers;
477
	}
478
479
	/**
480
	 * Returns if the group is watched by the specified user or not.
481
	 *
482
	 * @since 0.1
483
	 *
484
	 * @param User $user
485
	 *
486
	 * @return boolean
487
	 */
488
	public function isWatchedByUser( User $user ) {
489
		return in_array( $user->getId(), $this->getWatchingUsers() );
490
	}
491
492
	/**
493
	 * Gets all the watching users and passes them, together with the specified
494
	 * changes and the group object itself, to the SWLGroupNotify hook.
495
	 *
496
	 * @since 0.1
497
	 *
498
	 * @param SMWChangeSet $changes
499
	 */
500
	public function notifyWatchingUsers( SWLChangeSet $changes ) {
501
		$users = $this->getWatchingUsers();
502
503
		if ( $changes->hasChanges( true ) ) {
0 ignored issues
show
Unused Code introduced by
The call to SWLChangeSet::hasChanges() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
504
			wfRunHooks( 'SWLGroupNotify', array( $this, $users, $changes ) );
505
		}
506
	}
507
508
}
509
510