ApiQuerySiteinfo::appendDefaultOptions()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 *
4
 *
5
 * Created on Sep 25, 2006
6
 *
7
 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License along
20
 * with this program; if not, write to the Free Software Foundation, Inc.,
21
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
 * http://www.gnu.org/copyleft/gpl.html
23
 *
24
 * @file
25
 */
26
27
/**
28
 * A query action to return meta information about the wiki site.
29
 *
30
 * @ingroup API
31
 */
32
class ApiQuerySiteinfo extends ApiQueryBase {
33
34
	public function __construct( ApiQuery $query, $moduleName ) {
35
		parent::__construct( $query, $moduleName, 'si' );
36
	}
37
38
	public function execute() {
39
		$params = $this->extractRequestParams();
40
		$done = [];
41
		$fit = false;
42
		foreach ( $params['prop'] as $p ) {
43
			switch ( $p ) {
44
				case 'general':
45
					$fit = $this->appendGeneralInfo( $p );
46
					break;
47
				case 'namespaces':
48
					$fit = $this->appendNamespaces( $p );
49
					break;
50
				case 'namespacealiases':
51
					$fit = $this->appendNamespaceAliases( $p );
52
					break;
53
				case 'specialpagealiases':
54
					$fit = $this->appendSpecialPageAliases( $p );
55
					break;
56
				case 'magicwords':
57
					$fit = $this->appendMagicWords( $p );
58
					break;
59
				case 'interwikimap':
60
					$filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
61
					$fit = $this->appendInterwikiMap( $p, $filteriw );
62
					break;
63
				case 'dbrepllag':
64
					$fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
65
					break;
66
				case 'statistics':
67
					$fit = $this->appendStatistics( $p );
68
					break;
69
				case 'usergroups':
70
					$fit = $this->appendUserGroups( $p, $params['numberingroup'] );
71
					break;
72
				case 'libraries':
73
					$fit = $this->appendInstalledLibraries( $p );
74
					break;
75
				case 'extensions':
76
					$fit = $this->appendExtensions( $p );
77
					break;
78
				case 'fileextensions':
79
					$fit = $this->appendFileExtensions( $p );
80
					break;
81
				case 'rightsinfo':
82
					$fit = $this->appendRightsInfo( $p );
83
					break;
84
				case 'restrictions':
85
					$fit = $this->appendRestrictions( $p );
86
					break;
87
				case 'languages':
88
					$fit = $this->appendLanguages( $p );
89
					break;
90
				case 'skins':
91
					$fit = $this->appendSkins( $p );
92
					break;
93
				case 'extensiontags':
94
					$fit = $this->appendExtensionTags( $p );
95
					break;
96
				case 'functionhooks':
97
					$fit = $this->appendFunctionHooks( $p );
98
					break;
99
				case 'showhooks':
100
					$fit = $this->appendSubscribedHooks( $p );
101
					break;
102
				case 'variables':
103
					$fit = $this->appendVariables( $p );
104
					break;
105
				case 'protocols':
106
					$fit = $this->appendProtocols( $p );
107
					break;
108
				case 'defaultoptions':
109
					$fit = $this->appendDefaultOptions( $p );
110
					break;
111
				case 'uploaddialog':
112
					$fit = $this->appendUploadDialog( $p );
113
					break;
114
				default:
115
					ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
116
			}
117
			if ( !$fit ) {
118
				// Abuse siprop as a query-continue parameter
119
				// and set it to all unprocessed props
120
				$this->setContinueEnumParameter( 'prop', implode( '|',
121
					array_diff( $params['prop'], $done ) ) );
122
				break;
123
			}
124
			$done[] = $p;
125
		}
126
	}
127
128
	protected function appendGeneralInfo( $property ) {
0 ignored issues
show
Coding Style introduced by
appendGeneralInfo uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
129
		global $wgContLang;
130
131
		$config = $this->getConfig();
132
133
		$data = [];
134
		$mainPage = Title::newMainPage();
135
		$data['mainpage'] = $mainPage->getPrefixedText();
136
		$data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
137
		$data['sitename'] = $config->get( 'Sitename' );
138
139
		// wgLogo can either be a relative or an absolute path
140
		// make sure we always return an absolute path
141
		$data['logo'] = wfExpandUrl( $config->get( 'Logo' ), PROTO_RELATIVE );
142
143
		$data['generator'] = "MediaWiki {$config->get( 'Version' )}";
144
145
		$data['phpversion'] = PHP_VERSION;
146
		$data['phpsapi'] = PHP_SAPI;
147
		if ( defined( 'HHVM_VERSION' ) ) {
148
			$data['hhvmversion'] = HHVM_VERSION;
149
		}
150
		$data['dbtype'] = $config->get( 'DBtype' );
151
		$data['dbversion'] = $this->getDB()->getServerVersion();
152
153
		$allowFrom = [ '' ];
154
		$allowException = true;
155
		if ( !$config->get( 'AllowExternalImages' ) ) {
156
			$data['imagewhitelistenabled'] = (bool)$config->get( 'EnableImageWhitelist' );
157
			$allowFrom = $config->get( 'AllowExternalImagesFrom' );
158
			$allowException = !empty( $allowFrom );
159
		}
160
		if ( $allowException ) {
161
			$data['externalimages'] = (array)$allowFrom;
162
			ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
163
		}
164
165
		$data['langconversion'] = !$config->get( 'DisableLangConversion' );
166
		$data['titleconversion'] = !$config->get( 'DisableTitleConversion' );
167
168
		if ( $wgContLang->linkPrefixExtension() ) {
169
			$linkPrefixCharset = $wgContLang->linkPrefixCharset();
170
			$data['linkprefixcharset'] = $linkPrefixCharset;
171
			// For backwards compatibility
172
			$data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
173
		} else {
174
			$data['linkprefixcharset'] = '';
175
			$data['linkprefix'] = '';
176
		}
177
178
		$linktrail = $wgContLang->linkTrail();
179
		$data['linktrail'] = $linktrail ?: '';
180
181
		$data['legaltitlechars'] = Title::legalChars();
182
		$data['invalidusernamechars'] = $config->get( 'InvalidUsernameCharacters' );
183
184
		$data['allunicodefixes'] = (bool)$config->get( 'AllUnicodeFixes' );
185
		$data['fixarabicunicode'] = (bool)$config->get( 'FixArabicUnicode' );
186
		$data['fixmalayalamunicode'] = (bool)$config->get( 'FixMalayalamUnicode' );
187
188
		global $IP;
189
		$git = SpecialVersion::getGitHeadSha1( $IP );
190
		if ( $git ) {
191
			$data['git-hash'] = $git;
192
			$data['git-branch'] =
193
				SpecialVersion::getGitCurrentBranch( $GLOBALS['IP'] );
194
		}
195
196
		// 'case-insensitive' option is reserved for future
197
		$data['case'] = $config->get( 'CapitalLinks' ) ? 'first-letter' : 'case-sensitive';
198
		$data['lang'] = $config->get( 'LanguageCode' );
199
200
		$fallbacks = [];
201
		foreach ( $wgContLang->getFallbackLanguages() as $code ) {
202
			$fallbacks[] = [ 'code' => $code ];
203
		}
204
		$data['fallback'] = $fallbacks;
205
		ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
206
207
		if ( $wgContLang->hasVariants() ) {
208
			$variants = [];
209
			foreach ( $wgContLang->getVariants() as $code ) {
210
				$variants[] = [
211
					'code' => $code,
212
					'name' => $wgContLang->getVariantname( $code ),
213
				];
214
			}
215
			$data['variants'] = $variants;
216
			ApiResult::setIndexedTagName( $data['variants'], 'lang' );
217
		}
218
219
		$data['rtl'] = $wgContLang->isRTL();
220
		$data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
221
222
		$data['readonly'] = wfReadOnly();
223
		if ( $data['readonly'] ) {
224
			$data['readonlyreason'] = wfReadOnlyReason();
225
		}
226
		$data['writeapi'] = (bool)$config->get( 'EnableWriteAPI' );
227
228
		$data['maxarticlesize'] = $config->get( 'MaxArticleSize' ) * 1024;
229
230
		$tz = $config->get( 'Localtimezone' );
231
		$offset = $config->get( 'LocalTZoffset' );
232
		if ( is_null( $tz ) ) {
233
			$tz = 'UTC';
234
			$offset = 0;
235
		} elseif ( is_null( $offset ) ) {
236
			$offset = 0;
237
		}
238
		$data['timezone'] = $tz;
239
		$data['timeoffset'] = intval( $offset );
240
		$data['articlepath'] = $config->get( 'ArticlePath' );
241
		$data['scriptpath'] = $config->get( 'ScriptPath' );
242
		$data['script'] = $config->get( 'Script' );
243
		$data['variantarticlepath'] = $config->get( 'VariantArticlePath' );
244
		$data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
245
		$data['server'] = $config->get( 'Server' );
246
		$data['servername'] = $config->get( 'ServerName' );
247
		$data['wikiid'] = wfWikiID();
248
		$data['time'] = wfTimestamp( TS_ISO_8601, time() );
249
250
		$data['misermode'] = (bool)$config->get( 'MiserMode' );
251
252
		$data['uploadsenabled'] = UploadBase::isEnabled();
253
		$data['maxuploadsize'] = UploadBase::getMaxUploadSize();
254
		$data['minuploadchunksize'] = (int)$config->get( 'MinUploadChunkSize' );
255
256
		$data['thumblimits'] = $config->get( 'ThumbLimits' );
257
		ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
258
		ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
259
		$data['imagelimits'] = [];
260
		ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
261
		ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
262
		foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
263
			$data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
264
		}
265
266
		$favicon = $config->get( 'Favicon' );
267
		if ( !empty( $favicon ) ) {
268
			// wgFavicon can either be a relative or an absolute path
269
			// make sure we always return an absolute path
270
			$data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
271
		}
272
273
		$data['centralidlookupprovider'] = $config->get( 'CentralIdLookupProvider' );
274
		$providerIds = array_keys( $config->get( 'CentralIdLookupProviders' ) );
275
		$data['allcentralidlookupproviders'] = $providerIds;
276
277
		$data['interwikimagic'] = (bool)$config->get( 'InterwikiMagic' );
278
		$data['magiclinks'] = $config->get( 'EnableMagicLinks' );
279
280
		Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
281
282
		return $this->getResult()->addValue( 'query', $property, $data );
283
	}
