Completed
Push — master ( 103bc8...f9ad25 )
by
unknown
02:42 queued 10s
created

CommonsLinkChecker   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 180
Duplicated Lines 3.33 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

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

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A getSupportedContextTypes() 0 7 1
A getDefaultContextTypes() 0 7 1
B getCommonsNamespace() 0 22 7
B checkConstraint() 6 58 7
A checkConstraintParameters() 0 10 2
A commonsLinkIsWellFormed() 0 6 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\Site\MediaWikiPageNameNormalizer;
7
use Wikibase\DataModel\Entity\ItemId;
8
use Wikibase\DataModel\Snak\PropertyValueSnak;
9
use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker;
10
use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context;
11
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException;
12
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser;
13
use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage;
14
use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
15
use WikibaseQuality\ConstraintReport\Constraint;
16
use WikibaseQuality\ConstraintReport\Role;
17
18
/**
19
 * @author BP2014N1
20
 * @license GPL-2.0-or-later
21
 */
22
class CommonsLinkChecker implements ConstraintChecker {
23
24
	/**
25
	 * @var ConstraintParameterParser
26
	 */
27
	private $constraintParameterParser;
28
29
	/**
30
	 * @var MediaWikiPageNameNormalizer
31
	 */
32
	private $pageNameNormalizer;
33
34
	public function __construct(
35
		ConstraintParameterParser $constraintParameterParser,
36
		MediaWikiPageNameNormalizer $pageNameNormalizer
37
	) {
38
		$this->constraintParameterParser = $constraintParameterParser;
39
		$this->pageNameNormalizer = $pageNameNormalizer;
40
	}
41
42
	/**
43
	 * @codeCoverageIgnore This method is purely declarative.
44
	 */
45
	public function getSupportedContextTypes() {
46
		return [
47
			Context::TYPE_STATEMENT => CheckResult::STATUS_COMPLIANCE,
48
			Context::TYPE_QUALIFIER => CheckResult::STATUS_COMPLIANCE,
49
			Context::TYPE_REFERENCE => CheckResult::STATUS_COMPLIANCE,
50
		];
51
	}
52
53
	/**
54
	 * @codeCoverageIgnore This method is purely declarative.
55
	 */
56
	public function getDefaultContextTypes() {
57
		return [
58
			Context::TYPE_STATEMENT,
59
			Context::TYPE_QUALIFIER,
60
			Context::TYPE_REFERENCE,
61
		];
62
	}
63
64
	/**
65
	 * Get the number of a namespace on Wikimedia Commons (commonswiki).
66
	 * All namespaces not known to this function will be looked up by the TitleParser.
67
	 *
68
	 * @param string $namespace
69
	 *
70
	 * @return array first element is the namespace number (default namespace for TitleParser),
71
	 * second element is a string to prepend to the title before giving it to the TitleParser
72
	 */
73
	private function getCommonsNamespace( $namespace ) {
74
		switch ( $namespace ) {
75
			case '':
76
				return [ NS_MAIN, '' ];
77
			// extra namespaces, see operations/mediawiki-config.git,
78
			// wmf-config/InitialiseSettings.php, 'wgExtraNamespaces' key, 'commonswiki' subkey
79
			case 'Creator':
80
				return [ 100, '' ];
81
			case 'TimedText':
82
				return [ 102, '' ];
83
			case 'Sequence':
84
				return [ 104, '' ];
85
			case 'Institution':
86
				return [ 106, '' ];
87
			// extension namespace, see mediawiki/extensions/JsonConfig.git,
88
			// extension.json, 'namespaces' key, third element
89
			case 'Data':
90
				return [ 486, '' ];
91
			default:
92
				return [ NS_MAIN, $namespace . ':' ];
93
		}
94
	}
95
96
	/**
97
	 * Checks 'Commons link' constraint.
98
	 *
99
	 * @param Context $context
100
	 * @param Constraint $constraint
101
	 *
102
	 * @throws ConstraintParameterException
103
	 * @return CheckResult
104
	 */
105
	public function checkConstraint( Context $context, Constraint $constraint ) {
106
		$parameters = [];
107
		$constraintParameters = $constraint->getConstraintParameters();
108
		$namespace = $this->constraintParameterParser->parseNamespaceParameter( $constraintParameters, $constraint->getConstraintTypeItemId() );
109
		$parameters['namespace'] = [ $namespace ];
110
111
		$snak = $context->getSnak();
112
113
		if ( !$snak instanceof PropertyValueSnak ) {
114
			// nothing to check
115
			return new CheckResult( $context, $constraint, $parameters, CheckResult::STATUS_COMPLIANCE );
116
		}
117
118
		$dataValue = $snak->getDataValue();
119
120
		/*
121
		 * error handling:
122
		 *   type of $dataValue for properties with 'Commons link' constraint has to be 'string'
123
		 *   parameter $namespace can be null, works for commons galleries
124
		 */
125 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...
126
			$message = ( new ViolationMessage( 'wbqc-violation-message-value-needed-of-type' ) )
127
				->withEntityId( new ItemId( $constraint->getConstraintTypeItemId() ), Role::CONSTRAINT_TYPE_ITEM )
128
				->withDataValueType( 'string' );
129
			return new CheckResult( $context, $constraint, $parameters, CheckResult::STATUS_VIOLATION, $message );
130
		}
131
132
		$commonsLink = $dataValue->getValue();
133
134
		try {
135
			if ( !$this->commonsLinkIsWellFormed( $commonsLink ) ) {
136
				throw new MalformedTitleException( 'wbqc-violation-message-commons-link-not-well-formed', $commonsLink ); // caught below
137
			}
138
139
			$prefix = $this->getCommonsNamespace( $namespace )[1];
140
			$normalizedTitle = $this->pageNameNormalizer->normalizePageName(
141
				$prefix . $commonsLink,
142
				'https://commons.wikimedia.org/w/api.php'
143
			);
144
145
			if ( $normalizedTitle === false ) {
146
				if ( $this->valueIncludesNamespace( $commonsLink, $namespace ) ) {
147
					throw new MalformedTitleException( 'wbqc-violation-message-commons-link-not-well-formed', $commonsLink ); // caught below
148
				} else {
149
					$message = new ViolationMessage( 'wbqc-violation-message-commons-link-no-existent' );
150
					$status = CheckResult::STATUS_VIOLATION;
151
				}
152
			} else {
153
				$message = null;
154
				$status = CheckResult::STATUS_COMPLIANCE;
155
			}
156
		} 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...
157
			$message = new ViolationMessage( 'wbqc-violation-message-commons-link-not-well-formed' );
158
			$status = CheckResult::STATUS_VIOLATION;
159
		}
