2004-02-18 02:15:00 +00:00
|
|
|
<?php
|
2012-05-08 12:51:21 +00:00
|
|
|
/**
|
|
|
|
|
* Page existence cache.
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
|
|
|
*
|
|
|
|
|
* @file
|
|
|
|
|
* @ingroup Cache
|
|
|
|
|
*/
|
2017-02-07 04:49:57 +00:00
|
|
|
|
2016-04-27 21:52:50 +00:00
|
|
|
use MediaWiki\Linker\LinkTarget;
|
|
|
|
|
use MediaWiki\MediaWikiServices;
|
2021-06-02 15:49:19 +00:00
|
|
|
use MediaWiki\Page\PageIdentity;
|
|
|
|
|
use MediaWiki\Page\PageReference;
|
|
|
|
|
use Psr\Log\LoggerAwareInterface;
|
|
|
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
|
use Psr\Log\NullLogger;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\Rdbms\Database;
|
|
|
|
|
use Wikimedia\Rdbms\IDatabase;
|
2020-08-13 20:10:35 +00:00
|
|
|
use Wikimedia\Rdbms\ILoadBalancer;
|
2012-05-08 12:51:21 +00:00
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
|
|
|
|
* Cache for article titles (prefixed DB keys) and ids linked from one source
|
2008-04-14 07:45:50 +00:00
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup Cache
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
class LinkCache implements LoggerAwareInterface {
|
2018-07-11 13:11:12 +00:00
|
|
|
/** @var MapCacheLRU */
|
|
|
|
|
private $goodLinks;
|
|
|
|
|
/** @var MapCacheLRU */
|
|
|
|
|
private $badLinks;
|
2016-09-02 14:19:13 +00:00
|
|
|
/** @var WANObjectCache */
|
|
|
|
|
private $wanCache;
|
|
|
|
|
|
|
|
|
|
/** @var bool */
|
2011-12-21 18:53:00 +00:00
|
|
|
private $mForUpdate = false;
|
2004-07-18 08:48:43 +00:00
|
|
|
|
2016-09-02 14:19:13 +00:00
|
|
|
/** @var TitleFormatter */
|
2016-04-27 21:52:50 +00:00
|
|
|
private $titleFormatter;
|
|
|
|
|
|
2019-04-09 09:30:58 +00:00
|
|
|
/** @var NamespaceInfo */
|
|
|
|
|
private $nsInfo;
|
|
|
|
|
|
2020-08-13 20:10:35 +00:00
|
|
|
/** @var ILoadBalancer|null */
|
|
|
|
|
private $loadBalancer;
|
|
|
|
|
|
2021-06-02 15:49:19 +00:00
|
|
|
/** @var LoggerInterface */
|
|
|
|
|
private $logger;
|
|
|
|
|
|
2015-07-26 23:07:59 +00:00
|
|
|
/**
|
|
|
|
|
* How many Titles to store. There are two caches, so the amount actually
|
|
|
|
|
* stored in memory can be up to twice this.
|
|
|
|
|
*/
|
2020-05-15 22:16:46 +00:00
|
|
|
private const MAX_SIZE = 10000;
|
2015-07-26 23:07:59 +00:00
|
|
|
|
2020-08-13 20:10:35 +00:00
|
|
|
/**
|
|
|
|
|
* @param TitleFormatter $titleFormatter
|
|
|
|
|
* @param WANObjectCache $cache
|
|
|
|
|
* @param NamespaceInfo|null $nsInfo Null for backward compatibility, but deprecated
|
|
|
|
|
* @param ILoadBalancer|null $loadBalancer Use null when no database is set up, for example on installation
|
|
|
|
|
*/
|
2019-04-09 09:30:58 +00:00
|
|
|
public function __construct(
|
|
|
|
|
TitleFormatter $titleFormatter,
|
|
|
|
|
WANObjectCache $cache,
|
2020-08-13 20:10:35 +00:00
|
|
|
NamespaceInfo $nsInfo = null,
|
|
|
|
|
ILoadBalancer $loadBalancer = null
|
2019-04-09 09:30:58 +00:00
|
|
|
) {
|
|
|
|
|
if ( !$nsInfo ) {
|
|
|
|
|
wfDeprecated( __METHOD__ . ' with no NamespaceInfo argument', '1.34' );
|
|
|
|
|
$nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
|
|
|
|
|
}
|
2018-07-11 13:11:12 +00:00
|
|
|
$this->goodLinks = new MapCacheLRU( self::MAX_SIZE );
|
|
|
|
|
$this->badLinks = new MapCacheLRU( self::MAX_SIZE );
|
2016-09-02 14:19:13 +00:00
|
|
|
$this->wanCache = $cache;
|
2016-04-27 21:52:50 +00:00
|
|
|
$this->titleFormatter = $titleFormatter;
|
2019-04-09 09:30:58 +00:00
|
|
|
$this->nsInfo = $nsInfo;
|
2020-08-13 20:10:35 +00:00
|
|
|
$this->loadBalancer = $loadBalancer;
|
2021-06-02 15:49:19 +00:00
|
|
|
$this->logger = new NullLogger();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param LoggerInterface $logger
|
|
|
|
|
*/
|
|
|
|
|
public function setLogger( LoggerInterface $logger ) {
|
|
|
|
|
$this->logger = $logger;
|
2015-07-26 23:07:59 +00:00
|
|
|
}
|
|
|
|
|
|
2013-05-20 10:37:15 +00:00
|
|
|
/**
|
|
|
|
|
* Get an instance of this class.
|
2011-04-25 22:41:54 +00:00
|
|
|
*
|
|
|
|
|
* @return LinkCache
|
2021-05-03 12:29:14 +00:00
|
|
|
* @deprecated since 1.28, hard deprecated since 1.37
|
|
|
|
|
* Use MediaWikiServices::getLinkCache instead
|
2006-01-05 02:05:53 +00:00
|
|
|
*/
|
2016-04-27 21:30:54 +00:00
|
|
|
public static function singleton() {
|
2021-05-03 12:29:14 +00:00
|
|
|
wfDeprecated( __METHOD__, '1.28' );
|
2016-05-12 22:44:33 +00:00
|
|
|
return MediaWikiServices::getInstance()->getLinkCache();
|
2003-11-04 08:59:28 +00:00
|
|
|
}
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2021-05-14 20:04:02 +00:00
|
|
|
* General accessor to get/set whether the primary DB should be used
|
2015-10-30 09:29:12 +00:00
|
|
|
*
|
|
|
|
|
* This used to also set the FOR UPDATE option (locking the rows read
|
|
|
|
|
* in order to avoid link table inconsistency), which was later removed
|
|
|
|
|
* for performance on wikis with a high edit rate.
|
2011-05-28 18:58:51 +00:00
|
|
|
*
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param bool|null $update
|
2011-05-28 18:58:51 +00:00
|
|
|
* @return bool
|
2019-07-04 21:24:34 +00:00
|
|
|
* @deprecated Since 1.34
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2009-12-11 21:07:27 +00:00
|
|
|
public function forUpdate( $update = null ) {
|
2004-07-18 08:48:43 +00:00
|
|
|
return wfSetVar( $this->mForUpdate, $update );
|
|
|
|
|
}
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2011-05-28 18:58:51 +00:00
|
|
|
/**
|
2021-06-02 15:49:19 +00:00
|
|
|
* @param LinkTarget|PageReference|string $page
|
|
|
|
|
* @param bool $passThrough Return $page if $page is a string
|
|
|
|
|
*
|
|
|
|
|
* @return ?string the cache key
|
2011-05-28 18:58:51 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
private function getCacheKey( $page, $passThrough = false ) {
|
|
|
|
|
if ( is_string( $page ) ) {
|
|
|
|
|
if ( $passThrough ) {
|
|
|
|
|
return $page;
|
|
|
|
|
} else {
|
|
|
|
|
throw new InvalidArgumentException( 'They key may not be given as a string here' );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $page instanceof PageReference && $page->getWikiId() !== PageReference::LOCAL ) {
|
|
|
|
|
// No cross-wiki support yet. Perhaps LinkCache can become wiki-aware in the future.
|
|
|
|
|
$this->logger->info(
|
|
|
|
|
'cross-wiki page reference',
|
2021-07-01 17:21:19 +00:00
|
|
|
[
|
|
|
|
|
'page-wiki' => $page->getWikiId(),
|
|
|
|
|
'page-reference' => $this->titleFormatter->getFullText( $page )
|
|
|
|
|
]
|
2021-06-02 15:49:19 +00:00
|
|
|
);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $page instanceof PageIdentity && !$page->canExist() ) {
|
|
|
|
|
// Non-proper page, perhaps a special page or interwiki link or relative section link.
|
|
|
|
|
$this->logger->warning(
|
2021-07-27 06:51:19 +00:00
|
|
|
'non-proper page reference: {page-reference}',
|
2021-07-01 17:21:19 +00:00
|
|
|
[ 'page-reference' => $this->titleFormatter->getFullText( $page ) ]
|
2021-06-02 15:49:19 +00:00
|
|
|
);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $page instanceof LinkTarget
|
|
|
|
|
&& ( $page->isExternal() || $page->getText() === '' || $page->getNamespace() < 0 )
|
|
|
|
|
) {
|
|
|
|
|
// Interwiki link or relative section link. These do not have a page ID, so they
|
|
|
|
|
// can neither be "good" nor "bad" in the sense of this class.
|
|
|
|
|
$this->logger->warning(
|
2021-07-27 06:51:19 +00:00
|
|
|
'link to non-proper page: {page-link}',
|
2021-07-01 17:21:19 +00:00
|
|
|
[ 'page-link' => $this->titleFormatter->getFullText( $page ) ]
|
2021-06-02 15:49:19 +00:00
|
|
|
);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->titleFormatter->getPrefixedDBkey( $page );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the ID of the given page, if information about this page has been cached.
|
|
|
|
|
*
|
|
|
|
|
* @param LinkTarget|PageReference|string $page The page to get the ID for,
|
|
|
|
|
* as an object or a prefixed DB key.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only a string was accepted.
|
|
|
|
|
* @return int Page ID, or zero if the page was not cached or does not exist or is not a
|
|
|
|
|
* proper page (e.g. a special page or an interwiki link).
|
|
|
|
|
*/
|
|
|
|
|
public function getGoodLinkID( $page ) {
|
|
|
|
|
$key = $this->getCacheKey( $page, true );
|
|
|
|
|
|
|
|
|
|
if ( $key === null ) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$info = $this->goodLinks->get( $key );
|
|
|
|
|
|
2015-11-09 23:17:39 +00:00
|
|
|
if ( !$info ) {
|
2003-04-14 23:10:40 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
2015-11-09 23:14:42 +00:00
|
|
|
return $info['id'];
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2008-04-09 05:21:00 +00:00
|
|
|
/**
|
2021-06-02 15:49:19 +00:00
|
|
|
* Get a field of a page from the cache.
|
|
|
|
|
*
|
2015-11-09 23:14:42 +00:00
|
|
|
* If this link is not a cached good title, it will return NULL.
|
2021-06-02 15:49:19 +00:00
|
|
|
* @param LinkTarget|PageReference $page The page to get cached info for.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only LinkTarget was accepted.
|
|
|
|
|
* @param string $field ( 'id', 'length', 'redirect', 'revision', 'model', 'lang', 'restrictions' )
|
|
|
|
|
* @return string|int|null The field value, or null if the page was not cached or does not exist
|
|
|
|
|
* or is not a proper page (e.g. a special page or interwiki link).
|
2008-04-09 05:21:00 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function getGoodLinkFieldObj( $page, string $field ) {
|
|
|
|
|
$key = $this->getCacheKey( $page );
|
|
|
|
|
if ( $key === null ) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$info = $this->goodLinks->get( $key );
|
2015-11-09 23:17:39 +00:00
|
|
|
if ( !$info ) {
|
2009-12-11 21:07:27 +00:00
|
|
|
return null;
|
2008-04-09 05:21:00 +00:00
|
|
|
}
|
2015-11-09 23:14:42 +00:00
|
|
|
return $info[$field];
|
2008-04-09 05:21:00 +00:00
|
|
|
}
|
2003-04-14 23:10:40 +00:00
|
|
|
|
2011-05-28 18:58:51 +00:00
|
|
|
/**
|
2021-06-02 15:49:19 +00:00
|
|
|
* Returns true if the fact that this page does not exist had been added to the cache.
|
|
|
|
|
*
|
|
|
|
|
* @param LinkTarget|PageReference|string $page The page to get cached info for,
|
|
|
|
|
* as an object or a prefixed DB key.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only a string was accepted.
|
|
|
|
|
* @return bool True if the page is known to not exist.
|
2011-05-28 18:58:51 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function isBadLink( $page ) {
|
|
|
|
|
$key = $this->getCacheKey( $page, true );
|
|
|
|
|
|
|
|
|
|
return $key !== null && $this->badLinks->has( $key );
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
2008-04-09 05:21:00 +00:00
|
|
|
/**
|
2021-06-02 15:49:19 +00:00
|
|
|
* Add information about an existing page to the cache.
|
|
|
|
|
*
|
|
|
|
|
* @see addGoodLinkObjFromRow()
|
2010-07-17 20:13:49 +00:00
|
|
|
*
|
2013-11-17 20:42:23 +00:00
|
|
|
* @param int $id Page's ID
|
2021-06-02 15:49:19 +00:00
|
|
|
* @param LinkTarget|PageReference $page The page to set cached info for.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only LinkTarget was accepted.
|
2013-11-17 20:42:23 +00:00
|
|
|
* @param int $len Text's length
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param int|null $redir Whether the page is a redirect
|
2013-11-17 20:42:23 +00:00
|
|
|
* @param int $revision Latest revision's ID
|
2014-08-20 21:46:11 +00:00
|
|
|
* @param string|null $model Latest revision's content model ID
|
2015-12-19 15:25:45 +00:00
|
|
|
* @param string|null $lang Language code of the page, if not the content language
|
2008-04-09 05:21:00 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function addGoodLinkObj( $id, $page, $len = -1, $redir = null,
|
2015-12-19 15:25:45 +00:00
|
|
|
$revision = 0, $model = null, $lang = null
|
2013-11-17 20:42:23 +00:00
|
|
|
) {
|
2021-06-02 15:49:19 +00:00
|
|
|
$key = $this->getCacheKey( $page );
|
|
|
|
|
|
|
|
|
|
if ( $key === null ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->goodLinks->set( $key, [
|
2015-07-26 23:07:59 +00:00
|
|
|
'id' => (int)$id,
|
2013-11-17 20:42:23 +00:00
|
|
|
'length' => (int)$len,
|
|
|
|
|
'redirect' => (int)$redir,
|
|
|
|
|
'revision' => (int)$revision,
|
2014-08-20 21:46:11 +00:00
|
|
|
'model' => $model ? (string)$model : null,
|
2015-12-19 15:25:45 +00:00
|
|
|
'lang' => $lang ? (string)$lang : null,
|
2019-03-15 09:24:31 +00:00
|
|
|
'restrictions' => null
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2021-06-02 15:49:19 +00:00
|
|
|
$this->badLinks->clear( $key );
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
2011-09-20 15:19:18 +00:00
|
|
|
/**
|
|
|
|
|
* Same as above with better interface.
|
|
|
|
|
* @since 1.19
|
2021-06-02 15:49:19 +00:00
|
|
|
*
|
|
|
|
|
* @param LinkTarget|PageReference $page The page to set cached info for.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only LinkTarget was accepted.
|
|
|
|
|
* @param stdClass $row Object which has all fields returned by getSelectFields().
|
|
|
|
|
*
|
2011-09-20 15:19:18 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function addGoodLinkObjFromRow( $page, stdClass $row ) {
|
|
|
|
|
$key = $this->getCacheKey( $page );
|
|
|
|
|
|
|
|
|
|
if ( $key === null ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->goodLinks->set( $key, [
|
2015-07-26 23:07:59 +00:00
|
|
|
'id' => intval( $row->page_id ),
|
2011-09-20 15:19:18 +00:00
|
|
|
'length' => intval( $row->page_len ),
|
|
|
|
|
'redirect' => intval( $row->page_is_redirect ),
|
|
|
|
|
'revision' => intval( $row->page_latest ),
|
2019-03-15 09:24:31 +00:00
|
|
|
'model' => !empty( $row->page_content_model )
|
|
|
|
|
? strval( $row->page_content_model )
|
|
|
|
|
: null,
|
|
|
|
|
'lang' => !empty( $row->page_lang )
|
|
|
|
|
? strval( $row->page_lang )
|
|
|
|
|
: null,
|
|
|
|
|
'restrictions' => !empty( $row->page_restrictions )
|
|
|
|
|
? strval( $row->page_restrictions )
|
|
|
|
|
: null
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2021-06-02 15:49:19 +00:00
|
|
|
$this->badLinks->clear( $key );
|
2011-09-20 15:19:18 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-26 19:21:50 +00:00
|
|
|
/**
|
2021-06-02 15:49:19 +00:00
|
|
|
* @param LinkTarget|PageReference $page The page to set cached info for.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only LinkTarget was accepted.
|
2011-05-26 19:21:50 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function addBadLinkObj( $page ) {
|
|
|
|
|
$key = $this->getCacheKey( $page );
|
|
|
|
|
if ( $key !== null && !$this->isBadLink( $key ) ) {
|
|
|
|
|
$this->badLinks->set( $key, 1 );
|
|
|
|
|
$this->goodLinks->clear( $key );
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-26 21:36:24 +00:00
|
|
|
/**
|
2021-06-02 15:49:19 +00:00
|
|
|
* @param LinkTarget|PageReference|string $page The page to clear cached info for,
|
|
|
|
|
* as an object or a prefixed DB key.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only a string was accepted.
|
2016-04-26 21:36:24 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function clearBadLink( $page ) {
|
|
|
|
|
$key = $this->getCacheKey( $page, true );
|
|
|
|
|
|
|
|
|
|
if ( $key !== null ) {
|
|
|
|
|
$this->badLinks->clear( $key );
|
|
|
|
|
}
|
2003-11-04 08:59:28 +00:00
|
|
|
}
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2011-05-26 19:21:50 +00:00
|
|
|
/**
|
2021-06-02 15:49:19 +00:00
|
|
|
* @param LinkTarget|PageReference $page The page to clear cached info for.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only LinkTarget was accepted.
|
2011-05-26 19:21:50 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function clearLink( $page ) {
|
|
|
|
|
$key = $this->getCacheKey( $page );
|
|
|
|
|
|
|
|
|
|
if ( $key !== null ) {
|
|
|
|
|
$this->badLinks->clear( $key );
|
|
|
|
|
$this->goodLinks->clear( $key );
|
|
|
|
|
}
|
2008-09-07 08:24:06 +00:00
|
|
|
}
|
2003-04-14 23:10:40 +00:00
|
|
|
|
2016-05-13 07:00:39 +00:00
|
|
|
/**
|
|
|
|
|
* Fields that LinkCache needs to select
|
|
|
|
|
*
|
|
|
|
|
* @since 1.28
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public static function getSelectFields() {
|
2019-12-17 16:20:32 +00:00
|
|
|
global $wgPageLanguageUseDB;
|
2016-05-13 07:00:39 +00:00
|
|
|
|
2019-03-15 09:24:31 +00:00
|
|
|
$fields = [
|
|
|
|
|
'page_id',
|
|
|
|
|
'page_len',
|
|
|
|
|
'page_is_redirect',
|
|
|
|
|
'page_latest',
|
2019-12-17 16:20:32 +00:00
|
|
|
'page_restrictions',
|
|
|
|
|
'page_content_model',
|
2019-03-15 09:24:31 +00:00
|
|
|
];
|
2019-12-17 16:20:32 +00:00
|
|
|
|
2016-05-13 07:00:39 +00:00
|
|
|
if ( $wgPageLanguageUseDB ) {
|
|
|
|
|
$fields[] = 'page_lang';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $fields;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-30 09:33:11 +00:00
|
|
|
/**
|
2021-06-02 15:49:19 +00:00
|
|
|
* Add a title to the link cache, return the page_id or zero if non-existent.
|
|
|
|
|
* This causes the link to be looked up in the database if it is not yet cached.
|
|
|
|
|
*
|
|
|
|
|
* @param LinkTarget|PageReference $page The page to load.
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only LinkTarget was accepted.
|
2010-07-17 20:13:49 +00:00
|
|
|
*
|
2015-11-09 23:14:42 +00:00
|
|
|
* @return int Page ID or zero
|
2005-12-30 09:33:11 +00:00
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function addLinkObj( $page ) {
|
|
|
|
|
if ( $page instanceof LinkTarget ) {
|
|
|
|
|
$nt = $page;
|
|
|
|
|
} else {
|
|
|
|
|
$nt = TitleValue::castPageToLinkTarget( $page );
|
2005-10-22 20:52:30 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 15:49:19 +00:00
|
|
|
$key = $this->getCacheKey( $nt );
|
|
|
|
|
if ( $key === null ) {
|
2005-08-02 13:35:19 +00:00
|
|
|
return 0;
|
2003-10-16 13:30:45 +00:00
|
|
|
}
|
2011-02-12 04:06:22 +00:00
|
|
|
|
2021-06-02 15:49:19 +00:00
|
|
|
if ( !$this->mForUpdate ) {
|
|
|
|
|
$id = $this->getGoodLinkID( $key );
|
|
|
|
|
if ( $id != 0 ) {
|
|
|
|
|
return $id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $this->isBadLink( $key ) ) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-13 20:10:35 +00:00
|
|
|
// Only query database, when load balancer is provided by service wiring
|
|
|
|
|
// This maybe not happen when running as part of the installer
|
|
|
|
|
if ( $this->loadBalancer === null ) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-02 14:19:13 +00:00
|
|
|
// Cache template/file pages as they are less often viewed but heavily used
|
|
|
|
|
if ( $this->mForUpdate ) {
|
2021-04-29 02:37:11 +00:00
|
|
|
$row = $this->fetchPageRow( $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_PRIMARY ), $nt );
|
2016-09-02 14:19:13 +00:00
|
|
|
} elseif ( $this->isCacheable( $nt ) ) {
|
|
|
|
|
// These pages are often transcluded heavily, so cache them
|
|
|
|
|
$cache = $this->wanCache;
|
|
|
|
|
$row = $cache->getWithSetCallback(
|
|
|
|
|
$cache->makeKey( 'page', $nt->getNamespace(), sha1( $nt->getDBkey() ) ),
|
|
|
|
|
$cache::TTL_DAY,
|
|
|
|
|
function ( $curValue, &$ttl, array &$setOpts ) use ( $cache, $nt ) {
|
2020-08-13 20:10:35 +00:00
|
|
|
$dbr = $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
|
2016-09-02 14:19:13 +00:00
|
|
|
$setOpts += Database::getCacheSetOptions( $dbr );
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2016-09-02 14:19:13 +00:00
|
|
|
$row = $this->fetchPageRow( $dbr, $nt );
|
|
|
|
|
$mtime = $row ? wfTimestamp( TS_UNIX, $row->page_touched ) : false;
|
|
|
|
|
$ttl = $cache->adaptiveTTL( $mtime, $ttl );
|
2015-11-09 23:14:42 +00:00
|
|
|
|
2016-09-02 14:19:13 +00:00
|
|
|
return $row;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
} else {
|
2020-08-13 20:10:35 +00:00
|
|
|
$row = $this->fetchPageRow( $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA ), $nt );
|
2016-09-02 14:19:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $row ) {
|
2015-11-09 23:14:42 +00:00
|
|
|
$this->addGoodLinkObjFromRow( $nt, $row );
|
|
|
|
|
$id = intval( $row->page_id );
|
2008-08-29 19:12:56 +00:00
|
|
|
} else {
|
2005-05-26 10:23:36 +00:00
|
|
|
$this->addBadLinkObj( $nt );
|
2011-09-20 16:26:39 +00:00
|
|
|
$id = 0;
|
2005-05-26 10:23:36 +00:00
|
|
|
}
|
2011-09-20 15:19:18 +00:00
|
|
|
|
2003-04-14 23:10:40 +00:00
|
|
|
return $id;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-03 04:43:16 +00:00
|
|
|
/**
|
|
|
|
|
* @param WANObjectCache $cache
|
2021-06-02 15:49:19 +00:00
|
|
|
* @param LinkTarget|Pagereference $page
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only LinkTarget was accepted.
|
2016-09-03 04:43:16 +00:00
|
|
|
* @return string[]
|
|
|
|
|
* @since 1.28
|
|
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function getMutableCacheKeys( WANObjectCache $cache, $page ) {
|
|
|
|
|
$key = $this->getCacheKey( $page );
|
|
|
|
|
// if no key can be derived, the page isn't cacheable
|
|
|
|
|
if ( $key === null ) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $this->isCacheable( $page ) ) {
|
|
|
|
|
return [ $cache->makeKey( 'page', $page->getNamespace(), sha1( $page->getDBkey() ) ) ];
|
2016-09-03 04:43:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 15:49:19 +00:00
|
|
|
/**
|
|
|
|
|
* @param LinkTarget|PageReference $page
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
private function isCacheable( $page ) {
|
|
|
|
|
$ns = $page->getNamespace();
|
2019-07-10 22:18:48 +00:00
|
|
|
if ( in_array( $ns, [ NS_TEMPLATE, NS_FILE, NS_CATEGORY, NS_MEDIAWIKI ] ) ) {
|
2019-03-14 06:44:38 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// Focus on transcluded pages more than the main content
|
2019-04-09 09:30:58 +00:00
|
|
|
if ( $this->nsInfo->isContent( $ns ) ) {
|
2019-03-14 06:44:38 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Non-talk extension namespaces (e.g. NS_MODULE)
|
2019-04-09 09:30:58 +00:00
|
|
|
return ( $ns >= 100 && $this->nsInfo->isSubject( $ns ) );
|
2016-09-02 14:19:13 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 15:49:19 +00:00
|
|
|
/**
|
|
|
|
|
* @param IDatabase $db
|
|
|
|
|
* @param LinkTarget|PageReference $page
|
|
|
|
|
*
|
|
|
|
|
* @return stdClass|false
|
|
|
|
|
*/
|
|
|
|
|
private function fetchPageRow( IDatabase $db, $page ) {
|
2016-09-02 14:19:13 +00:00
|
|
|
$fields = self::getSelectFields();
|
2021-06-02 15:49:19 +00:00
|
|
|
if ( $this->isCacheable( $page ) ) {
|
2016-09-02 14:19:13 +00:00
|
|
|
$fields[] = 'page_touched';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $db->selectRow(
|
|
|
|
|
'page',
|
|
|
|
|
$fields,
|
2021-06-02 15:49:19 +00:00
|
|
|
[ 'page_namespace' => $page->getNamespace(), 'page_title' => $page->getDBkey() ],
|
2016-09-02 14:19:13 +00:00
|
|
|
__METHOD__
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Purge the link cache for a title
|
|
|
|
|
*
|
2021-06-02 15:49:19 +00:00
|
|
|
* @param LinkTarget|PageReference $page
|
|
|
|
|
* In MediaWiki 1.36 and earlier, only LinkTarget was accepted.
|
2016-09-02 14:19:13 +00:00
|
|
|
* @since 1.28
|
|
|
|
|
*/
|
2021-06-02 15:49:19 +00:00
|
|
|
public function invalidateTitle( $page ) {
|
|
|
|
|
if ( $this->isCacheable( $page ) ) {
|
2018-06-15 04:00:38 +00:00
|
|
|
$cache = $this->wanCache;
|
2016-09-02 14:19:13 +00:00
|
|
|
$cache->delete(
|
2021-06-02 15:49:19 +00:00
|
|
|
$cache->makeKey( 'page', $page->getNamespace(), sha1( $page->getDBkey() ) )
|
2016-09-02 14:19:13 +00:00
|
|
|
);
|
|
|
|
|
}
|
2021-06-02 15:49:19 +00:00
|
|
|
|
|
|
|
|
$this->clearLink( $page );
|
2016-09-02 14:19:13 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2005-08-02 13:35:19 +00:00
|
|
|
* Clears cache
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2008-04-09 13:02:34 +00:00
|
|
|
public function clear() {
|
2018-07-11 13:11:12 +00:00
|
|
|
$this->goodLinks->clear();
|
|
|
|
|
$this->badLinks->clear();
|
2005-05-29 10:17:44 +00:00
|
|
|
}
|
2021-06-02 15:49:19 +00:00
|
|
|
|
2003-07-06 11:42:42 +00:00
|
|
|
}
|