284
285
	protected function appendNamespaces( $property ) {
286
		global $wgContLang;
287
		$data = [
288
			ApiResult::META_TYPE => 'assoc',
289
		];
290
		foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
291
			$data[$ns] = [
292
				'id' => intval( $ns ),
293
				'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
294
			];
295
			ApiResult::setContentValue( $data[$ns], 'name', $title );
0 ignored issues
show
Bug introduced by
It seems like $data[$ns] can also be of type string; however, ApiResult::setContentValue() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
296
			$canonical = MWNamespace::getCanonicalName( $ns );
297
298
			$data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
299
300
			if ( $canonical ) {
301
				$data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
0 ignored issues
show
Bug introduced by
It seems like $canonical defined by \MWNamespace::getCanonicalName($ns) on line 296 can also be of type boolean; however, strtr() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
302
			}
303
304
			$data[$ns]['content'] = MWNamespace::isContent( $ns );
305
			$data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
306
307
			$contentmodel = MWNamespace::getNamespaceContentModel( $ns );
308
			if ( $contentmodel ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $contentmodel of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
309
				$data[$ns]['defaultcontentmodel'] = $contentmodel;
310
			}
311
		}
312
313
		ApiResult::setArrayType( $data, 'assoc' );
314
		ApiResult::setIndexedTagName( $data, 'ns' );
