* Remove checks in HTMLFileCache.php and Article.php. These haven't been needed since the same check was added to Setup.php, many years ago. When FileCache is enabled, The Setup.php code disables MWDebug. There is no reason for FileCache to then also disable itself based on unused config. That means both of them lose. We now handle this logic in one place: MWDebug::setup(). * In rebuildFileCache.php, turn it off explicitly, just in case. The previous code there didn't work because finalSetup() is called after doMaintenance.php includes Setup.php, which is what checked this config var to decide on MWDebug::init. On the other hand, it's also always off in CLI mode. But, let's not depend on that, maybe we decide to enable it on CLI one day! Just keep it off explicitly here. Bug: T189966 Change-Id: I45a8f77092249751dc6f276aa5bb67ebf5b4f64c
190 lines
6 KiB
PHP
190 lines
6 KiB
PHP
<?php
|
|
/**
|
|
* Build file cache for content pages
|
|
*
|
|
* 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 Maintenance
|
|
*/
|
|
|
|
use MediaWiki\MediaWikiServices;
|
|
|
|
require_once __DIR__ . '/Maintenance.php';
|
|
|
|
/**
|
|
* Maintenance script that builds file cache for content pages.
|
|
*
|
|
* @ingroup Maintenance
|
|
*/
|
|
class RebuildFileCache extends Maintenance {
|
|
private $enabled = true;
|
|
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->addDescription( 'Build file cache for content pages' );
|
|
$this->addOption( 'start', 'Page_id to start from', false, true );
|
|
$this->addOption( 'end', 'Page_id to end on', false, true );
|
|
$this->addOption( 'overwrite', 'Refresh page cache' );
|
|
$this->setBatchSize( 100 );
|
|
}
|
|
|
|
public function finalSetup() {
|
|
global $wgUseFileCache;
|
|
|
|
$this->enabled = $wgUseFileCache;
|
|
// Script will handle capturing output and saving it itself
|
|
$wgUseFileCache = false;
|
|
// Avoid DB writes (like enotif/counters)
|
|
MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
|
|
->setReason( 'Building cache' );
|
|
|
|
// Ensure no debug-specific logic ends up in the cache (must be after Setup.php)
|
|
MWDebug::deinit();
|
|
|
|
parent::finalSetup();
|
|
}
|
|
|
|
public function execute() {
|
|
if ( !$this->enabled ) {
|
|
$this->fatalError( "Nothing to do -- \$wgUseFileCache is disabled." );
|
|
}
|
|
|
|
$start = $this->getOption( 'start', "0" );
|
|
if ( !ctype_digit( $start ) ) {
|
|
$this->fatalError( "Invalid value for start parameter." );
|
|
}
|
|
$start = intval( $start );
|
|
|
|
$end = $this->getOption( 'end', "0" );
|
|
if ( !ctype_digit( $end ) ) {
|
|
$this->fatalError( "Invalid value for end parameter." );
|
|
}
|
|
$end = intval( $end );
|
|
|
|
$this->output( "Building content page file cache from page {$start}!\n" );
|
|
|
|
$dbr = $this->getDB( DB_REPLICA );
|
|
$batchSize = $this->getBatchSize();
|
|
$overwrite = $this->hasOption( 'overwrite' );
|
|
$start = ( $start > 0 )
|
|
? $start
|
|
: $dbr->selectField( 'page', 'MIN(page_id)', '', __METHOD__ );
|
|
$end = ( $end > 0 )
|
|
? $end
|
|
: $dbr->selectField( 'page', 'MAX(page_id)', '', __METHOD__ );
|
|
if ( !$start ) {
|
|
$this->fatalError( "Nothing to do." );
|
|
}
|
|
|
|
// Mock request (hack, no real client)
|
|
$_SERVER['HTTP_ACCEPT_ENCODING'] = 'bgzip';
|
|
|
|
# Do remaining chunk
|
|
$end += $batchSize - 1;
|
|
$blockStart = $start;
|
|
$blockEnd = $start + $batchSize - 1;
|
|
|
|
$dbw = $this->getDB( DB_MASTER );
|
|
// Go through each page and save the output
|
|
while ( $blockEnd <= $end ) {
|
|
// Get the pages
|
|
$res = $dbr->select( 'page',
|
|
[ 'page_namespace', 'page_title', 'page_id' ],
|
|
[ 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()->
|
|
getContentNamespaces(),
|
|
"page_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd ],
|
|
__METHOD__,
|
|
[ 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' ]
|
|
);
|
|
|
|
$this->beginTransaction( $dbw, __METHOD__ ); // for any changes
|
|
foreach ( $res as $row ) {
|
|
$rebuilt = false;
|
|
|
|
$title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
|
|
if ( $title === null ) {
|
|
$this->output( "Page {$row->page_id} has bad title\n" );
|
|
continue; // broken title?
|
|
}
|
|
|
|
$context = new RequestContext();
|
|
$context->setTitle( $title );
|
|
$article = Article::newFromTitle( $title, $context );
|
|
$context->setWikiPage( $article->getPage() );
|
|
|
|
// Some extensions like FlaggedRevs while error out if this is unset
|
|
RequestContext::getMain()->setTitle( $title );
|
|
|
|
// If the article is cacheable, then load it
|
|
if ( $article->isFileCacheable( HTMLFileCache::MODE_REBUILD ) ) {
|
|
$viewCache = new HTMLFileCache( $title, 'view' );
|
|
$historyCache = new HTMLFileCache( $title, 'history' );
|
|
if ( $viewCache->isCacheGood() && $historyCache->isCacheGood() ) {
|
|
if ( $overwrite ) {
|
|
$rebuilt = true;
|
|
} else {
|
|
$this->output( "Page '$title' (id {$row->page_id}) already cached\n" );
|
|
continue; // done already!
|
|
}
|
|
}
|
|
|
|
Wikimedia\suppressWarnings(); // header notices
|
|
|
|
// 1. Cache ?action=view
|
|
// Be sure to reset the mocked request time (T24852)
|
|
$_SERVER['REQUEST_TIME_FLOAT'] = microtime( true );
|
|
ob_start();
|
|
$article->view();
|
|
$context->getOutput()->output();
|
|
$context->getOutput()->clearHTML();
|
|
$viewHtml = ob_get_clean();
|
|
$viewCache->saveToFileCache( $viewHtml );
|
|
|
|
// 2. Cache ?action=history
|
|
// Be sure to reset the mocked request time (T24852)
|
|
$_SERVER['REQUEST_TIME_FLOAT'] = microtime( true );
|
|
ob_start();
|
|
Action::factory( 'history', $article, $context )->show();
|
|
$context->getOutput()->output();
|
|
$context->getOutput()->clearHTML();
|
|
$historyHtml = ob_get_clean();
|
|
$historyCache->saveToFileCache( $historyHtml );
|
|
|
|
Wikimedia\restoreWarnings();
|
|
|
|
if ( $rebuilt ) {
|
|
$this->output( "Re-cached page '$title' (id {$row->page_id})..." );
|
|
} else {
|
|
$this->output( "Cached page '$title' (id {$row->page_id})..." );
|
|
}
|
|
$this->output( "[view: " . strlen( $viewHtml ) . " bytes; " .
|
|
"history: " . strlen( $historyHtml ) . " bytes]\n" );
|
|
} else {
|
|
$this->output( "Page '$title' (id {$row->page_id}) not cacheable\n" );
|
|
}
|
|
}
|
|
$this->commitTransaction( $dbw, __METHOD__ ); // commit any changes (just for sanity)
|
|
|
|
$blockStart += $batchSize;
|
|
$blockEnd += $batchSize;
|
|
}
|
|
$this->output( "Done!\n" );
|
|
}
|
|
}
|
|
|
|
$maintClass = RebuildFileCache::class;
|
|
require_once RUN_MAINTENANCE_IF_MAIN;
|