Completed
Pull Request — 1.x (#76)
by
unknown
04:16 queued 02:31
created

makeNumberOfPageViewsDataItem()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 0
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 7
cp 0
crap 12
1
<?php
2
3
namespace SESP\Annotator;
4
5
use SESP\PropertyRegistry;
6
use SESP\AppFactory;
7
8
use SMW\SemanticData;
9
use SMW\DIProperty;
10
use SMW\DIWikiPage;
11
use SMW\Store;
12
13
use SMWDataItem as DataItem;
14
use SMWDIBlob as DIBlob;
15
use SMWDIBoolean as DIBoolean;
16
use SMWDITime as DITime;
17
use SMWDINumber as DINumber;
18
19
use WikiPage;
20
use User;
21
use RuntimeException;
22
23
/**
24
 * @ingroup SESP
25
 *
26
 * @license GNU GPL v2+
27
 * @since 1.0
28
 *
29
 * @author mwjames
30
 * @author rotsee
31
 */
32
class ExtraPropertyAnnotator {
33
34
	/**
35
	 * @var SemanticData
36
	 */
37
	protected $semanticData = null;
38
39
	/**
40
	 * @var AppFactory
41
	 */
42
	private $appFactory = null;
43
44
45
	protected $configuration = null;
46
	private $dbConnection = null;
47
	private $page = null;
48
49
	/**
50
	 * @since 1.0
51
	 *
52
	 * @param SemanticData $semanticData
53
	 * @param Factory $factory
0 ignored issues
show
Documentation introduced by
There is no parameter named $factory. Did you maybe mean $appFactory?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
54
	 * @param array $configuration
55
	 */
56 9
	public function __construct( SemanticData $semanticData, AppFactory $appFactory, array $configuration ) {
57 9
		$this->semanticData = $semanticData;
58 9
		$this->appFactory = $appFactory;
59 9
		$this->configuration = $configuration;
60 9
	}
61
62
	/**
63
	 * @since 1.0
64
	 *
65
	 * @return boolean
66
	 * @throws RuntimeException
67
	 *
68
	 * @return boolean
69
	 */
70 8
	public function addAnnotation() {
71
72 8
		$subject = $this->semanticData->getSubject();
73
74 8
		if ( $subject === null || $subject->getTitle() === null || $subject->getTitle()->isSpecialPage() ) {
75 1
			return false;
76
		}
77
78 7
		if ( isset( $this->configuration['sespSpecialProperties'] ) &&
79 7
			is_array( $this->configuration['sespSpecialProperties'] ) ) {
80 6
			return $this->addPropertyValues();
81
		}
82
83 1
		throw new RuntimeException( "Expected a 'sespSpecialProperties' configuration array" );
84
	}
85
86
	/**
87
	 * @since 1.0
88
	 *
89
	 * @return SemanticData
90
	 */
91 6
	public function getSemanticData() {
92 6
		return $this->semanticData;
93
	}
94
95
	/**
96
	 * @since 1.0
97
	 *
98
	 * @return WikiPage
99
	 */
100 6
	public function getWikiPage() {
101
102 6
		if ( $this->page === null ) {
103 6
			$this->page = $this->appFactory->newWikiPage( $this->semanticData->getSubject()->getTitle() ); //$this->loadRegisteredObject( 'WikiPage' );
0 ignored issues
show
Bug introduced by
It seems like $this->semanticData->getSubject()->getTitle() can be null; however, newWikiPage() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
104 6
		}
105
106 6
		return $this->page;
107
	}
108
109 6
	protected function addPropertyValues() {
110
111 6
		$cachedProperties = array();
112
113 6
		foreach ( $this->configuration['sespSpecialProperties'] as $externalId ) {
114
115 6
			$propertyId = PropertyRegistry::getInstance()->getPropertyId( $externalId );
116
117 6
			if ( $this->hasRegisteredPropertyId( $propertyId, $cachedProperties ) ) {
118
				continue;
119
			}
120
121 6
			$propertyDI = new DIProperty( $propertyId );
122
123 6
			if ( $this->getSemanticData()->getPropertyValues( $propertyDI ) !== array() ) {
124
				$cachedProperties[ $propertyId ] = true;
125
				continue;
126
			}
127
128 6
			$dataItem = $this->createDataItemById( $externalId, $propertyDI );
129
130 6
			if ( $dataItem instanceof DataItem ) {
131 4
				$cachedProperties[ $propertyId ] = true;
132 4
				$this->getSemanticData()->addPropertyObjectValue( $propertyDI, $dataItem );
133 4
			}
134
135 6
		}
136
137 6
		return true;
138
	}
139
140 6
	protected function hasRegisteredPropertyId( $propertyId, $cachedProperties ) {
141 6
		return ( DIProperty::getPredefinedPropertyTypeId( $propertyId ) === '' ) ||
0 ignored issues
show
Deprecated Code introduced by
The method SMW\DIProperty::getPredefinedPropertyTypeId() has been deprecated with message: since 2.1, use PropertyRegistry::getPredefinedPropertyTypeId

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
142 6
			array_key_exists( $propertyId, $cachedProperties );
143
	}
144
145 6
	protected function createDataItemById( $externalId, $property ) {
146
147 6
		$dataItem = null;
148
149
		// _REVID was incorrect in the original SESP because getId returns the
150
		// page id not the revision Id
151
152
		switch ( $externalId ) {
153 6
			case '_CUSER' :
154 1
				$dataItem = $this->makeFirstAuthorDataItem();
155 1
				break;
156 5
			case '_VIEWS' :
157
				$dataItem = $this->makeNumberOfPageViewsDataItem();
158
				break;
159 5
			case '_USERREG' :
160 2
				$dataItem = $this->makeUserRegistrationDataItem();
161 2
				break;
162 3
			case '_USEREDITCNT' :
163
				$dataItem = $this->makeUserEditCountDataItem();
164
				break;
165 3
			case '_PAGEID' :
166
				$dataItem = $this->makePageIdDataItem();
167
				break;
168 3
			case '_PAGELGTH' :
169
				$dataItem = $this->makePageLengthDataItem();
0 ignored issues
show
Bug introduced by
The method makePageLengthDataItem() does not exist on SESP\Annotator\ExtraPropertyAnnotator. Did you maybe mean makePageLengthItem()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
170
				break;
171 3
			case '_REVID' :
172 2
				$dataItem = $this->makeRevisionIdDataItem();
173 2
				break;
174 1
			case '_NREV' :
175 1
				$dataItem = $this->makeNumberOfRevisionsDataItem();
176 1
				break;
177
			case '_NTREV' :
178
				$dataItem = $this->makeNumberOfTalkPageRevisionsDataItem();
179
				break;
180
			case '_EUSER' :
181
				$this->addPropertyValuesForPageContributors( $property );
182
				break;
183
			case '_SUBP' :
184
				$this->addPropertyValuesForSubPages( $property );
185
				break;
186
			case '_MEDIATYPE' :
187
			case '_MIMETYPE' :
188
				$this->addPropertyValuesForMIMEAndMediaType();
189
				break;
190
			case '_EXIFDATA' :
191
				$this->addPropertyValuesForExifData();
192
				break;
193
			case '_SHORTURL' :
194
				$this->addPropertyValuesForShortUrl();
195
				break;
196
		}
197
198 6
		return $dataItem;
199
	}
200
201 2
	private function isUserPage() {
202 2
		return $this->getWikiPage()->getTitle()->inNamespace( NS_USER );
203
	}
204
205
	private function isFilePage() {
206
		return $this->getWikiPage()->getTitle()->inNamespace( NS_FILE );
207
	}
208
209 1
	private function makeFirstAuthorDataItem() {
210 1
		$creator = $this->getWikiPage()->getCreator();
211
212 1
		if ( $creator ) {
213 1
			return DIWikiPage::newFromTitle( $creator->getUserPage() );
214
		}
215
	}
216
217
	private function makeNumberOfPageViewsDataItem() {
218
		if ( $this->configuration['wgDisableCounters'] ) {
219
			return null;
220
		}
221
222
		$count = $this->getPageViewCount();
223
224
		if ( !is_numeric( $count ) ) {
225
			return null;
226
		}
227
228
		return new DINumber( $count );
229
	}
230
231
	private function getPageViewCount() {
232
		if ( class_exists( '\HitCounters\HitCounters' ) ) {
233
			return \HitCounters\HitCounters::getCount( $this->getWikiPage()->getTitle() );
234
		}
235
236
		if ( method_exists( $this->getWikiPage(), 'getCount' ) ) {
237
			return $this->getWikiPage()->getCount();
238
		}
239
240
		return null;
241
	}
242
243
	private function addPropertyValuesForPageContributors( DIProperty $property ) {
244
245
		$user = User::newFromId( $this->getWikiPage()->getUser() );
246
		$authors = $this->getWikiPage()->getContributors();
247
248
		while ( $user ) {
249
			if ( !( in_array( 'bot', $user->getRights() ) &&
250
				$this->configuration['wgSESPExcludeBots'] ) &&
251
				!$user->isAnon() ) { //no anonymous users (hidden users are not returned)
252
253
				$this->getSemanticData()->addPropertyObjectValue(
254
					$property,
255
					DIWikiPage::newFromTitle( $user->getUserPage() )
256
				);
257
			}
258
259
			$user = $authors->current();
260
			$authors->next();
261
		}
262
	}
263
264
	private function makePageIdDataItem() {
265
		$pageID = $this->getWikiPage()->getId();
266
267
		if ( is_integer( $pageID ) && $pageID > 0 ) {
268
			return new DINumber( $pageID );
269
		}
270
	}
271
272
	private function makePageLengthItem() {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
273
		$pageLen = $this->getWikiPage()->getLen();
274
		
275
		if ( is_integer( $pageLen ) && $pageLen > 0 ) {
276
			return new DINumber( $pageLen );
277
		}
278
	}
279
	
280 2
	private function makeRevisionIdDataItem() {
281 2
		$revID = $this->getWikiPage()->getLatest();
282
283 2
		if ( is_integer( $revID ) && $revID > 0 ) {
284 1
			return new DINumber( $revID );
285
		}
286 1
	}
287
288 1
	private function getPageRevisionsForId( $pageId ) {
289
290 1
		if ( $this->dbConnection === null ) {
291 1
			$this->dbConnection = $this->appFactory->newDatabaseConnection(); //( 'DBConnection', 'DatabaseBase' );
292 1
		}
293
294 1
		return $this->dbConnection->estimateRowCount(
295 1
			"revision",
296 1
			"*",
297 1
			array( "rev_page" => $pageId )
298 1
		);
299
	}
300
301 1
	private function makeNumberOfRevisionsDataItem() {
302 1
		$numberOfPageRevisions = $this->getPageRevisionsForId(
303 1
			$this->getWikiPage()->getTitle()->getArticleID()
304 1
		);
305
306 1
		if ( $this->getWikiPage()->getTitle()->exists() && $numberOfPageRevisions > 0 ) {
307 1
			return new DINumber( $numberOfPageRevisions );
308
		}
309
	}
310
311
	private function makeNumberOfTalkPageRevisionsDataItem() {
312
		$numberOfTalkPageRevisions = $this->getPageRevisionsForId(
313
			$this->getWikiPage()->getTitle()->getTalkPage()->getArticleID()
314
		);
315
316
		if ( $this->getWikiPage()->getTitle()->getTalkPage()->exists() && $numberOfTalkPageRevisions > 0 ) {
317
			return new DINumber( $numberOfTalkPageRevisions );
318
		}
319
	}
320
321
	private function addPropertyValuesForMIMEAndMediaType(){
322
323
		if ( $this->isFilePage() ) {
324
325
			$file = $this->getWikiPage()->getFile();
326
			$mimetype = $file->getMimeType();
327
			$mediaType = \MimeMagic::singleton()->findMediaType( $mimetype );
328
			list( $mimetypemajor, $mimetypeminor ) = $file->splitMime( $mimetype );
0 ignored issues
show
Unused Code introduced by
The assignment to $mimetypemajor 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...
329
330
			$this->getSemanticData()->addPropertyObjectValue(
331
				new DIProperty( PropertyRegistry::getInstance()->getPropertyId( '_MIMETYPE' ) ),
332
				new DIBlob( $mimetypeminor )
333
			);
334
335
			$this->getSemanticData()->addPropertyObjectValue(
336
				new DIProperty( PropertyRegistry::getInstance()->getPropertyId( '_MEDIATYPE' ) ),
337
				new DIBlob( $mediaType )
338
			);
339
		}
340
341
	}
342
343
	private function addPropertyValuesForSubPages( DIProperty $property ) {
344
345
		//-1 = no limit. Returns TitleArray object
346
		$subpages = $this->getWikiPage()->getTitle()->getSubpages ( -1 );
347
348
		foreach ( $subpages as $title ) {
349
			$this->getSemanticData()->addPropertyObjectValue(
350
				$property,
351
				DIWikiPage::newFromTitle( $title )
352
			);
353
		}
354
	}
355
356
	private function addPropertyValuesForExifData() {
357
		if ( $this->isFilePage() ) {
358
			$this->appFactory->newExifDataAnnotator( $this->getSemanticData(), $this->getWikiPage()->getFile() )->addAnnotation();
359
		}
360
	}
361
362
	private function addPropertyValuesForShortUrl() {
363
364
		$shortUrlAnnotator = $this->appFactory->newShortUrlAnnotator( $this->getSemanticData() );
365
366
		if ( $shortUrlAnnotator->canUseShortUrl() ) {
367
			$shortUrlAnnotator->addAnnotation();
368
		}
369
	}
370
371 2
	private function makeUserRegistrationDataItem() {
372
373 2
		if ( !$this->isUserPage() ) {
374
			return null;
375
		}
376
377 2
		$user = $this->appFactory->newUserFromTitle( $this->getWikiPage()->getTitle() );
378
379 2
		if ( $user instanceof User ) {
0 ignored issues
show
Bug introduced by
The class User does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
380
381 1
			$timestamp = wfTimestamp( TS_ISO_8601, $user->getRegistration() );
382 1
			$date = new \DateTime( $timestamp );
383
384 1
			return new DITime(
385 1
				DITime::CM_GREGORIAN,
386 1
				$date->format('Y'),
387 1
				$date->format('m'),
0 ignored issues
show
Documentation introduced by
$date->format('m') is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
388 1
				$date->format('d'),
0 ignored issues
show
Documentation introduced by
$date->format('d') is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
389 1
				$date->format('H'),
0 ignored issues
show
Documentation introduced by
$date->format('H') is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
390 1
				$date->format('i')
0 ignored issues
show
Documentation introduced by
$date->format('i') is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
391 1
			);
392
		}
393 1
	}
394
395
	private function makeUserEditCountDataItem() {
396
397
		if ( !$this->isUserPage() ) {
398
			return;
399
		}
400
401
		$user = $this->appFactory->newUserFromTitle( $this->getWikiPage()->getTitle() );
402
403
		$count = $user instanceof User ? $user->getEditCount() : false;
0 ignored issues
show
Bug introduced by
The class User does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
404
405
		if ( $count ) {
406
			return new DINumber( $count );
407
		}
408
	}
409
410
}
411