315
316
		return $this->getResult()->addValue( 'query', $property, $data );
317
	}
318
319
	protected function appendNamespaceAliases( $property ) {
320
		global $wgContLang;
321
		$aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
322
			$wgContLang->getNamespaceAliases() );
323
		$namespaces = $wgContLang->getNamespaces();
324
		$data = [];
325
		foreach ( $aliases as $title => $ns ) {
326
			if ( $namespaces[$ns] == $title ) {
327
				// Don't list duplicates
328
				continue;
329
			}
330
			$item = [
331
				'id' => intval( $ns )
332
			];
333
			ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
334
			$data[] = $item;
335
		}
336
337
		sort( $data );
338
339
		ApiResult::setIndexedTagName( $data, 'ns' );
340
341
		return $this->getResult()->addValue( 'query', $property, $data );
342
	}
343
344 View Code Duplication
	protected function appendSpecialPageAliases( $property ) {
345
		global $wgContLang;
346
		$data = [];
347
		$aliases = $wgContLang->getSpecialPageAliases();
348
		foreach ( SpecialPageFactory::getNames() as $specialpage ) {
349
			if ( isset( $aliases[$specialpage] ) ) {
350
				$arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
351
				ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
352
				$data[] = $arr;
353
			}
354
		}
355
		ApiResult::setIndexedTagName( $data, 'specialpage' );
356
357
		return $this->getResult()->addValue( 'query', $property, $data );
358
	}
