mediawiki-extensions-Scribunto/includes/Engines/LuaCommon/SiteLibrary.php
Daimona Eaytoy 7e54c088ac Avoid DB access in databaseless tests
- Force a content model on the title used by LuaEngineTestBase, so that
  calls to getPageLanguage() won't end up hitting the DB
- Don't actually use SiteStats from SiteLibrary in unit tests. There
  seem to be no test actually using this data.

Bug: T345372
Change-Id: I35884f04b582678982fb5f64d9199bab41cd8bce
2023-08-31 19:12:55 +00:00

247 lines
8 KiB
PHP

<?php
namespace MediaWiki\Extension\Scribunto\Engines\LuaCommon;
use MediaWiki\Category\Category;
use MediaWiki\MediaWikiServices;
use MediaWiki\Title\Title;
use SiteStats;
use SpecialVersion;
class SiteLibrary extends LibraryBase {
/** @var string|null */
private static $namespacesCacheLang = null;
/** @var array[]|null */
private static $namespacesCache = null;
/** @var array[] */
private static $interwikiMapCache = [];
/** @var int[][] */
private $pagesInCategoryCache = [];
public function register() {
global $wgNamespaceAliases;
$lib = [
'getNsIndex' => [ $this, 'getNsIndex' ],
'pagesInCategory' => [ $this, 'pagesInCategory' ],
'pagesInNamespace' => [ $this, 'pagesInNamespace' ],
'usersInGroup' => [ $this, 'usersInGroup' ],
'interwikiMap' => [ $this, 'interwikiMap' ],
];
$parser = $this->getParser();
$services = MediaWikiServices::getInstance();
$contLang = $services->getContentLanguage();
$info = [
'siteName' => $GLOBALS['wgSitename'],
'server' => $GLOBALS['wgServer'],
'scriptPath' => $GLOBALS['wgScriptPath'],
'stylePath' => $GLOBALS['wgStylePath'],
'currentVersion' => SpecialVersion::getVersion(
'', $parser ? $parser->getTargetLanguage() : $contLang
),
];
if ( !self::$namespacesCache || self::$namespacesCacheLang !== $contLang->getCode() ) {
$namespaces = [];
$namespacesByName = [];
$namespaceInfo = $services->getNamespaceInfo();
foreach ( $contLang->getFormattedNamespaces() as $ns => $title ) {
$canonical = $namespaceInfo->getCanonicalName( $ns );
$namespaces[$ns] = [
'id' => $ns,
'name' => $title,
'canonicalName' => strtr( $canonical, '_', ' ' ),
'hasSubpages' => $namespaceInfo->hasSubpages( $ns ),
'hasGenderDistinction' => $namespaceInfo->hasGenderDistinction( $ns ),
'isCapitalized' => $namespaceInfo->isCapitalized( $ns ),
'isContent' => $namespaceInfo->isContent( $ns ),
'isIncludable' => !$namespaceInfo->isNonincludable( $ns ),
'isMovable' => $namespaceInfo->isMovable( $ns ),
'isSubject' => $namespaceInfo->isSubject( $ns ),
'isTalk' => $namespaceInfo->isTalk( $ns ),
'defaultContentModel' => $namespaceInfo->getNamespaceContentModel( $ns ),
'aliases' => [],
];
if ( $ns >= NS_MAIN ) {
$namespaces[$ns]['subject'] = $namespaceInfo->getSubject( $ns );
$namespaces[$ns]['talk'] = $namespaceInfo->getTalk( $ns );
$namespaces[$ns]['associated'] = $namespaceInfo->getAssociated( $ns );
} else {
$namespaces[$ns]['subject'] = $ns;
}
$namespacesByName[strtr( $title, ' ', '_' )] = $ns;
if ( $canonical ) {
$namespacesByName[$canonical] = $ns;
}
}
$aliases = array_merge( $wgNamespaceAliases, $contLang->getNamespaceAliases() );
foreach ( $aliases as $title => $ns ) {
if ( !isset( $namespacesByName[$title] ) && isset( $namespaces[$ns] ) ) {
$ct = count( $namespaces[$ns]['aliases'] );
$namespaces[$ns]['aliases'][$ct + 1] = $title;
$namespacesByName[$title] = $ns;
}
}
$namespaces[NS_MAIN]['displayName'] = wfMessage( 'blanknamespace' )->inContentLanguage()->text();
self::$namespacesCache = $namespaces;
self::$namespacesCacheLang = $contLang->getCode();
}
$info['namespaces'] = self::$namespacesCache;
if ( defined( 'MW_PHPUNIT_TEST' ) ) {
$info['stats'] = [
'pages' => 1,
'articles' => 1,
'files' => 0,
'edits' => 1,
'users' => 1,
'activeUsers' => 1,
'admins' => 1,
];
} else {
$info['stats'] = [
'pages' => (int)SiteStats::pages(),
'articles' => (int)SiteStats::articles(),
'files' => (int)SiteStats::images(),
'edits' => (int)SiteStats::edits(),
'users' => (int)SiteStats::users(),
'activeUsers' => (int)SiteStats::activeUsers(),
'admins' => (int)SiteStats::numberingroup( 'sysop' ),
];
}
return $this->getEngine()->registerInterface( 'mw.site.lua', $lib, $info );
}
/**
* Handler for pagesInCategory
* @internal
* @param string|null $category
* @param string|null $which
* @return int[]|int[][]
*/
public function pagesInCategory( $category = null, $which = null ) {
$this->checkType( 'pagesInCategory', 1, $category, 'string' );
$this->checkTypeOptional( 'pagesInCategory', 2, $which, 'string', 'all' );
$title = Title::makeTitleSafe( NS_CATEGORY, $category );
if ( !$title ) {
return [ 0 ];
}
$cacheKey = $title->getDBkey();
if ( !isset( $this->pagesInCategoryCache[$cacheKey] ) ) {
$this->incrementExpensiveFunctionCount();
$category = Category::newFromTitle( $title );
$counts = [
'all' => $category->getMemberCount(),
'subcats' => $category->getSubcatCount(),
'files' => $category->getFileCount(),
'pages' => $category->getPageCount( Category::COUNT_CONTENT_PAGES ),
];
$this->pagesInCategoryCache[$cacheKey] = $counts;
}
if ( $which === '*' ) {
return [ $this->pagesInCategoryCache[$cacheKey] ];
}
if ( !isset( $this->pagesInCategoryCache[$cacheKey][$which] ) ) {
$this->checkType(
'pagesInCategory', 2, $which, "one of '*', 'all', 'pages', 'subcats', or 'files'"
);
}
return [ $this->pagesInCategoryCache[$cacheKey][$which] ];
}
/**
* Handler for pagesInNamespace
* @internal
* @param int|string|null $ns
* @return int[]
*/
public function pagesInNamespace( $ns = null ) {
$this->checkType( 'pagesInNamespace', 1, $ns, 'number' );
return [ (int)SiteStats::pagesInNs( intval( $ns ) ) ];
}
/**
* Handler for usersInGroup
* @internal
* @param string|null $group
* @return int[]
*/
public function usersInGroup( $group = null ) {
$this->checkType( 'usersInGroup', 1, $group, 'string' );
return [ (int)SiteStats::numberingroup( strtolower( $group ) ) ];
}
/**
* Handler for getNsIndex
* @internal
* @param string|null $name
* @return int[]|bool[]
*/
public function getNsIndex( $name = null ) {
$this->checkType( 'getNsIndex', 1, $name, 'string' );
// PHP call is case-insensitive but chokes on non-standard spaces/underscores.
$name = trim( preg_replace( '/[\s_]+/', '_', $name ), '_' );
return [ MediaWikiServices::getInstance()->getContentLanguage()->getNsIndex( $name ) ];
}
/**
* Handler for interwikiMap
* @internal
* @param string|null $filter
* @return array[]
*/
public function interwikiMap( $filter = null ) {
global $wgLocalInterwikis, $wgExtraInterlanguageLinkPrefixes;
$this->checkTypeOptional( 'interwikiMap', 1, $filter, 'string', null );
$local = null;
if ( $filter === 'local' ) {
$local = true;
} elseif ( $filter === '!local' ) {
$local = false;
} elseif ( $filter !== null ) {
throw new LuaError(
"bad argument #1 to 'interwikiMap' (unknown filter '$filter')"
);
}
$cacheKey = $filter ?? 'null';
if ( !isset( self::$interwikiMapCache[$cacheKey] ) ) {
// Not expensive because we can have a max of three cache misses in the
// entire page parse.
$interwikiMap = [];
$lookup = MediaWikiServices::getInstance()->getInterwikiLookup();
$prefixes = $lookup->getAllPrefixes( $local );
foreach ( $prefixes as $row ) {
$prefix = $row['iw_prefix'];
$val = [
'prefix' => $prefix,
'url' => wfExpandUrl( $row['iw_url'], PROTO_RELATIVE ),
'isProtocolRelative' => substr( $row['iw_url'], 0, 2 ) === '//',
'isLocal' => isset( $row['iw_local'] ) && $row['iw_local'] == '1',
'isTranscludable' => isset( $row['iw_trans'] ) && $row['iw_trans'] == '1',
'isCurrentWiki' => in_array( $prefix, $wgLocalInterwikis ),
'isExtraLanguageLink' => in_array( $prefix, $wgExtraInterlanguageLinkPrefixes ),
];
if ( $val['isExtraLanguageLink'] ) {
$displayText = wfMessage( "interlanguage-link-$prefix" );
if ( !$displayText->isDisabled() ) {
$val['displayText'] = $displayText->text();
}
$tooltip = wfMessage( "interlanguage-link-sitename-$prefix" );
if ( !$tooltip->isDisabled() ) {
$val['tooltip'] = $tooltip->text();
}
}
$interwikiMap[$prefix] = $val;
}
self::$interwikiMapCache[$cacheKey] = $interwikiMap;
}
return [ self::$interwikiMapCache[$cacheKey] ];
}
}