160
161
		return new CheckResult( $context, $constraint, $parameters, $status, $message );
162
	}
163
164
	public function checkConstraintParameters( Constraint $constraint ) {
165
		$constraintParameters = $constraint->getConstraintParameters();
166
		$exceptions = [];
167
		try {
168
			$this->constraintParameterParser->parseNamespaceParameter( $constraintParameters, $constraint->getConstraintTypeItemId() );
169
		} catch ( ConstraintParameterException $e ) {
170
			$exceptions[] = $e;
171
		}
172
		return $exceptions;
173
	}
174
175
	/**
176
	 * @param string $commonsLink
177
	 *
178
	 * @return bool
179
	 */
180
	private function commonsLinkIsWellFormed( $commonsLink ) {
181
		$toReplace = [ "_", "%20" ];
182
		$compareString = trim( str_replace( $toReplace, '', $commonsLink ) );
183
184
		return $commonsLink === $compareString;
185
	}
186
187
	/**
188
	 * Checks whether the value of the statement already includes the namespace.
189
	 * This special case should be reported as “malformed title” instead of “title does not exist”.
190
	 *
191
	 * @param string $value
192
	 * @param string $namespace
193
	 *
194
	 * @return bool
195
	 */
196
	private function valueIncludesNamespace( $value, $namespace ) {
197
		return $namespace !== '' &&
198
			strncasecmp( $value, $namespace . ':', strlen( $namespace ) + 1 ) === 0;
199
	}
200
201
}
202