359
360 View Code Duplication
	protected function appendMagicWords( $property ) {
361
		global $wgContLang;
362
		$data = [];
363
		foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
364
			$caseSensitive = array_shift( $aliases );
365
			$arr = [ 'name' => $magicword, 'aliases' => $aliases ];
366
			$arr['case-sensitive'] = (bool)$caseSensitive;
367
			ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
368
			$data[] = $arr;
369
		}
370
		ApiResult::setIndexedTagName( $data, 'magicword' );
371
372
		return $this->getResult()->addValue( 'query', $property, $data );
373
	}
374
375
	protected function appendInterwikiMap( $property, $filter ) {
376
		$local = null;
377
		if ( $filter === 'local' ) {
378
			$local = 1;
379
		} elseif ( $filter === '!local' ) {
380
			$local = 0;
381
		} elseif ( $filter ) {
382
			ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
383
		}
384
385
		$params = $this->extractRequestParams();
386
		$langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
387
		$langNames = Language::fetchLanguageNames( $langCode );
388
389
		$getPrefixes = Interwiki::getAllPrefixes( $local );
390
		$extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
391
		$localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
392
		$data = [];
393
394
		foreach ( $getPrefixes as $row ) {
395
			$prefix = $row['iw_prefix'];
396
			$val = [];
397
			$val['prefix'] = $prefix;
398
			if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
399
				$val['local'] = true;
400
			}
401
			if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
402
				$val['trans'] = true;
403
			}
404
405
			if ( isset( $langNames[$prefix] ) ) {
406
				$val['language'] = $langNames[$prefix];
407
			}
408
			if ( in_array( $prefix, $localInterwikis ) ) {
409
				$val['localinterwiki'] = true;
410
			}
411
			if ( in_array( $prefix, $extraLangPrefixes ) ) {
412
				$val['extralanglink'] = true;
413
414
				$linktext = wfMessage( "interlanguage-link-$prefix" );
415
				if ( !$linktext->isDisabled() ) {
416
					$val['linktext'] = $linktext->text();
417
				}
418
419
				$sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
420
				if ( !$sitename->isDisabled() ) {
421
					$val['sitename'] = $sitename->text();
422
				}
423
			}
424
425
			$val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
426
			$val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
427
			if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
428
				$val['wikiid'] = $row['iw_wikiid'];
429
			}
430
			if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
431
				$val['api'] = $row['iw_api'];
432
			}
433
434
			$data[] = $val;
435
		}
436
437
		ApiResult::setIndexedTagName( $data, 'iw' );
438
439
		return $this->getResult()->addValue( 'query', $property, $data );
440
	}
