SpecialUnconnectedPages   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 214
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 2
dl 0
loc 214
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A isSyndicated() 0 3 1
A isCacheable() 0 3 1
A setNamespaceChecker() 0 3 1
A getNamespaceChecker() 0 7 2
A buildConditionals() 0 20 5
A getQueryInfo() 0 32 1
A reallyDoQuery() 0 7 3
A formatResult() 0 12 2
A getPageHeader() 0 28 3
A getGroupName() 0 3 1
A linkParameters() 0 5 1
1
<?php
2
3
namespace Wikibase\Client\Specials;
4
5
use Html;
6
use MWNamespace;
7
use QueryPage;
8
use Skin;
9
use Title;
10
use Wikibase\Client\NamespaceChecker;
11
use Wikibase\Client\WikibaseClient;
12
use Wikimedia\Rdbms\FakeResultWrapper;
13
use Wikimedia\Rdbms\IDatabase;
14
use Wikimedia\Rdbms\IResultWrapper;
15
16
/**
17
 * List client pages that are not connected to repository items.
18
 *
19
 * @license GPL-2.0-or-later
20
 * @author John Erling Blad < [email protected] >
21
 * @author Amir Sarabadani < [email protected] >
22
 * @author Daniel Kinzler
23
 */
24
class SpecialUnconnectedPages extends QueryPage {
25
26
	/**
27
	 * @var int maximum supported offset
28
	 */
29
	const MAX_OFFSET = 10000;
30
31
	/**
32
	 * @var NamespaceChecker|null
33
	 */
34
	private $namespaceChecker = null;
35
36
	/**
37
	 * @see SpecialPage::__construct
38
	 *
39
	 * @param string $name
40
	 */
41
	public function __construct( $name = 'UnconnectedPages' ) {
42
		parent::__construct( $name );
43
	}
44
45
	/**
46
	 * @see QueryPage::isSyndicated
47
	 *
48
	 * @return bool Always false because we do not want to build RSS/Atom feeds for this page.
49
	 */
50
	public function isSyndicated() {
51
		return false;
52
	}
53
54
	/**
55
	 * @see QueryPage::isCacheable
56
	 *
57
	 * @return bool Always false because we can not have caching since we will store additional information.
58
	 */
59
	public function isCacheable() {
60
		return false;
61
	}
62
63
	public function setNamespaceChecker( NamespaceChecker $namespaceChecker ) {
64
		$this->namespaceChecker = $namespaceChecker;
65
	}
66
67
	/**
68
	 * @return NamespaceChecker
69
	 */
70
	public function getNamespaceChecker() {
71
		if ( $this->namespaceChecker === null ) {
72
			$this->namespaceChecker = WikibaseClient::getDefaultInstance()->getNamespaceChecker();
73
		}
74
75
		return $this->namespaceChecker;
76
	}
77
78
	/**
79
	 * Build conditionals for namespace
80
	 *
81
	 * @param IDatabase $dbr
82
	 * @param Title|null $title
83
	 * @param NamespaceChecker|null $checker
84
	 *
85
	 * @return string[]
86
	 */
87
	public function buildConditionals( IDatabase $dbr, Title $title = null, NamespaceChecker $checker = null ) {
88
		$conds = [];
89
90
		if ( $checker === null ) {
91
			$checker = $this->getNamespaceChecker();
92
		}
93
		if ( $title !== null ) {
94
			$conds[] = 'page_title >= ' . $dbr->addQuotes( $title->getDBkey() );
95
			$conds[] = 'page_namespace = ' . (int)$title->getNamespace();
96
		}
97
		$wbNamespaces = $checker->getWikibaseNamespaces();
98
		$ns = $this->getRequest()->getIntOrNull( 'namespace' );
99
		if ( $ns !== null && in_array( $ns, $wbNamespaces ) ) {
100
			$conds[] = 'page_namespace = ' . $ns;
101
		} else {
102
			$conds[] = 'page_namespace IN (' . implode( ',', $wbNamespaces ) . ')';
103
		}
104
105
		return $conds;
106
	}
107
108
	/**
109
	 * @see QueryPage::getQueryInfo
110
	 *
111
	 * @return array[]
112
	 */
113
	public function getQueryInfo() {
114
		$dbr = wfGetDB( DB_REPLICA );
115
116
		$conds = $this->buildConditionals( $dbr );
117
		$conds['page_is_redirect'] = 0;
118
		$conds[] = 'pp_propname IS NULL';
119
120
		return [
121
			'tables' => [
122
				'page',
123
				'page_props',
124
			],
125
			'fields' => [
126
				'value' => 'page_id',
127
				'namespace' => 'page_namespace',
128
				'title' => 'page_title',
129
				// Placeholder, we'll get this from page_props in the future.
130
				'page_num_iwlinks' => '0',
131
			],
132
			'conds' => $conds,
133
			// Sorting is determined getOrderFields(), which returns [ 'value' ] per default.
134
			'options' => [],
135
			'join_conds' => [
136
				// TODO Also get explicit_langlink_count from page_props once that is populated.
137
				// Could even filter or sort by it via pp_sortkey.
138
				'page_props' => [
139
					'LEFT JOIN',
140
					[ 'page_id = pp_page', "pp_propname = 'wikibase_item'" ]
141
				],
142
			]
143
		];
144
	}
145
146
	/**
147
	 * @see QueryPage::reallyDoQuery
148
	 *
149
	 * @param int|bool $limit
150
	 * @param int|bool $offset
151
	 *
152
	 * @return IResultWrapper
153
	 */
154
	public function reallyDoQuery( $limit, $offset = false ) {
155
		if ( is_int( $offset ) && $offset > self::MAX_OFFSET ) {
156
			return new FakeResultWrapper( [] );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \Wikimedia\Rd...ResultWrapper(array()); (Wikimedia\Rdbms\FakeResultWrapper) is incompatible with the return type documented by Wikibase\Client\Specials...tedPages::reallyDoQuery of type Wikimedia\Rdbms\IResultWrapper.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
157
		}
158
159
		return parent::reallyDoQuery( $limit, $offset );
160
	}
161
162
	/**
163
	 * @see QueryPage::formatResult
164
	 *
165
	 * @param Skin $skin
166
	 * @param object $result
167
	 *
168
	 * @return string
169
	 */
170
	public function formatResult( $skin, $result ) {
171
		// FIXME: This should use a TitleFactory.
172
		$title = Title::newFromID( $result->value );
173
		$out = $this->getLinkRenderer()->makeKnownLink( $title );
174
175
		if ( $result->page_num_iwlinks > 0 ) {
176
			$out .= ' ' . $this->msg( 'wikibase-unconnectedpages-format-row' )
177
				->numParams( $result->page_num_iwlinks )->text();
178
		}
179
180
		return $out;
181
	}
182
183
	/**
184
	 * @see QueryPage::getPageHeader
185
	 *
186
	 * @return string
187
	 */
188
	public function getPageHeader() {
189
		$excludeNamespaces = array_diff(
190
			MWNamespace::getValidNamespaces(),
191
			$this->getNamespaceChecker()->getWikibaseNamespaces()
192
		);
193
194
		$limit = $this->getRequest()->getIntOrNull( 'limit' );
195
		$ns = $this->getRequest()->getIntOrNull( 'namespace' );
196
197
		return Html::openElement(
198
			'form',
199
			[
200
				'action' => $this->getPageTitle()->getLocalURL()
201
			]
202
		) .
203
		( $limit === null ? '' : Html::hidden( 'limit', $limit ) ) .
204
		Html::namespaceSelector( [
205
			'selected' => $ns === null ? '' : $ns,
206
			'all' => '',
207
			'exclude' => $excludeNamespaces,
208
			'label' => $this->msg( 'namespace' )->text()
209
		] ) . ' ' .
210
		Html::submitButton(
211
			$this->msg( 'wikibase-unconnectedpages-submit' )->text(),
212
			[]
213
		) .
214
		Html::closeElement( 'form' );
215
	}
216
217
	/**
218
	 * @see SpecialPage::getGroupName
219
	 *
220
	 * @return string
221
	 */
222
	protected function getGroupName() {
223
		return 'maintenance';
224
	}
225
226
	/**
227
	 * @see QueryPage::linkParameters
228
	 *
229
	 * @return array
230
	 */
231
	public function linkParameters() {
232
		return [
233
			'namespace' => $this->getRequest()->getIntOrNull( 'namespace' ),
234
		];
235
	}
236
237
}
238