Completed
Push — master ( f2f357...ccb861 )
by
unknown
02:21
created

CommonsLinkChecker   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 193
Duplicated Lines 3.11 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 8
dl 6
loc 193
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A getSupportedContextTypes() 0 7 1
A getDefaultContextTypes() 0 7 1
A getCommonsNamespace() 0 18 6
B checkConstraint() 6 53 7
A checkConstraintParameters() 0 10 2
A pageExists() 0 17 2
A commonsLinkIsWellFormed() 0 5 1
A valueIncludesNamespace() 0 4 2

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
namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker;
4
5
use MalformedTitleException;
6
use MediaWiki\MediaWikiServices;
7
use TitleParser;
8
use TitleValue;
9
use Wikibase\DataModel\Entity\ItemId;
10
use Wikibase\DataModel\Snak\PropertyValueSnak;
11
use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker;
12
use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context;
13
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException;
14
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser;
15
use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage;
16
use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
17
use WikibaseQuality\ConstraintReport\Constraint;
18
use WikibaseQuality\ConstraintReport\Role;
19
20
/**
21
 * @author BP2014N1
22
 * @license GPL-2.0-or-later
23
 */
24
class CommonsLinkChecker implements ConstraintChecker {
25
26
	/**
27
	 * @var ConstraintParameterParser
28
	 */
29
	private $constraintParameterParser;
30
31
	/**
32
	 * @var TitleParser
33
	 */
34
	private $titleParser;
35
36
	public function __construct(
37
		ConstraintParameterParser $constraintParameterParser,
38
		TitleParser $titleParser
39
	) {
40
		$this->constraintParameterParser = $constraintParameterParser;
41
		$this->titleParser = $titleParser;
42
	}
43
44
	/**
45
	 * @codeCoverageIgnore This method is purely declarative.
46
	 */
47
	public function getSupportedContextTypes() {
48
		return [
49
			Context::TYPE_STATEMENT => CheckResult::STATUS_COMPLIANCE,
50
			Context::TYPE_QUALIFIER => CheckResult::STATUS_COMPLIANCE,
51
			Context::TYPE_REFERENCE => CheckResult::STATUS_COMPLIANCE,
52
		];
53
	}
54
55
	/**
56
	 * @codeCoverageIgnore This method is purely declarative.
57
	 */
58
	public function getDefaultContextTypes() {
59
		return [
60
			Context::TYPE_STATEMENT,
61
			Context::TYPE_QUALIFIER,
62
			Context::TYPE_REFERENCE,
63
		];
64
	}
65
66
	/**
67
	 * Get the number of a namespace on Wikimedia Commons (commonswiki).
68
	 * All namespaces not known to this function will be looked up by the TitleParser.
69
	 *
70
	 * @param string $namespace
71
	 *
72
	 * @return array first element is the namespace number (default namespace for TitleParser),
73
	 * second element is a string to prepend to the title before giving it to the TitleParser
74
	 */
75
	private function getCommonsNamespace( $namespace ) {
76
		// for namespace numbers see mediawiki-config repo, wmf-config/InitialiseSettings.php,
77
		// 'wgExtraNamespaces' key, 'commonswiki' subkey
78
		switch ( $namespace ) {
79
			case '':
80
				return [ NS_MAIN, '' ];
81
			case 'Creator':
82
				return [ 100, '' ];
83
			case 'TimedText':
84
				return [ 102, '' ];
85
			case 'Sequence':
86
				return [ 104, '' ];
87
			case 'Institution':
88
				return [ 106, '' ];
89
			default:
90
				return [ NS_MAIN, $namespace . ':' ];
91
		}
92
	}
93
94
	/**
95
	 * Checks 'Commons link' constraint.
96
	 *
97
	 * @param Context $context
98
	 * @param Constraint $constraint
99
	 *
100
	 * @throws ConstraintParameterException
101
	 * @return CheckResult
102
	 */
103
	public function checkConstraint( Context $context, Constraint $constraint ) {
104
		$parameters = [];
105
		$constraintParameters = $constraint->getConstraintParameters();
106
		$namespace = $this->constraintParameterParser->parseNamespaceParameter( $constraintParameters, $constraint->getConstraintTypeItemId() );
107
		$parameters['namespace'] = [ $namespace ];
108
109
		$snak = $context->getSnak();
110
111
		if ( !$snak instanceof PropertyValueSnak ) {
112
			// nothing to check
113
			return new CheckResult( $context, $constraint, $parameters, CheckResult::STATUS_COMPLIANCE );
114
		}
115
116
		$dataValue = $snak->getDataValue();
117
118
		/*
119
		 * error handling:
120
		 *   type of $dataValue for properties with 'Commons link' constraint has to be 'string'
121
		 *   parameter $namespace can be null, works for commons galleries
122
		 */
123 View Code Duplication
		if ( $dataValue->getType() !== 'string' ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
			$message = ( new ViolationMessage( 'wbqc-violation-message-value-needed-of-type' ) )
125
				->withEntityId( new ItemId( $constraint->getConstraintTypeItemId() ), Role::CONSTRAINT_TYPE_ITEM )
126
				->withDataValueType( 'string' );
127
			return new CheckResult( $context, $constraint, $parameters, CheckResult::STATUS_VIOLATION, $message );
128
		}
129
130
		$commonsLink = $dataValue->getValue();
131
132
		try {
133
			if ( !$this->commonsLinkIsWellFormed( $commonsLink ) ) {
134
				throw new MalformedTitleException( 'wbqc-violation-message-commons-link-not-well-formed', $commonsLink ); // caught below
135
			}
136
			list( $defaultNamespace, $prefix ) = $this->getCommonsNamespace( $namespace );
137
			$title = $this->titleParser->parseTitle( $prefix . $commonsLink, $defaultNamespace );
138
			if ( $this->pageExists( $title ) ) {
139
				$message = null;
140
				$status = CheckResult::STATUS_COMPLIANCE;
141
			} else {
142
				if ( $this->valueIncludesNamespace( $commonsLink, $namespace ) ) {
143
					throw new MalformedTitleException( 'wbqc-violation-message-commons-link-not-well-formed', $commonsLink ); // caught below
144
				} else {
145
					$message = new ViolationMessage( 'wbqc-violation-message-commons-link-no-existent' );
146
					$status = CheckResult::STATUS_VIOLATION;
147
				}
148
			}
149
		} catch ( MalformedTitleException $e ) {
0 ignored issues
show
Bug introduced by
The class MalformedTitleException 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...
150
			$message = new ViolationMessage( 'wbqc-violation-message-commons-link-not-well-formed' );
151
			$status = CheckResult::STATUS_VIOLATION;
152
		}
153
154
		return new CheckResult( $context, $constraint, $parameters, $status, $message );
155
	}
156
157
	public function checkConstraintParameters( Constraint $constraint ) {
158
		$constraintParameters = $constraint->getConstraintParameters();
159
		$exceptions = [];
160
		try {
161
			$this->constraintParameterParser->parseNamespaceParameter( $constraintParameters, $constraint->getConstraintTypeItemId() );
162
		} catch ( ConstraintParameterException $e ) {
163
			$exceptions[] = $e;
164
		}
165
		return $exceptions;
166
	}
167
168
	/**
169
	 * @param TitleValue $title
170
	 *
171
	 * @return bool
172
	 */
173
	private function pageExists( TitleValue $title ) {
174
		$commonsWikiId = 'commonswiki';
175
		if ( defined( 'MW_PHPUNIT_TEST' ) ) {
176
			$commonsWikiId = false;
177
		}
178
179
		$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
180
		$dbConnection = $lbFactory->getMainLB( $commonsWikiId )->getConnection(
181
			DB_REPLICA, false, $commonsWikiId
182
		);
183
		$row = $dbConnection->selectRow( 'page', '*', [
184
			'page_title' => $title->getDBkey(),
185
			'page_namespace' => $title->getNamespace()
186
		] );
187
188
		return $row !== false;
189
	}
190
191
	/**
192
	 * @param string $commonsLink
193
	 *
194
	 * @return bool
195
	 */
196
	private function commonsLinkIsWellFormed( $commonsLink ) {
197
		$toReplace = [ "_", "%20" ];
198
		$compareString = trim( str_replace( $toReplace, '', $commonsLink ) );
199
		return $commonsLink === $compareString;
200
	}
201
202
	/**
203
	 * Checks whether the value of the statement already includes the namespace.
204
	 * This special case should be reported as “malformed title” instead of “title does not exist”.
205
	 *
206
	 * @param string $value
207
	 * @param string $namespace
208
	 *
209
	 * @return bool
210
	 */
211
	private function valueIncludesNamespace( $value, $namespace ) {
212
		return $namespace !== '' &&
213
			strncasecmp( $value, $namespace . ':', strlen( $namespace ) + 1 ) === 0;
214
	}
215
216
}
217