441
442
	protected function appendDbReplLagInfo( $property, $includeAll ) {
443
		$data = [];
444
		$lb = wfGetLB();
0 ignored issues
show
Deprecated Code introduced by
The function wfGetLB() has been deprecated with message: since 1.27, use MediaWikiServices::getDBLoadBalancer() or MediaWikiServices::getDBLoadBalancerFactory() instead.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
445
		$showHostnames = $this->getConfig()->get( 'ShowHostnames' );
446
		if ( $includeAll ) {
447
			if ( !$showHostnames ) {
448
				$this->dieUsage(
449
					'Cannot view all servers info unless $wgShowHostnames is true',
450
					'includeAllDenied'
451
				);
452
			}
453
454
			$lags = $lb->getLagTimes();
455
			foreach ( $lags as $i => $lag ) {
456
				$data[] = [
457
					'host' => $lb->getServerName( $i ),
458
					'lag' => $lag
459
				];
460
			}
461
		} else {
462
			list( , $lag, $index ) = $lb->getMaxLag();
463
			$data[] = [
464
				'host' => $showHostnames
465
						? $lb->getServerName( $index )
466
						: '',
467
				'lag' => intval( $lag )
468
			];
469
		}
470
471
		ApiResult::setIndexedTagName( $data, 'db' );
472
473
		return $this->getResult()->addValue( 'query', $property, $data );
474
	}
475
476
	protected function appendStatistics( $property ) {
477
		$data = [];
478
		$data['pages'] = intval( SiteStats::pages() );
479
		$data['articles'] = intval( SiteStats::articles() );
480
		$data['edits'] = intval( SiteStats::edits() );
481
		$data['images'] = intval( SiteStats::images() );
482
		$data['users'] = intval( SiteStats::users() );
483
		$data['activeusers'] = intval( SiteStats::activeUsers() );
484
		$data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
485
		$data['jobs'] = intval( SiteStats::jobs() );
486
487
		Hooks::run( 'APIQuerySiteInfoStatisticsInfo', [ &$data ] );
488
489
		return $this->getResult()->addValue( 'query', $property, $data );
490
	}
491
492
	protected function appendUserGroups( $property, $numberInGroup ) {
493
		$config = $this->getConfig();
494
495
		$data = [];
496
		$result = $this->getResult();
497
		$allGroups = array_values( User::getAllGroups() );
498
		foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
499
			$arr = [
500
				'name' => $group,
501
				'rights' => array_keys( $permissions, true ),
502
			];
503
504
			if ( $numberInGroup ) {
505
				$autopromote = $config->get( 'Autopromote' );
506
507
				if ( $group == 'user' ) {
508
					$arr['number'] = SiteStats::users();
509
				// '*' and autopromote groups have no size
510
				} elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
511
					$arr['number'] = SiteStats::numberingroup( $group );
512
				}
513
			}
514
515
			$groupArr = [
516
				'add' => $config->get( 'AddGroups' ),
517
				'remove' => $config->get( 'RemoveGroups' ),
518
				'add-self' => $config->get( 'GroupsAddToSelf' ),
519
				'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
520
			];
521
522
			foreach ( $groupArr as $type => $rights ) {
523
				if ( isset( $rights[$group] ) ) {
524
					if ( $rights[$group] === true ) {
525
						$groups = $allGroups;
526
					} else {
527
						$groups = array_intersect( $rights[$group], $allGroups );
528
					}
529
					if ( $groups ) {
530
						$arr[$type] = $groups;
531
						ApiResult::setArrayType( $arr[$type], 'BCarray' );
532
						ApiResult::setIndexedTagName( $arr[$type], 'group' );
533
					}
534
				}
535
			}
536
537
			ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
538
			$data[] = $arr;
539
		}
540
541
		ApiResult::setIndexedTagName( $data, 'group' );
542
543
		return $result->addValue( 'query', $property, $data );
544
	}
545
546
	protected function appendFileExtensions( $property ) {
547
		$data = [];
548
		foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
549
			$data[] = [ 'ext' => $ext ];
550
		}
551
		ApiResult::setIndexedTagName( $data, 'fe' );
