wikimedia /
mediawiki
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Displays information about a page. |
||
| 4 | * |
||
| 5 | * Copyright © 2011 Alexandre Emsenhuber |
||
| 6 | * |
||
| 7 | * This program is free software; you can redistribute it and/or modify |
||
| 8 | * it under the terms of the GNU General Public License as published by |
||
| 9 | * the Free Software Foundation; either version 2 of the License, or |
||
| 10 | * (at your option) any later version. |
||
| 11 | * |
||
| 12 | * This program is distributed in the hope that it will be useful, |
||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 15 | * GNU General Public License for more details. |
||
| 16 | * |
||
| 17 | * You should have received a copy of the GNU General Public License |
||
| 18 | * along with this program; if not, write to the Free Software |
||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
||
| 20 | * |
||
| 21 | * @file |
||
| 22 | * @ingroup Actions |
||
| 23 | */ |
||
| 24 | |||
| 25 | use MediaWiki\MediaWikiServices; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * Displays information about a page. |
||
| 29 | * |
||
| 30 | * @ingroup Actions |
||
| 31 | */ |
||
| 32 | class InfoAction extends FormlessAction { |
||
| 33 | const VERSION = 1; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * Returns the name of the action this object responds to. |
||
| 37 | * |
||
| 38 | * @return string Lowercase name |
||
| 39 | */ |
||
| 40 | public function getName() { |
||
| 41 | return 'info'; |
||
| 42 | } |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Whether this action can still be executed by a blocked user. |
||
| 46 | * |
||
| 47 | * @return bool |
||
| 48 | */ |
||
| 49 | public function requiresUnblock() { |
||
| 50 | return false; |
||
| 51 | } |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Whether this action requires the wiki not to be locked. |
||
| 55 | * |
||
| 56 | * @return bool |
||
| 57 | */ |
||
| 58 | public function requiresWrite() { |
||
| 59 | return false; |
||
| 60 | } |
||
| 61 | |||
| 62 | /** |
||
| 63 | * Clear the info cache for a given Title. |
||
| 64 | * |
||
| 65 | * @since 1.22 |
||
| 66 | * @param Title $title Title to clear cache for |
||
| 67 | * @param int|null $revid Revision id to clear |
||
| 68 | */ |
||
| 69 | public static function invalidateCache( Title $title, $revid = null ) { |
||
| 70 | if ( !$revid ) { |
||
|
0 ignored issues
–
show
|
|||
| 71 | $revision = Revision::newFromTitle( $title, 0, Revision::READ_LATEST ); |
||
| 72 | $revid = $revision ? $revision->getId() : null; |
||
| 73 | } |
||
| 74 | if ( $revid !== null ) { |
||
| 75 | $key = self::getCacheKey( $title, $revid ); |
||
| 76 | ObjectCache::getMainWANInstance()->delete( $key ); |
||
| 77 | } |
||
| 78 | } |
||
| 79 | |||
| 80 | /** |
||
| 81 | * Shows page information on GET request. |
||
| 82 | * |
||
| 83 | * @return string Page information that will be added to the output |
||
| 84 | */ |
||
| 85 | public function onView() { |
||
| 86 | $content = ''; |
||
| 87 | |||
| 88 | // Validate revision |
||
| 89 | $oldid = $this->page->getOldID(); |
||
|
0 ignored issues
–
show
The method
getOldID does only exist in Article and CategoryPage and ImagePage, but not in Page and WikiPage.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 90 | if ( $oldid ) { |
||
| 91 | $revision = $this->page->getRevisionFetched(); |
||
|
0 ignored issues
–
show
The method
getRevisionFetched does only exist in Article and CategoryPage and ImagePage, but not in Page and WikiPage.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 92 | |||
| 93 | // Revision is missing |
||
| 94 | if ( $revision === null ) { |
||
| 95 | return $this->msg( 'missing-revision', $oldid )->parse(); |
||
| 96 | } |
||
| 97 | |||
| 98 | // Revision is not current |
||
| 99 | if ( !$revision->isCurrent() ) { |
||
| 100 | return $this->msg( 'pageinfo-not-current' )->plain(); |
||
| 101 | } |
||
| 102 | } |
||
| 103 | |||
| 104 | // Page header |
||
| 105 | if ( !$this->msg( 'pageinfo-header' )->isDisabled() ) { |
||
| 106 | $content .= $this->msg( 'pageinfo-header' )->parse(); |
||
| 107 | } |
||
| 108 | |||
| 109 | // Hide "This page is a member of # hidden categories" explanation |
||
| 110 | $content .= Html::element( 'style', [], |
||
| 111 | '.mw-hiddenCategoriesExplanation { display: none; }' ) . "\n"; |
||
| 112 | |||
| 113 | // Hide "Templates used on this page" explanation |
||
| 114 | $content .= Html::element( 'style', [], |
||
| 115 | '.mw-templatesUsedExplanation { display: none; }' ) . "\n"; |
||
| 116 | |||
| 117 | // Get page information |
||
| 118 | $pageInfo = $this->pageInfo(); |
||
| 119 | |||
| 120 | // Allow extensions to add additional information |
||
| 121 | Hooks::run( 'InfoAction', [ $this->getContext(), &$pageInfo ] ); |
||
| 122 | |||
| 123 | // Render page information |
||
| 124 | foreach ( $pageInfo as $header => $infoTable ) { |
||
| 125 | // Messages: |
||
| 126 | // pageinfo-header-basic, pageinfo-header-edits, pageinfo-header-restrictions, |
||
| 127 | // pageinfo-header-properties, pageinfo-category-info |
||
| 128 | $content .= $this->makeHeader( $this->msg( "pageinfo-${header}" )->escaped() ) . "\n"; |
||
| 129 | $table = "\n"; |
||
| 130 | foreach ( $infoTable as $infoRow ) { |
||
| 131 | $name = ( $infoRow[0] instanceof Message ) ? $infoRow[0]->escaped() : $infoRow[0]; |
||
| 132 | $value = ( $infoRow[1] instanceof Message ) ? $infoRow[1]->escaped() : $infoRow[1]; |
||
| 133 | $id = ( $infoRow[0] instanceof Message ) ? $infoRow[0]->getKey() : null; |
||
| 134 | $table = $this->addRow( $table, $name, $value, $id ) . "\n"; |
||
| 135 | } |
||
| 136 | $content = $this->addTable( $content, $table ) . "\n"; |
||
| 137 | } |
||
| 138 | |||
| 139 | // Page footer |
||
| 140 | if ( !$this->msg( 'pageinfo-footer' )->isDisabled() ) { |
||
| 141 | $content .= $this->msg( 'pageinfo-footer' )->parse(); |
||
| 142 | } |
||
| 143 | |||
| 144 | return $content; |
||
| 145 | } |
||
| 146 | |||
| 147 | /** |
||
| 148 | * Creates a header that can be added to the output. |
||
| 149 | * |
||
| 150 | * @param string $header The header text. |
||
| 151 | * @return string The HTML. |
||
| 152 | */ |
||
| 153 | protected function makeHeader( $header ) { |
||
| 154 | $spanAttribs = [ 'class' => 'mw-headline', 'id' => Sanitizer::escapeId( $header ) ]; |
||
| 155 | |||
| 156 | return Html::rawElement( 'h2', [], Html::element( 'span', $spanAttribs, $header ) ); |
||
| 157 | } |
||
| 158 | |||
| 159 | /** |
||
| 160 | * Adds a row to a table that will be added to the content. |
||
| 161 | * |
||
| 162 | * @param string $table The table that will be added to the content |
||
| 163 | * @param string $name The name of the row |
||
| 164 | * @param string $value The value of the row |
||
| 165 | * @param string $id The ID to use for the 'tr' element |
||
| 166 | * @return string The table with the row added |
||
| 167 | */ |
||
| 168 | protected function addRow( $table, $name, $value, $id ) { |
||
| 169 | return $table . |
||
| 170 | Html::rawElement( |
||
| 171 | 'tr', |
||
| 172 | $id === null ? [] : [ 'id' => 'mw-' . $id ], |
||
| 173 | Html::rawElement( 'td', [ 'style' => 'vertical-align: top;' ], $name ) . |
||
| 174 | Html::rawElement( 'td', [], $value ) |
||
| 175 | ); |
||
| 176 | } |
||
| 177 | |||
| 178 | /** |
||
| 179 | * Adds a table to the content that will be added to the output. |
||
| 180 | * |
||
| 181 | * @param string $content The content that will be added to the output |
||
| 182 | * @param string $table The table |
||
| 183 | * @return string The content with the table added |
||
| 184 | */ |
||
| 185 | protected function addTable( $content, $table ) { |
||
| 186 | return $content . Html::rawElement( 'table', [ 'class' => 'wikitable mw-page-info' ], |
||
| 187 | $table ); |
||
| 188 | } |
||
| 189 | |||
| 190 | /** |
||
| 191 | * Returns page information in an easily-manipulated format. Array keys are used so extensions |
||
| 192 | * may add additional information in arbitrary positions. Array values are arrays with one |
||
| 193 | * element to be rendered as a header, arrays with two elements to be rendered as a table row. |
||
| 194 | * |
||
| 195 | * @return array |
||
| 196 | */ |
||
| 197 | protected function pageInfo() { |
||
| 198 | global $wgContLang; |
||
| 199 | |||
| 200 | $user = $this->getUser(); |
||
| 201 | $lang = $this->getLanguage(); |
||
| 202 | $title = $this->getTitle(); |
||
| 203 | $id = $title->getArticleID(); |
||
| 204 | $config = $this->context->getConfig(); |
||
| 205 | $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); |
||
| 206 | |||
| 207 | $pageCounts = $this->pageCounts( $this->page ); |
||
| 208 | |||
| 209 | $pageProperties = []; |
||
| 210 | $props = PageProps::getInstance()->getAllProperties( $title ); |
||
| 211 | if ( isset( $props[$id] ) ) { |
||
| 212 | $pageProperties = $props[$id]; |
||
| 213 | } |
||
| 214 | |||
| 215 | // Basic information |
||
| 216 | $pageInfo = []; |
||
| 217 | $pageInfo['header-basic'] = []; |
||
| 218 | |||
| 219 | // Display title |
||
| 220 | $displayTitle = $title->getPrefixedText(); |
||
| 221 | if ( isset( $pageProperties['displaytitle'] ) ) { |
||
| 222 | $displayTitle = $pageProperties['displaytitle']; |
||
| 223 | } |
||
| 224 | |||
| 225 | $pageInfo['header-basic'][] = [ |
||
| 226 | $this->msg( 'pageinfo-display-title' ), $displayTitle |
||
| 227 | ]; |
||
| 228 | |||
| 229 | // Is it a redirect? If so, where to? |
||
| 230 | if ( $title->isRedirect() ) { |
||
| 231 | $pageInfo['header-basic'][] = [ |
||
| 232 | $this->msg( 'pageinfo-redirectsto' ), |
||
| 233 | Linker::link( $this->page->getRedirectTarget() ) . |
||
|
0 ignored issues
–
show
The method
getRedirectTarget does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 234 | $this->msg( 'word-separator' )->escaped() . |
||
| 235 | $this->msg( 'parentheses' )->rawParams( Linker::link( |
||
| 236 | $this->page->getRedirectTarget(), |
||
| 237 | $this->msg( 'pageinfo-redirectsto-info' )->escaped(), |
||
| 238 | [], |
||
| 239 | [ 'action' => 'info' ] |
||
| 240 | ) )->escaped() |
||
| 241 | ]; |
||
| 242 | } |
||
| 243 | |||
| 244 | // Default sort key |
||
| 245 | $sortKey = $title->getCategorySortkey(); |
||
| 246 | if ( isset( $pageProperties['defaultsort'] ) ) { |
||
| 247 | $sortKey = $pageProperties['defaultsort']; |
||
| 248 | } |
||
| 249 | |||
| 250 | $sortKey = htmlspecialchars( $sortKey ); |
||
| 251 | $pageInfo['header-basic'][] = [ $this->msg( 'pageinfo-default-sort' ), $sortKey ]; |
||
| 252 | |||
| 253 | // Page length (in bytes) |
||
| 254 | $pageInfo['header-basic'][] = [ |
||
| 255 | $this->msg( 'pageinfo-length' ), $lang->formatNum( $title->getLength() ) |
||
| 256 | ]; |
||
| 257 | |||
| 258 | // Page ID (number not localised, as it's a database ID) |
||
| 259 | $pageInfo['header-basic'][] = [ $this->msg( 'pageinfo-article-id' ), $id ]; |
||
| 260 | |||
| 261 | // Language in which the page content is (supposed to be) written |
||
| 262 | $pageLang = $title->getPageLanguage()->getCode(); |
||
| 263 | |||
| 264 | if ( $config->get( 'PageLanguageUseDB' ) |
||
| 265 | && $this->getTitle()->userCan( 'pagelang', $this->getUser() ) |
||
| 266 | ) { |
||
| 267 | // Link to Special:PageLanguage with pre-filled page title if user has permissions |
||
| 268 | $titleObj = SpecialPage::getTitleFor( 'PageLanguage', $title->getPrefixedText() ); |
||
| 269 | $langDisp = Linker::link( |
||
| 270 | $titleObj, |
||
| 271 | $this->msg( 'pageinfo-language' )->escaped() |
||
| 272 | ); |
||
| 273 | } else { |
||
| 274 | // Display just the message |
||
| 275 | $langDisp = $this->msg( 'pageinfo-language' )->escaped(); |
||
| 276 | } |
||
| 277 | |||
| 278 | $pageInfo['header-basic'][] = [ $langDisp, |
||
| 279 | Language::fetchLanguageName( $pageLang, $lang->getCode() ) |
||
| 280 | . ' ' . $this->msg( 'parentheses', $pageLang )->escaped() ]; |
||
| 281 | |||
| 282 | // Content model of the page |
||
| 283 | $modelHtml = htmlspecialchars( ContentHandler::getLocalizedName( $title->getContentModel() ) ); |
||
| 284 | // If the user can change it, add a link to Special:ChangeContentModel |
||
| 285 | if ( $title->quickUserCan( 'editcontentmodel' ) ) { |
||
| 286 | $modelHtml .= ' ' . $this->msg( 'parentheses' )->rawParams( $linkRenderer->makeLink( |
||
| 287 | SpecialPage::getTitleValueFor( 'ChangeContentModel', $title->getPrefixedText() ), |
||
| 288 | $this->msg( 'pageinfo-content-model-change' )->text() |
||
| 289 | ) )->escaped(); |
||
| 290 | } |
||
| 291 | |||
| 292 | $pageInfo['header-basic'][] = [ |
||
| 293 | $this->msg( 'pageinfo-content-model' ), |
||
| 294 | $modelHtml |
||
| 295 | ]; |
||
| 296 | |||
| 297 | if ( $title->inNamespace( NS_USER ) ) { |
||
| 298 | $pageUser = User::newFromName( $title->getRootText() ); |
||
| 299 | if ( $pageUser && $pageUser->getId() && !$pageUser->isHidden() ) { |
||
| 300 | $pageInfo['header-basic'][] = [ |
||
| 301 | $this->msg( 'pageinfo-user-id' ), |
||
| 302 | $pageUser->getId() |
||
| 303 | ]; |
||
| 304 | } |
||
| 305 | } |
||
| 306 | |||
| 307 | // Search engine status |
||
| 308 | $pOutput = new ParserOutput(); |
||
| 309 | if ( isset( $pageProperties['noindex'] ) ) { |
||
| 310 | $pOutput->setIndexPolicy( 'noindex' ); |
||
| 311 | } |
||
| 312 | if ( isset( $pageProperties['index'] ) ) { |
||
| 313 | $pOutput->setIndexPolicy( 'index' ); |
||
| 314 | } |
||
| 315 | |||
| 316 | // Use robot policy logic |
||
| 317 | $policy = $this->page->getRobotPolicy( 'view', $pOutput ); |
||
|
0 ignored issues
–
show
The method
getRobotPolicy does only exist in Article and CategoryPage and ImagePage, but not in Page and WikiPage.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 318 | $pageInfo['header-basic'][] = [ |
||
| 319 | // Messages: pageinfo-robot-index, pageinfo-robot-noindex |
||
| 320 | $this->msg( 'pageinfo-robot-policy' ), |
||
| 321 | $this->msg( "pageinfo-robot-${policy['index']}" ) |
||
| 322 | ]; |
||
| 323 | |||
| 324 | $unwatchedPageThreshold = $config->get( 'UnwatchedPageThreshold' ); |
||
| 325 | if ( |
||
| 326 | $user->isAllowed( 'unwatchedpages' ) || |
||
| 327 | ( $unwatchedPageThreshold !== false && |
||
| 328 | $pageCounts['watchers'] >= $unwatchedPageThreshold ) |
||
| 329 | ) { |
||
| 330 | // Number of page watchers |
||
| 331 | $pageInfo['header-basic'][] = [ |
||
| 332 | $this->msg( 'pageinfo-watchers' ), |
||
| 333 | $lang->formatNum( $pageCounts['watchers'] ) |
||
| 334 | ]; |
||
| 335 | if ( |
||
| 336 | $config->get( 'ShowUpdatedMarker' ) && |
||
| 337 | isset( $pageCounts['visitingWatchers'] ) |
||
| 338 | ) { |
||
| 339 | $minToDisclose = $config->get( 'UnwatchedPageSecret' ); |
||
| 340 | if ( $pageCounts['visitingWatchers'] > $minToDisclose || |
||
| 341 | $user->isAllowed( 'unwatchedpages' ) ) { |
||
| 342 | $pageInfo['header-basic'][] = [ |
||
| 343 | $this->msg( 'pageinfo-visiting-watchers' ), |
||
| 344 | $lang->formatNum( $pageCounts['visitingWatchers'] ) |
||
| 345 | ]; |
||
| 346 | } else { |
||
| 347 | $pageInfo['header-basic'][] = [ |
||
| 348 | $this->msg( 'pageinfo-visiting-watchers' ), |
||
| 349 | $this->msg( 'pageinfo-few-visiting-watchers' ) |
||
| 350 | ]; |
||
| 351 | } |
||
| 352 | } |
||
| 353 | } elseif ( $unwatchedPageThreshold !== false ) { |
||
| 354 | $pageInfo['header-basic'][] = [ |
||
| 355 | $this->msg( 'pageinfo-watchers' ), |
||
| 356 | $this->msg( 'pageinfo-few-watchers' )->numParams( $unwatchedPageThreshold ) |
||
| 357 | ]; |
||
| 358 | } |
||
| 359 | |||
| 360 | // Redirects to this page |
||
| 361 | $whatLinksHere = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedText() ); |
||
| 362 | $pageInfo['header-basic'][] = [ |
||
| 363 | Linker::link( |
||
| 364 | $whatLinksHere, |
||
| 365 | $this->msg( 'pageinfo-redirects-name' )->escaped(), |
||
| 366 | [], |
||
| 367 | [ |
||
| 368 | 'hidelinks' => 1, |
||
| 369 | 'hidetrans' => 1, |
||
| 370 | 'hideimages' => $title->getNamespace() == NS_FILE |
||
| 371 | ] |
||
| 372 | ), |
||
| 373 | $this->msg( 'pageinfo-redirects-value' ) |
||
| 374 | ->numParams( count( $title->getRedirectsHere() ) ) |
||
| 375 | ]; |
||
| 376 | |||
| 377 | // Is it counted as a content page? |
||
| 378 | if ( $this->page->isCountable() ) { |
||
|
0 ignored issues
–
show
The method
isCountable does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 379 | $pageInfo['header-basic'][] = [ |
||
| 380 | $this->msg( 'pageinfo-contentpage' ), |
||
| 381 | $this->msg( 'pageinfo-contentpage-yes' ) |
||
| 382 | ]; |
||
| 383 | } |
||
| 384 | |||
| 385 | // Subpages of this page, if subpages are enabled for the current NS |
||
| 386 | if ( MWNamespace::hasSubpages( $title->getNamespace() ) ) { |
||
| 387 | $prefixIndex = SpecialPage::getTitleFor( |
||
| 388 | 'Prefixindex', $title->getPrefixedText() . '/' ); |
||
| 389 | $pageInfo['header-basic'][] = [ |
||
| 390 | Linker::link( $prefixIndex, $this->msg( 'pageinfo-subpages-name' )->escaped() ), |
||
| 391 | $this->msg( 'pageinfo-subpages-value' ) |
||
| 392 | ->numParams( |
||
| 393 | $pageCounts['subpages']['total'], |
||
| 394 | $pageCounts['subpages']['redirects'], |
||
| 395 | $pageCounts['subpages']['nonredirects'] ) |
||
| 396 | ]; |
||
| 397 | } |
||
| 398 | |||
| 399 | if ( $title->inNamespace( NS_CATEGORY ) ) { |
||
| 400 | $category = Category::newFromTitle( $title ); |
||
| 401 | |||
| 402 | // $allCount is the total number of cat members, |
||
| 403 | // not the count of how many members are normal pages. |
||
| 404 | $allCount = (int)$category->getPageCount(); |
||
| 405 | $subcatCount = (int)$category->getSubcatCount(); |
||
| 406 | $fileCount = (int)$category->getFileCount(); |
||
| 407 | $pagesCount = $allCount - $subcatCount - $fileCount; |
||
| 408 | |||
| 409 | $pageInfo['category-info'] = [ |
||
| 410 | [ |
||
| 411 | $this->msg( 'pageinfo-category-total' ), |
||
| 412 | $lang->formatNum( $allCount ) |
||
| 413 | ], |
||
| 414 | [ |
||
| 415 | $this->msg( 'pageinfo-category-pages' ), |
||
| 416 | $lang->formatNum( $pagesCount ) |
||
| 417 | ], |
||
| 418 | [ |
||
| 419 | $this->msg( 'pageinfo-category-subcats' ), |
||
| 420 | $lang->formatNum( $subcatCount ) |
||
| 421 | ], |
||
| 422 | [ |
||
| 423 | $this->msg( 'pageinfo-category-files' ), |
||
| 424 | $lang->formatNum( $fileCount ) |
||
| 425 | ] |
||
| 426 | ]; |
||
| 427 | } |
||
| 428 | |||
| 429 | // Page protection |
||
| 430 | $pageInfo['header-restrictions'] = []; |
||
| 431 | |||
| 432 | // Is this page affected by the cascading protection of something which includes it? |
||
| 433 | if ( $title->isCascadeProtected() ) { |
||
| 434 | $cascadingFrom = ''; |
||
| 435 | $sources = $title->getCascadeProtectionSources()[0]; |
||
| 436 | |||
| 437 | foreach ( $sources as $sourceTitle ) { |
||
|
0 ignored issues
–
show
The expression
$sources of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
Loading history...
|
|||
| 438 | $cascadingFrom .= Html::rawElement( |
||
| 439 | 'li', [], Linker::linkKnown( $sourceTitle ) ); |
||
| 440 | } |
||
| 441 | |||
| 442 | $cascadingFrom = Html::rawElement( 'ul', [], $cascadingFrom ); |
||
| 443 | $pageInfo['header-restrictions'][] = [ |
||
| 444 | $this->msg( 'pageinfo-protect-cascading-from' ), |
||
| 445 | $cascadingFrom |
||
| 446 | ]; |
||
| 447 | } |
||
| 448 | |||
| 449 | // Is out protection set to cascade to other pages? |
||
| 450 | if ( $title->areRestrictionsCascading() ) { |
||
| 451 | $pageInfo['header-restrictions'][] = [ |
||
| 452 | $this->msg( 'pageinfo-protect-cascading' ), |
||
| 453 | $this->msg( 'pageinfo-protect-cascading-yes' ) |
||
| 454 | ]; |
||
| 455 | } |
||
| 456 | |||
| 457 | // Page protection |
||
| 458 | foreach ( $title->getRestrictionTypes() as $restrictionType ) { |
||
| 459 | $protectionLevel = implode( ', ', $title->getRestrictions( $restrictionType ) ); |
||
| 460 | |||
| 461 | if ( $protectionLevel == '' ) { |
||
| 462 | // Allow all users |
||
| 463 | $message = $this->msg( 'protect-default' )->escaped(); |
||
| 464 | } else { |
||
| 465 | // Administrators only |
||
| 466 | // Messages: protect-level-autoconfirmed, protect-level-sysop |
||
| 467 | $message = $this->msg( "protect-level-$protectionLevel" ); |
||
| 468 | if ( $message->isDisabled() ) { |
||
| 469 | // Require "$1" permission |
||
| 470 | $message = $this->msg( "protect-fallback", $protectionLevel )->parse(); |
||
| 471 | } else { |
||
| 472 | $message = $message->escaped(); |
||
| 473 | } |
||
| 474 | } |
||
| 475 | $expiry = $title->getRestrictionExpiry( $restrictionType ); |
||
| 476 | $formattedexpiry = $this->msg( 'parentheses', |
||
| 477 | $this->getLanguage()->formatExpiry( $expiry ) )->escaped(); |
||
|
0 ignored issues
–
show
It seems like
$expiry defined by $title->getRestrictionExpiry($restrictionType) on line 475 can also be of type boolean; however, Language::formatExpiry() 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...
|
|||
| 478 | $message .= $this->msg( 'word-separator' )->escaped() . $formattedexpiry; |
||
| 479 | |||
| 480 | // Messages: restriction-edit, restriction-move, restriction-create, |
||
| 481 | // restriction-upload |
||
| 482 | $pageInfo['header-restrictions'][] = [ |
||
| 483 | $this->msg( "restriction-$restrictionType" ), $message |
||
| 484 | ]; |
||
| 485 | } |
||
| 486 | |||
| 487 | if ( !$this->page->exists() ) { |
||
|
0 ignored issues
–
show
The method
exists does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 488 | return $pageInfo; |
||
| 489 | } |
||
| 490 | |||
| 491 | // Edit history |
||
| 492 | $pageInfo['header-edits'] = []; |
||
| 493 | |||
| 494 | $firstRev = $this->page->getOldestRevision(); |
||
|
0 ignored issues
–
show
The method
getOldestRevision does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 495 | $lastRev = $this->page->getRevision(); |
||
|
0 ignored issues
–
show
The method
getRevision does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 496 | $batch = new LinkBatch; |
||
| 497 | |||
| 498 | View Code Duplication | if ( $firstRev ) { |
|
| 499 | $firstRevUser = $firstRev->getUserText( Revision::FOR_THIS_USER ); |
||
| 500 | if ( $firstRevUser !== '' ) { |
||
| 501 | $firstRevUserTitle = Title::makeTitle( NS_USER, $firstRevUser ); |
||
| 502 | $batch->addObj( $firstRevUserTitle ); |
||
| 503 | $batch->addObj( $firstRevUserTitle->getTalkPage() ); |
||
| 504 | } |
||
| 505 | } |
||
| 506 | |||
| 507 | View Code Duplication | if ( $lastRev ) { |
|
| 508 | $lastRevUser = $lastRev->getUserText( Revision::FOR_THIS_USER ); |
||
| 509 | if ( $lastRevUser !== '' ) { |
||
| 510 | $lastRevUserTitle = Title::makeTitle( NS_USER, $lastRevUser ); |
||
| 511 | $batch->addObj( $lastRevUserTitle ); |
||
| 512 | $batch->addObj( $lastRevUserTitle->getTalkPage() ); |
||
| 513 | } |
||
| 514 | } |
||
| 515 | |||
| 516 | $batch->execute(); |
||
| 517 | |||
| 518 | if ( $firstRev ) { |
||
| 519 | // Page creator |
||
| 520 | $pageInfo['header-edits'][] = [ |
||
| 521 | $this->msg( 'pageinfo-firstuser' ), |
||
| 522 | Linker::revUserTools( $firstRev ) |
||
| 523 | ]; |
||
| 524 | |||
| 525 | // Date of page creation |
||
| 526 | $pageInfo['header-edits'][] = [ |
||
| 527 | $this->msg( 'pageinfo-firsttime' ), |
||
| 528 | Linker::linkKnown( |
||
| 529 | $title, |
||
| 530 | htmlspecialchars( $lang->userTimeAndDate( $firstRev->getTimestamp(), $user ) ), |
||
| 531 | [], |
||
| 532 | [ 'oldid' => $firstRev->getId() ] |
||
| 533 | ) |
||
| 534 | ]; |
||
| 535 | } |
||
| 536 | |||
| 537 | if ( $lastRev ) { |
||
| 538 | // Latest editor |
||
| 539 | $pageInfo['header-edits'][] = [ |
||
| 540 | $this->msg( 'pageinfo-lastuser' ), |
||
| 541 | Linker::revUserTools( $lastRev ) |
||
| 542 | ]; |
||
| 543 | |||
| 544 | // Date of latest edit |
||
| 545 | $pageInfo['header-edits'][] = [ |
||
| 546 | $this->msg( 'pageinfo-lasttime' ), |
||
| 547 | Linker::linkKnown( |
||
| 548 | $title, |
||
| 549 | htmlspecialchars( |
||
| 550 | $lang->userTimeAndDate( $this->page->getTimestamp(), $user ) |
||
|
0 ignored issues
–
show
The method
getTimestamp does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 551 | ), |
||
| 552 | [], |
||
| 553 | [ 'oldid' => $this->page->getLatest() ] |
||
|
0 ignored issues
–
show
The method
getLatest does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 554 | ) |
||
| 555 | ]; |
||
| 556 | } |
||
| 557 | |||
| 558 | // Total number of edits |
||
| 559 | $pageInfo['header-edits'][] = [ |
||
| 560 | $this->msg( 'pageinfo-edits' ), $lang->formatNum( $pageCounts['edits'] ) |
||
| 561 | ]; |
||
| 562 | |||
| 563 | // Total number of distinct authors |
||
| 564 | if ( $pageCounts['authors'] > 0 ) { |
||
| 565 | $pageInfo['header-edits'][] = [ |
||
| 566 | $this->msg( 'pageinfo-authors' ), $lang->formatNum( $pageCounts['authors'] ) |
||
| 567 | ]; |
||
| 568 | } |
||
| 569 | |||
| 570 | // Recent number of edits (within past 30 days) |
||
| 571 | $pageInfo['header-edits'][] = [ |
||
| 572 | $this->msg( 'pageinfo-recent-edits', |
||
| 573 | $lang->formatDuration( $config->get( 'RCMaxAge' ) ) ), |
||
| 574 | $lang->formatNum( $pageCounts['recent_edits'] ) |
||
| 575 | ]; |
||
| 576 | |||
| 577 | // Recent number of distinct authors |
||
| 578 | $pageInfo['header-edits'][] = [ |
||
| 579 | $this->msg( 'pageinfo-recent-authors' ), |
||
| 580 | $lang->formatNum( $pageCounts['recent_authors'] ) |
||
| 581 | ]; |
||
| 582 | |||
| 583 | // Array of MagicWord objects |
||
| 584 | $magicWords = MagicWord::getDoubleUnderscoreArray(); |
||
| 585 | |||
| 586 | // Array of magic word IDs |
||
| 587 | $wordIDs = $magicWords->names; |
||
| 588 | |||
| 589 | // Array of IDs => localized magic words |
||
| 590 | $localizedWords = $wgContLang->getMagicWords(); |
||
| 591 | |||
| 592 | $listItems = []; |
||
| 593 | foreach ( $pageProperties as $property => $value ) { |
||
| 594 | if ( in_array( $property, $wordIDs ) ) { |
||
| 595 | $listItems[] = Html::element( 'li', [], $localizedWords[$property][1] ); |
||
| 596 | } |
||
| 597 | } |
||
| 598 | |||
| 599 | $localizedList = Html::rawElement( 'ul', [], implode( '', $listItems ) ); |
||
| 600 | $hiddenCategories = $this->page->getHiddenCategories(); |
||
|
0 ignored issues
–
show
The method
getHiddenCategories does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 601 | |||
| 602 | if ( |
||
| 603 | count( $listItems ) > 0 || |
||
| 604 | count( $hiddenCategories ) > 0 || |
||
| 605 | $pageCounts['transclusion']['from'] > 0 || |
||
| 606 | $pageCounts['transclusion']['to'] > 0 |
||
| 607 | ) { |
||
| 608 | $options = [ 'LIMIT' => $config->get( 'PageInfoTransclusionLimit' ) ]; |
||
| 609 | $transcludedTemplates = $title->getTemplateLinksFrom( $options ); |
||
| 610 | if ( $config->get( 'MiserMode' ) ) { |
||
| 611 | $transcludedTargets = []; |
||
| 612 | } else { |
||
| 613 | $transcludedTargets = $title->getTemplateLinksTo( $options ); |
||
| 614 | } |
||
| 615 | |||
| 616 | // Page properties |
||
| 617 | $pageInfo['header-properties'] = []; |
||
| 618 | |||
| 619 | // Magic words |
||
| 620 | if ( count( $listItems ) > 0 ) { |
||
| 621 | $pageInfo['header-properties'][] = [ |
||
| 622 | $this->msg( 'pageinfo-magic-words' )->numParams( count( $listItems ) ), |
||
| 623 | $localizedList |
||
| 624 | ]; |
||
| 625 | } |
||
| 626 | |||
| 627 | // Hidden categories |
||
| 628 | if ( count( $hiddenCategories ) > 0 ) { |
||
| 629 | $pageInfo['header-properties'][] = [ |
||
| 630 | $this->msg( 'pageinfo-hidden-categories' ) |
||
| 631 | ->numParams( count( $hiddenCategories ) ), |
||
| 632 | Linker::formatHiddenCategories( $hiddenCategories ) |
||
| 633 | ]; |
||
| 634 | } |
||
| 635 | |||
| 636 | // Transcluded templates |
||
| 637 | if ( $pageCounts['transclusion']['from'] > 0 ) { |
||
| 638 | if ( $pageCounts['transclusion']['from'] > count( $transcludedTemplates ) ) { |
||
| 639 | $more = $this->msg( 'morenotlisted' )->escaped(); |
||
| 640 | } else { |
||
| 641 | $more = null; |
||
| 642 | } |
||
| 643 | |||
| 644 | $templateListFormatter = new TemplatesOnThisPageFormatter( |
||
| 645 | $this->getContext(), |
||
| 646 | $linkRenderer |
||
| 647 | ); |
||
| 648 | |||
| 649 | $pageInfo['header-properties'][] = [ |
||
| 650 | $this->msg( 'pageinfo-templates' ) |
||
| 651 | ->numParams( $pageCounts['transclusion']['from'] ), |
||
| 652 | $templateListFormatter->format( $transcludedTemplates, false, $more ) |
||
| 653 | ]; |
||
| 654 | } |
||
| 655 | |||
| 656 | if ( !$config->get( 'MiserMode' ) && $pageCounts['transclusion']['to'] > 0 ) { |
||
| 657 | if ( $pageCounts['transclusion']['to'] > count( $transcludedTargets ) ) { |
||
| 658 | $more = Linker::link( |
||
| 659 | $whatLinksHere, |
||
| 660 | $this->msg( 'moredotdotdot' )->escaped(), |
||
| 661 | [], |
||
| 662 | [ 'hidelinks' => 1, 'hideredirs' => 1 ] |
||
| 663 | ); |
||
| 664 | } else { |
||
| 665 | $more = null; |
||
| 666 | } |
||
| 667 | |||
| 668 | $templateListFormatter = new TemplatesOnThisPageFormatter( |
||
| 669 | $this->getContext(), |
||
| 670 | $linkRenderer |
||
| 671 | ); |
||
| 672 | |||
| 673 | $pageInfo['header-properties'][] = [ |
||
| 674 | $this->msg( 'pageinfo-transclusions' ) |
||
| 675 | ->numParams( $pageCounts['transclusion']['to'] ), |
||
| 676 | $templateListFormatter->format( $transcludedTargets, false, $more ) |
||
| 677 | ]; |
||
| 678 | } |
||
| 679 | } |
||
| 680 | |||
| 681 | return $pageInfo; |
||
| 682 | } |
||
| 683 | |||
| 684 | /** |
||
| 685 | * Returns page counts that would be too "expensive" to retrieve by normal means. |
||
| 686 | * |
||
| 687 | * @param WikiPage|Article|Page $page |
||
| 688 | * @return array |
||
| 689 | */ |
||
| 690 | protected function pageCounts( Page $page ) { |
||
| 691 | $fname = __METHOD__; |
||
| 692 | $config = $this->context->getConfig(); |
||
| 693 | |||
| 694 | return ObjectCache::getMainWANInstance()->getWithSetCallback( |
||
| 695 | self::getCacheKey( $page->getTitle(), $page->getLatest() ), |
||
| 696 | WANObjectCache::TTL_WEEK, |
||
| 697 | function ( $oldValue, &$ttl, &$setOpts ) use ( $page, $config, $fname ) { |
||
| 698 | $title = $page->getTitle(); |
||
| 699 | $id = $title->getArticleID(); |
||
| 700 | |||
| 701 | $dbr = wfGetDB( DB_REPLICA ); |
||
| 702 | $dbrWatchlist = wfGetDB( DB_REPLICA, 'watchlist' ); |
||
| 703 | $setOpts += Database::getCacheSetOptions( $dbr, $dbrWatchlist ); |
||
| 704 | |||
| 705 | $watchedItemStore = MediaWikiServices::getInstance()->getWatchedItemStore(); |
||
| 706 | |||
| 707 | $result = []; |
||
| 708 | $result['watchers'] = $watchedItemStore->countWatchers( $title ); |
||
| 709 | |||
| 710 | if ( $config->get( 'ShowUpdatedMarker' ) ) { |
||
| 711 | $updated = wfTimestamp( TS_UNIX, $page->getTimestamp() ); |
||
| 712 | $result['visitingWatchers'] = $watchedItemStore->countVisitingWatchers( |
||
| 713 | $title, |
||
| 714 | $updated - $config->get( 'WatchersMaxAge' ) |
||
| 715 | ); |
||
| 716 | } |
||
| 717 | |||
| 718 | // Total number of edits |
||
| 719 | $edits = (int)$dbr->selectField( |
||
| 720 | 'revision', |
||
| 721 | 'COUNT(*)', |
||
| 722 | [ 'rev_page' => $id ], |
||
| 723 | $fname |
||
| 724 | ); |
||
| 725 | $result['edits'] = $edits; |
||
| 726 | |||
| 727 | // Total number of distinct authors |
||
| 728 | if ( $config->get( 'MiserMode' ) ) { |
||
| 729 | $result['authors'] = 0; |
||
| 730 | } else { |
||
| 731 | $result['authors'] = (int)$dbr->selectField( |
||
| 732 | 'revision', |
||
| 733 | 'COUNT(DISTINCT rev_user_text)', |
||
| 734 | [ 'rev_page' => $id ], |
||
| 735 | $fname |
||
| 736 | ); |
||
| 737 | } |
||
| 738 | |||
| 739 | // "Recent" threshold defined by RCMaxAge setting |
||
| 740 | $threshold = $dbr->timestamp( time() - $config->get( 'RCMaxAge' ) ); |
||
| 741 | |||
| 742 | // Recent number of edits |
||
| 743 | $edits = (int)$dbr->selectField( |
||
| 744 | 'revision', |
||
| 745 | 'COUNT(rev_page)', |
||
| 746 | [ |
||
| 747 | 'rev_page' => $id, |
||
| 748 | "rev_timestamp >= " . $dbr->addQuotes( $threshold ) |
||
| 749 | ], |
||
| 750 | $fname |
||
| 751 | ); |
||
| 752 | $result['recent_edits'] = $edits; |
||
| 753 | |||
| 754 | // Recent number of distinct authors |
||
| 755 | $result['recent_authors'] = (int)$dbr->selectField( |
||
| 756 | 'revision', |
||
| 757 | 'COUNT(DISTINCT rev_user_text)', |
||
| 758 | [ |
||
| 759 | 'rev_page' => $id, |
||
| 760 | "rev_timestamp >= " . $dbr->addQuotes( $threshold ) |
||
| 761 | ], |
||
| 762 | $fname |
||
| 763 | ); |
||
| 764 | |||
| 765 | // Subpages (if enabled) |
||
| 766 | if ( MWNamespace::hasSubpages( $title->getNamespace() ) ) { |
||
| 767 | $conds = [ 'page_namespace' => $title->getNamespace() ]; |
||
| 768 | $conds[] = 'page_title ' . |
||
| 769 | $dbr->buildLike( $title->getDBkey() . '/', $dbr->anyString() ); |
||
| 770 | |||
| 771 | // Subpages of this page (redirects) |
||
| 772 | $conds['page_is_redirect'] = 1; |
||
| 773 | $result['subpages']['redirects'] = (int)$dbr->selectField( |
||
| 774 | 'page', |
||
| 775 | 'COUNT(page_id)', |
||
| 776 | $conds, |
||
| 777 | $fname |
||
| 778 | ); |
||
| 779 | |||
| 780 | // Subpages of this page (non-redirects) |
||
| 781 | $conds['page_is_redirect'] = 0; |
||
| 782 | $result['subpages']['nonredirects'] = (int)$dbr->selectField( |
||
| 783 | 'page', |
||
| 784 | 'COUNT(page_id)', |
||
| 785 | $conds, |
||
| 786 | $fname |
||
| 787 | ); |
||
| 788 | |||
| 789 | // Subpages of this page (total) |
||
| 790 | $result['subpages']['total'] = $result['subpages']['redirects'] |
||
| 791 | + $result['subpages']['nonredirects']; |
||
| 792 | } |
||
| 793 | |||
| 794 | // Counts for the number of transclusion links (to/from) |
||
| 795 | if ( $config->get( 'MiserMode' ) ) { |
||
| 796 | $result['transclusion']['to'] = 0; |
||
| 797 | } else { |
||
| 798 | $result['transclusion']['to'] = (int)$dbr->selectField( |
||
| 799 | 'templatelinks', |
||
| 800 | 'COUNT(tl_from)', |
||
| 801 | [ |
||
| 802 | 'tl_namespace' => $title->getNamespace(), |
||
| 803 | 'tl_title' => $title->getDBkey() |
||
| 804 | ], |
||
| 805 | $fname |
||
| 806 | ); |
||
| 807 | } |
||
| 808 | |||
| 809 | $result['transclusion']['from'] = (int)$dbr->selectField( |
||
| 810 | 'templatelinks', |
||
| 811 | 'COUNT(*)', |
||
| 812 | [ 'tl_from' => $title->getArticleID() ], |
||
| 813 | $fname |
||
| 814 | ); |
||
| 815 | |||
| 816 | return $result; |
||
| 817 | } |
||
| 818 | ); |
||
| 819 | } |
||
| 820 | |||
| 821 | /** |
||
| 822 | * Returns the name that goes in the "<h1>" page title. |
||
| 823 | * |
||
| 824 | * @return string |
||
| 825 | */ |
||
| 826 | protected function getPageTitle() { |
||
| 827 | return $this->msg( 'pageinfo-title', $this->getTitle()->getPrefixedText() )->text(); |
||
| 828 | } |
||
| 829 | |||
| 830 | /** |
||
| 831 | * Get a list of contributors of $article |
||
| 832 | * @return string Html |
||
| 833 | */ |
||
| 834 | protected function getContributors() { |
||
| 835 | $contributors = $this->page->getContributors(); |
||
|
0 ignored issues
–
show
The method
getContributors does only exist in Article and CategoryPage... ImagePage and WikiPage, but not in Page.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 836 | $real_names = []; |
||
| 837 | $user_names = []; |
||
| 838 | $anon_ips = []; |
||
| 839 | |||
| 840 | # Sift for real versus user names |
||
| 841 | /** @var $user User */ |
||
| 842 | foreach ( $contributors as $user ) { |
||
| 843 | $page = $user->isAnon() |
||
| 844 | ? SpecialPage::getTitleFor( 'Contributions', $user->getName() ) |
||
| 845 | : $user->getUserPage(); |
||
| 846 | |||
| 847 | $hiddenPrefs = $this->context->getConfig()->get( 'HiddenPrefs' ); |
||
| 848 | if ( $user->getId() == 0 ) { |
||
| 849 | $anon_ips[] = Linker::link( $page, htmlspecialchars( $user->getName() ) ); |
||
| 850 | } elseif ( !in_array( 'realname', $hiddenPrefs ) && $user->getRealName() ) { |
||
| 851 | $real_names[] = Linker::link( $page, htmlspecialchars( $user->getRealName() ) ); |
||
| 852 | } else { |
||
| 853 | $user_names[] = Linker::link( $page, htmlspecialchars( $user->getName() ) ); |
||
| 854 | } |
||
| 855 | } |
||
| 856 | |||
| 857 | $lang = $this->getLanguage(); |
||
| 858 | |||
| 859 | $real = $lang->listToText( $real_names ); |
||
| 860 | |||
| 861 | # "ThisSite user(s) A, B and C" |
||
| 862 | View Code Duplication | if ( count( $user_names ) ) { |
|
| 863 | $user = $this->msg( 'siteusers' ) |
||
| 864 | ->rawParams( $lang->listToText( $user_names ) ) |
||
| 865 | ->params( count( $user_names ) )->escaped(); |
||
| 866 | } else { |
||
| 867 | $user = false; |
||
| 868 | } |
||
| 869 | |||
| 870 | View Code Duplication | if ( count( $anon_ips ) ) { |
|
| 871 | $anon = $this->msg( 'anonusers' ) |
||
| 872 | ->rawParams( $lang->listToText( $anon_ips ) ) |
||
| 873 | ->params( count( $anon_ips ) )->escaped(); |
||
| 874 | } else { |
||
| 875 | $anon = false; |
||
| 876 | } |
||
| 877 | |||
| 878 | # This is the big list, all mooshed together. We sift for blank strings |
||
| 879 | $fulllist = []; |
||
| 880 | View Code Duplication | foreach ( [ $real, $user, $anon ] as $s ) { |
|
| 881 | if ( $s !== '' ) { |
||
| 882 | array_push( $fulllist, $s ); |
||
| 883 | } |
||
| 884 | } |
||
| 885 | |||
| 886 | $count = count( $fulllist ); |
||
| 887 | |||
| 888 | # "Based on work by ..." |
||
| 889 | return $count |
||
| 890 | ? $this->msg( 'othercontribs' )->rawParams( |
||
| 891 | $lang->listToText( $fulllist ) )->params( $count )->escaped() |
||
| 892 | : ''; |
||
| 893 | } |
||
| 894 | |||
| 895 | /** |
||
| 896 | * Returns the description that goes below the "<h1>" tag. |
||
| 897 | * |
||
| 898 | * @return string |
||
| 899 | */ |
||
| 900 | protected function getDescription() { |
||
| 901 | return ''; |
||
| 902 | } |
||
| 903 | |||
| 904 | /** |
||
| 905 | * @param Title $title |
||
| 906 | * @param int $revId |
||
| 907 | * @return string |
||
| 908 | */ |
||
| 909 | protected static function getCacheKey( Title $title, $revId ) { |
||
| 910 | return wfMemcKey( 'infoaction', md5( $title->getPrefixedText() ), $revId, self::VERSION ); |
||
| 911 | } |
||
| 912 | } |
||
| 913 |
In PHP, under loose comparison (like
==, or!=, orswitchconditions), values of different types might be equal.For
integervalues, zero is a special case, in particular the following results might be unexpected: