fiveupgrade.inc

来自「php 开发的内容管理系统」· INC 代码 · 共 1,215 行 · 第 1/3 页

INC
1,215
字号
<?phprequire_once( 'cleanupDupes.inc' );require_once( 'userDupes.inc' );require_once( 'updaters.inc' );define( 'MW_UPGRADE_COPY',     false );define( 'MW_UPGRADE_ENCODE',   true  );define( 'MW_UPGRADE_NULL',     null  );define( 'MW_UPGRADE_CALLBACK', null  ); // for self-documentation onlyclass FiveUpgrade {	function FiveUpgrade() {		global $wgDatabase;		$this->conversionTables = $this->prepareWindows1252();		$this->dbw =& $this->newConnection();		$this->dbr =& $this->streamConnection();		$this->cleanupSwaps = array();		$this->emailAuth = false; # don't preauthenticate emails		$this->maxLag    = 10; # if slaves are lagged more than 10 secs, wait	}	function doing( $step ) {		return is_null( $this->step ) || $step == $this->step;	}	function upgrade( $step ) {		$this->step = $step;		$tables = array(			'page',			'links',			'user',			'image',			'oldimage',			'watchlist',			'logging',			'archive',			'imagelinks',			'categorylinks',			'ipblocks',			'recentchanges',			'querycache' );		foreach( $tables as $table ) {			if( $this->doing( $table ) ) {				$method = 'upgrade' . ucfirst( $table );				$this->$method();			}		}		if( $this->doing( 'cleanup' ) ) {			$this->upgradeCleanup();		}	}	/**	 * Open a connection to the master server with the admin rights.	 * @return Database	 * @access private	 */	function &newConnection() {		global $wgDBadminuser, $wgDBadminpassword;		global $wgDBserver, $wgDBname;		$db =& new Database( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname );		return $db;	}	/**	 * Open a second connection to the master server, with buffering off.	 * This will let us stream large datasets in and write in chunks on the	 * other end.	 * @return Database	 * @access private	 */	function &streamConnection() {		$timeout = 3600 * 24;		$db =& $this->newConnection();		$db->bufferResults( false );		$db->query( "SET net_read_timeout=$timeout" );		$db->query( "SET net_write_timeout=$timeout" );		return $db;	}	/**	 * Prepare a conversion array for converting Windows Code Page 1252 to	 * UTF-8. This should provide proper conversion of text that was miscoded	 * as Windows-1252 by naughty user-agents, and doesn't rely on an outside	 * iconv library.	 *	 * @return array	 * @access private	 */	function prepareWindows1252() {		# Mappings from:		# http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT		static $cp1252 = array(			0x80 => 0x20AC,	#EURO SIGN			0x81 => UNICODE_REPLACEMENT,			0x82 => 0x201A,	#SINGLE LOW-9 QUOTATION MARK			0x83 => 0x0192,	#LATIN SMALL LETTER F WITH HOOK			0x84 => 0x201E,	#DOUBLE LOW-9 QUOTATION MARK			0x85 => 0x2026,	#HORIZONTAL ELLIPSIS			0x86 => 0x2020,	#DAGGER			0x87 => 0x2021,	#DOUBLE DAGGER			0x88 => 0x02C6,	#MODIFIER LETTER CIRCUMFLEX ACCENT			0x89 => 0x2030,	#PER MILLE SIGN			0x8A => 0x0160,	#LATIN CAPITAL LETTER S WITH CARON			0x8B => 0x2039,	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK			0x8C => 0x0152,	#LATIN CAPITAL LIGATURE OE			0x8D => UNICODE_REPLACEMENT,			0x8E => 0x017D,	#LATIN CAPITAL LETTER Z WITH CARON			0x8F => UNICODE_REPLACEMENT,			0x90 => UNICODE_REPLACEMENT,			0x91 => 0x2018,	#LEFT SINGLE QUOTATION MARK			0x92 => 0x2019,	#RIGHT SINGLE QUOTATION MARK			0x93 => 0x201C,	#LEFT DOUBLE QUOTATION MARK			0x94 => 0x201D,	#RIGHT DOUBLE QUOTATION MARK			0x95 => 0x2022,	#BULLET			0x96 => 0x2013,	#EN DASH			0x97 => 0x2014,	#EM DASH			0x98 => 0x02DC,	#SMALL TILDE			0x99 => 0x2122,	#TRADE MARK SIGN			0x9A => 0x0161,	#LATIN SMALL LETTER S WITH CARON			0x9B => 0x203A,	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK			0x9C => 0x0153,	#LATIN SMALL LIGATURE OE			0x9D => UNICODE_REPLACEMENT,			0x9E => 0x017E,	#LATIN SMALL LETTER Z WITH CARON			0x9F => 0x0178,	#LATIN CAPITAL LETTER Y WITH DIAERESIS			);		$pairs = array();		for( $i = 0; $i < 0x100; $i++ ) {			$unicode = isset( $cp1252[$i] ) ? $cp1252[$i] : $i;			$pairs[chr( $i )] = codepointToUtf8( $unicode );		}		return $pairs;	}	/**	 * Convert from 8-bit Windows-1252 to UTF-8 if necessary.	 * @param string $text	 * @return string	 * @access private	 */	function conv( $text ) {		global $wgUseLatin1;		return is_null( $text )			? null			: ( $wgUseLatin1				? strtr( $text, $this->conversionTables )				: $text );	}	/**	 * Dump timestamp and message to output	 * @param string $message	 * @access private	 */	function log( $message ) {		global $wgDBname;		echo $wgDBname . ' ' . wfTimestamp( TS_DB ) . ': ' . $message . "\n";		flush();	}	/**	 * Initialize the chunked-insert system.	 * Rows will be inserted in chunks of the given number, rather	 * than in a giant INSERT...SELECT query, to keep the serialized	 * MySQL database replication from getting hung up. This way other	 * things can be going on during conversion without waiting for	 * slaves to catch up as badly.	 *	 * @param int $chunksize Number of rows to insert at once	 * @param int $final Total expected number of rows / id of last row,	 *                   used for progress reports.	 * @param string $table to insert on	 * @param string $fname function name to report in SQL	 * @access private	 */	function setChunkScale( $chunksize, $final, $table, $fname ) {		$this->chunkSize  = $chunksize;		$this->chunkFinal = $final;		$this->chunkCount = 0;		$this->chunkStartTime = wfTime();		$this->chunkOptions = array( 'IGNORE' );		$this->chunkTable = $table;		$this->chunkFunction = $fname;	}	/**	 * Chunked inserts: perform an insert if we've reached the chunk limit.	 * Prints a progress report with estimated completion time.	 * @param array &$chunk -- This will be emptied if an insert is done.	 * @param int $key A key identifier to use in progress estimation in	 *                 place of the number of rows inserted. Use this if	 *                 you provided a max key number instead of a count	 *                 as the final chunk number in setChunkScale()	 * @access private	 */	function addChunk( &$chunk, $key = null ) {		if( count( $chunk ) >= $this->chunkSize ) {			$this->insertChunk( $chunk );			$this->chunkCount += count( $chunk );			$now = wfTime();			$delta = $now - $this->chunkStartTime;			$rate = $this->chunkCount / $delta;			if( is_null( $key ) ) {				$completed = $this->chunkCount;			} else {				$completed = $key;			}			$portion = $completed / $this->chunkFinal;			$estimatedTotalTime = $delta / $portion;			$eta = $this->chunkStartTime + $estimatedTotalTime;			printf( "%s: %6.2f%% done on %s; ETA %s [%d/%d] %.2f/sec\n",				wfTimestamp( TS_DB, intval( $now ) ),				$portion * 100.0,				$this->chunkTable,				wfTimestamp( TS_DB, intval( $eta ) ),				$completed,				$this->chunkFinal,				$rate );			flush();			$chunk = array();		}	}	/**	 * Chunked inserts: perform an insert unconditionally, at the end, and log.	 * @param array &$chunk -- This will be emptied if an insert is done.	 * @access private	 */	function lastChunk( &$chunk ) {		$n = count( $chunk );		if( $n > 0 ) {			$this->insertChunk( $chunk );		}		$this->log( "100.00% done on $this->chunkTable (last chunk $n rows)." );	}	/**	 * Chunked inserts: perform an insert.	 * @param array &$chunk -- This will be emptied if an insert is done.	 * @access private	 */	function insertChunk( &$chunk ) {		// Give slaves a chance to catch up		wfWaitForSlaves( $this->maxLag );		$this->dbw->insert( $this->chunkTable, $chunk, $this->chunkFunction, $this->chunkOptions );	}	/**	 * Copy and transcode a table to table_temp.	 * @param string $name Base name of the source table	 * @param string $tabledef CREATE TABLE definition, w/ $1 for the name	 * @param array $fields set of destination fields to these constants:	 *              MW_UPGRADE_COPY   - straight copy	 *              MW_UPGRADE_ENCODE - for old Latin1 wikis, conv to UTF-8	 *              MW_UPGRADE_NULL   - just put NULL	 * @param callable $callback An optional callback to modify the data	 *                           or perform other processing. Func should be	 *                           ( object $row, array $copy ) and return $copy	 * @access private	 */	function copyTable( $name, $tabledef, $fields, $callback = null ) {		$fname = 'FiveUpgrade::copyTable';		$name_temp = $name . '_temp';		$this->log( "Migrating $name table to $name_temp..." );		$table      = $this->dbw->tableName( $name );		$table_temp = $this->dbw->tableName( $name_temp );		// Create temporary table; we're going to copy everything in there,		// then at the end rename the final tables into place.		$def = str_replace( '$1', $table_temp, $tabledef );		$this->dbw->query( $def, $fname );		$numRecords = $this->dbw->selectField( $name, 'COUNT(*)', '', $fname );		$this->setChunkScale( 100, $numRecords, $name_temp, $fname );		// Pull all records from the second, streaming database connection.		$sourceFields = array_keys( array_filter( $fields,			create_function( '$x', 'return $x !== MW_UPGRADE_NULL;' ) ) );		$result = $this->dbr->select( $name,			$sourceFields,			'',			$fname );		$add = array();		while( $row = $this->dbr->fetchObject( $result ) ) {			$copy = array();			foreach( $fields as $field => $source ) {				if( $source === MW_UPGRADE_COPY ) {					$copy[$field] = $row->$field;				} elseif( $source === MW_UPGRADE_ENCODE ) {					$copy[$field] = $this->conv( $row->$field );				} elseif( $source === MW_UPGRADE_NULL ) {					$copy[$field] = null;				} else {					$this->log( "Unknown field copy type: $field => $source" );				}			}			if( is_callable( $callback ) ) {				$copy = call_user_func( $callback, $row, $copy );			}			$add[] = $copy;			$this->addChunk( $add );		}		$this->lastChunk( $add );		$this->dbr->freeResult( $result );		$this->log( "Done converting $name." );		$this->cleanupSwaps[] = $name;	}	function upgradePage() {		$fname = "FiveUpgrade::upgradePage";		$chunksize = 100;		if( $this->dbw->tableExists( 'page' ) ) {			$this->log( 'Page table already exists; aborting.' );			die( -1 );		}		$this->log( "Checking cur table for unique title index and applying if necessary" );		checkDupes( true );		$this->log( "...converting from cur/old to page/revision/text DB structure." );		extract( $this->dbw->tableNames( 'cur', 'old', 'page', 'revision', 'text' ) );		$this->log( "Creating page and revision tables..." );		$this->dbw->query("CREATE TABLE $page (  			page_id int(8) unsigned NOT NULL auto_increment,  			page_namespace int NOT NULL,  			page_title varchar(255) binary NOT NULL,  			page_restrictions tinyblob NOT NULL default '',  			page_counter bigint(20) unsigned NOT NULL default '0',  			page_is_redirect tinyint(1) unsigned NOT NULL default '0',  			page_is_new tinyint(1) unsigned NOT NULL default '0',  			page_random real unsigned NOT NULL,  			page_touched char(14) binary NOT NULL default '',  			page_latest int(8) unsigned NOT NULL,  			page_len int(8) unsigned NOT NULL,  			PRIMARY KEY page_id (page_id),  			UNIQUE INDEX name_title (page_namespace,page_title),  			INDEX (page_random),  			INDEX (page_len)			) TYPE=InnoDB", $fname );		$this->dbw->query("CREATE TABLE $revision (  			rev_id int(8) unsigned NOT NULL auto_increment,  			rev_page int(8) unsigned NOT NULL,  			rev_text_id int(8) unsigned NOT NULL,  			rev_comment tinyblob NOT NULL default '',  			rev_user int(5) unsigned NOT NULL default '0',  			rev_user_text varchar(255) binary NOT NULL default '',  			rev_timestamp char(14) binary NOT NULL default '',  			rev_minor_edit tinyint(1) unsigned NOT NULL default '0',			rev_deleted tinyint(1) unsigned NOT NULL default '0',  			PRIMARY KEY rev_page_id (rev_page, rev_id),  			UNIQUE INDEX rev_id (rev_id),  			INDEX rev_timestamp (rev_timestamp),  			INDEX page_timestamp (rev_page,rev_timestamp),  			INDEX user_timestamp (rev_user,rev_timestamp),  			INDEX usertext_timestamp (rev_user_text,rev_timestamp)			) TYPE=InnoDB", $fname );		$maxold = intval( $this->dbw->selectField( 'old', 'max(old_id)', '', $fname ) );		$this->log( "Last old record is {$maxold}" );		global $wgLegacySchemaConversion;		if( $wgLegacySchemaConversion ) {			// Create HistoryBlobCurStub entries.			// Text will be pulled from the leftover 'cur' table at runtime.			echo "......Moving metadata from cur; using blob references to text in cur table.\n";			$cur_text = "concat('O:18:\"historyblobcurstub\":1:{s:6:\"mCurId\";i:',cur_id,';}')";			$cur_flags = "'object'";		} else {			// Copy all cur text in immediately: this may take longer but avoids			// having to keep an extra table around.			echo "......Moving text from cur.\n";			$cur_text = 'cur_text';			$cur_flags = "''";		}		$maxcur = $this->dbw->selectField( 'cur', 'max(cur_id)', '', $fname );		$this->log( "Last cur entry is $maxcur" );		/**		 * Copy placeholder records for each page's current version into old		 * Don't do any conversion here; text records are converted at runtime		 * based on the flags (and may be originally binary!) while the meta		 * fields will be converted in the old -> rev and cur -> page steps.		 */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?