552
553
		return $this->getResult()->addValue( 'query', $property, $data );
554
	}
555
556
	protected function appendInstalledLibraries( $property ) {
557
		global $IP;
558
		$path = "$IP/vendor/composer/installed.json";
559
		if ( !file_exists( $path ) ) {
560
			return true;
561
		}
562
563
		$data = [];
564
		$installed = new ComposerInstalled( $path );
565
		foreach ( $installed->getInstalledDependencies() as $name => $info ) {
566
			if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
567
				// Skip any extensions or skins since they'll be listed
568
				// in their proper section
569
				continue;
570
			}
571
			$data[] = [
572
				'name' => $name,
573
				'version' => $info['version'],
574
			];
575
		}
576
		ApiResult::setIndexedTagName( $data, 'library' );
577
578
		return $this->getResult()->addValue( 'query', $property, $data );
579
	}
580
581
	protected function appendExtensions( $property ) {
582
		$data = [];
583
		foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
584
			foreach ( $extensions as $ext ) {
585
				$ret = [];
586
				$ret['type'] = $type;
587
				if ( isset( $ext['name'] ) ) {
588
					$ret['name'] = $ext['name'];
589
				}
590
				if ( isset( $ext['namemsg'] ) ) {
591
					$ret['namemsg'] = $ext['namemsg'];
592
				}
593
				if ( isset( $ext['description'] ) ) {
594
					$ret['description'] = $ext['description'];
595
				}
596
				if ( isset( $ext['descriptionmsg'] ) ) {
597
					// Can be a string or [ key, param1, param2, ... ]
598
					if ( is_array( $ext['descriptionmsg'] ) ) {
599
						$ret['descriptionmsg'] = $ext['descriptionmsg'][0];
600
						$ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
601
						ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
602
					} else {
603
						$ret['descriptionmsg'] = $ext['descriptionmsg'];
604
					}
605
				}
606
				if ( isset( $ext['author'] ) ) {
607
					$ret['author'] = is_array( $ext['author'] ) ?
608
						implode( ', ', $ext['author'] ) : $ext['author'];
609
				}
610
				if ( isset( $ext['url'] ) ) {
611
					$ret['url'] = $ext['url'];
612
				}
613
				if ( isset( $ext['version'] ) ) {
614
					$ret['version'] = $ext['version'];
615
				}
616
				if ( isset( $ext['path'] ) ) {
617
					$extensionPath = dirname( $ext['path'] );
618
					$gitInfo = new GitInfo( $extensionPath );
619
					$vcsVersion = $gitInfo->getHeadSHA1();
620
					if ( $vcsVersion !== false ) {
621
						$ret['vcs-system'] = 'git';
622
						$ret['vcs-version'] = $vcsVersion;
623
						$ret['vcs-url'] = $gitInfo->getHeadViewUrl();
624
						$vcsDate = $gitInfo->getHeadCommitDate();
625
						if ( $vcsDate !== false ) {
626
							$ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
0 ignored issues
show
Bug introduced by
It seems like $vcsDate defined by $gitInfo->getHeadCommitDate() on line 624 can also be of type boolean; however, wfTimestamp() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
627
						}
628
					}
629
630
					if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \SpecialVersion::getExtL...ileName($extensionPath) of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
631
						$ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
632
						$ret['license'] = SpecialPage::getTitleFor(
633
							'Version',
634
							"License/{$ext['name']}"
635
						)->getLinkURL();
636
					}
637
638
					if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \SpecialVersion::getExtA...ileName($extensionPath) of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
639
						$ret['credits'] = SpecialPage::getTitleFor(
640
							'Version',
641
							"Credits/{$ext['name']}"
642
						)->getLinkURL();
643
					}
644
				}
645
				$data[] = $ret;
646
			}
647
		}
648
649
		ApiResult::setIndexedTagName( $data, 'ext' );
650
651
		return $this->getResult()->addValue( 'query', $property, $data );
652
	}
653
654
	protected function appendRightsInfo( $property ) {
655
		$config = $this->getConfig();
656
		$rightsPage = $config->get( 'RightsPage' );
657
		if ( is_string( $rightsPage ) ) {
658
			$title = Title::newFromText( $rightsPage );
659
			$url = wfExpandUrl( $title, PROTO_CURRENT );
660
		} else {
661
			$title = false;
662
			$url = $config->get( 'RightsUrl' );
663
		}
664
		$text = $config->get( 'RightsText' );
665
		if ( !$text && $title ) {
666
			$text = $title->getPrefixedText();
667
		}
668
669
		$data = [
670
			'url' => $url ?: '',
671
			'text' => $text ?: ''
672
		];
673
674
		return $this->getResult()->addValue( 'query', $property, $data );
675
	}
676
677
	protected function appendRestrictions( $property ) {
678
		$config = $this->getConfig();
679
		$data = [
680
			'types' => $config->get( 'RestrictionTypes' ),
681
			'levels' => $config->get( 'RestrictionLevels' ),
682
			'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
683
			'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
684
		];
685
686
		ApiResult::setArrayType( $data['types'], 'BCarray' );
687
		ApiResult::setArrayType( $data['levels'], 'BCarray' );
688
		ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
689
		ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
690
691
		ApiResult::setIndexedTagName( $data['types'], 'type' );
692
		ApiResult::setIndexedTagName( $data['levels'], 'level' );
693
		ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
694
		ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
695
696
		return $this->getResult()->addValue( 'query', $property, $data );
697
	}
698
699
	public function appendLanguages( $property ) {
700
		$params = $this->extractRequestParams();
701
		$langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
702
		$langNames = Language::fetchLanguageNames( $langCode );
703
704
		$data = [];
705
706
		foreach ( $langNames as $code => $name ) {
707
			$lang = [ 'code' => $code ];
708
			ApiResult::setContentValue( $lang, 'name', $name );
709
			$data[] = $lang;
710
		}
711
		ApiResult::setIndexedTagName( $data, 'lang' );
712
713
		return $this->getResult()->addValue( 'query', $property, $data );
714
	}
715
716
	public function appendSkins( $property ) {
717
		$data = [];
718
		$allowed = Skin::getAllowedSkins();
719
		$default = Skin::normalizeKey( 'default' );
720
		foreach ( Skin::getSkinNames() as $name => $displayName ) {
721
			$msg = $this->msg( "skinname-{$name}" );
722
			$code = $this->getParameter( 'inlanguagecode' );
723
			if ( $code && Language::isValidCode( $code ) ) {
724
				$msg->inLanguage( $code );
725
			} else {
726
				$msg->inContentLanguage();
727
			}
728
			if ( $msg->exists() ) {
729
				$displayName = $msg->text();
730
			}
731
			$skin = [ 'code' => $name ];
732
			ApiResult::setContentValue( $skin, 'name', $displayName );
733
			if ( !isset( $allowed[$name] ) ) {
734
				$skin['unusable'] = true;
735
			}
736
			if ( $name === $default ) {
737
				$skin['default'] = true;
738
			}
739
			$data[] = $skin;
740
		}
741
		ApiResult::setIndexedTagName( $data, 'skin' );
742
743
		return $this->getResult()->addValue( 'query', $property, $data );
744
	}
745
746 View Code Duplication
	public function appendExtensionTags( $property ) {
747
		global $wgParser;
748
		$wgParser->firstCallInit();
749
		$tags = array_map( [ $this, 'formatParserTags' ], $wgParser->getTags() );
750
		ApiResult::setArrayType( $tags, 'BCarray' );
751
		ApiResult::setIndexedTagName( $tags, 't' );
752
753
		return $this->getResult()->addValue( 'query', $property, $tags );
754
	}
755
756 View Code Duplication
	public function appendFunctionHooks( $property ) {
757
		global $wgParser;
758
		$wgParser->firstCallInit();
759
		$hooks = $wgParser->getFunctionHooks();
760
		ApiResult::setArrayType( $hooks, 'BCarray' );
761
		ApiResult::setIndexedTagName( $hooks, 'h' );
762
763
		return $this->getResult()->addValue( 'query', $property, $hooks );
764
	}
765
766
	public function appendVariables( $property ) {
767
		$variables = MagicWord::getVariableIDs();
768
		ApiResult::setArrayType( $variables, 'BCarray' );
769
		ApiResult::setIndexedTagName( $variables, 'v' );
770
771
		return $this->getResult()->addValue( 'query', $property, $variables );
772
	}
773
774
	public function appendProtocols( $property ) {
775
		// Make a copy of the global so we don't try to set the _element key of it - bug 45130
776
		$protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
777
		ApiResult::setArrayType( $protocols, 'BCarray' );
778
		ApiResult::setIndexedTagName( $protocols, 'p' );
779
780
		return $this->getResult()->addValue( 'query', $property, $protocols );
781
	}
782
783
	public function appendDefaultOptions( $property ) {
784
		$options = User::getDefaultOptions();
785
		$options[ApiResult::META_BC_BOOLS] = array_keys( $options );
786
		return $this->getResult()->addValue( 'query', $property, $options );
787
	}
788
789
	public function appendUploadDialog( $property ) {
790
		$config = $this->getConfig()->get( 'UploadDialog' );
791
		return $this->getResult()->addValue( 'query', $property, $config );
792
	}
793
794
	private function formatParserTags( $item ) {
795
		return "<{$item}>";
796
	}
797
798
	public function appendSubscribedHooks( $property ) {
799
		$hooks = $this->getConfig()->get( 'Hooks' );
800
		$myWgHooks = $hooks;
801
		ksort( $myWgHooks );
802
803
		$data = [];
804
		foreach ( $myWgHooks as $name => $subscribers ) {
805
			$arr = [
806
				'name' => $name,
807
				'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
808
			];
809
810
			ApiResult::setArrayType( $arr['subscribers'], 'array' );
811
			ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
812
			$data[] = $arr;
813
		}
814
815
		ApiResult::setIndexedTagName( $data, 'hook' );
816
817
		return $this->getResult()->addValue( 'query', $property, $data );
818
	}
819
820
	public function getCacheMode( $params ) {
821
		// Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
822
		if (
823
			count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
824
			!is_null( $params['prop'] ) &&
825
			in_array( 'interwikimap', $params['prop'] )
826
		) {
827
			return 'anon-public-user-private';
828
		}
829
830
		return 'public';
831
	}
832
833
	public function getAllowedParams() {
834
		return [
835
			'prop' => [
836
				ApiBase::PARAM_DFLT => 'general',
837
				ApiBase::PARAM_ISMULTI => true,
838
				ApiBase::PARAM_TYPE => [
839
					'general',
840
					'namespaces',
841
					'namespacealiases',
842
					'specialpagealiases',
843
					'magicwords',
844
					'interwikimap',
845
					'dbrepllag',
846
					'statistics',
847
					'usergroups',
848
					'libraries',
849
					'extensions',
850
					'fileextensions',
851
					'rightsinfo',
852
					'restrictions',
853
					'languages',
854
					'skins',
855
					'extensiontags',
856
					'functionhooks',
857
					'showhooks',
858
					'variables',
859
					'protocols',
860
					'defaultoptions',
861
					'uploaddialog',
862
				],
863
				ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
864
			],
865
			'filteriw' => [
866
				ApiBase::PARAM_TYPE => [
867
					'local',
868
					'!local',
869
				]
870
			],
871
			'showalldb' => false,
872
			'numberingroup' => false,
873
			'inlanguagecode' => null,
874
		];
875
	}
876
877
	protected function getExamplesMessages() {
878
		return [
879
			'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
880
				=> 'apihelp-query+siteinfo-example-simple',
881
			'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
882
				=> 'apihelp-query+siteinfo-example-interwiki',
883
			'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
884
				=> 'apihelp-query+siteinfo-example-replag',
885
		];
886
	}
887
888
	public function getHelpUrls() {
889
		return 'https://www.mediawiki.org/wiki/API:Siteinfo';
890
	}
891
}
892