r15031 - Code Review

From MediaWiki.org

Jump to: navigation, search
Repository:MediaWiki
Revision:r15030 | r15031 (on ViewVC) | r15032 >
Date:08:38, 25 June 2006
Author:tstarling
Status:new
Tags:
Comment:Defer initialisation of the special page list, allow autoloading of the SpecialPage class. SpecialPage.php is not loaded for ordinary page views. Implemented some interfaces which allow special pages to do the same thing, although backwards compatibility is maintained. Old special page extensions will load SpecialPage during initialisation, so all extensions will have to be ported to get the full performance benefit.
Modified paths:

Diff [purge]

Index: trunk/phase3/includes/Defines.php
===================================================================
--- trunk/phase3/includes/Defines.php	(revision 15030)
+++ trunk/phase3/includes/Defines.php	(revision 15031)
@@ -4,6 +4,11 @@
  * @package MediaWiki
  */
 
+/**
+ * Version constants for the benefit of extensions
+ */
+define( 'MW_SPECIALPAGE_VERSION', 2 );
+
 /**#@+
  * Database related constants
  */
Index: trunk/phase3/includes/GlobalFunctions.php
===================================================================
--- trunk/phase3/includes/GlobalFunctions.php	(revision 15030)
+++ trunk/phase3/includes/GlobalFunctions.php	(revision 15031)
@@ -1937,4 +1937,31 @@
 	return strrev( $outChars );
 }
 
+/**
+ * Create an object with a given name and an array of construct parameters
+ * @param string $name
+ * @param array $p parameters
+ */
+function wfCreateObject( $name, $p ){
+	$p = array_values( $p );
+	switch ( count( $p ) ) {
+		case 0:
+			return new $name;
+		case 1:
+			return new $name( $p[0] );
+		case 2:
+			return new $name( $p[0], $p[1] );
+		case 3:
+			return new $name( $p[0], $p[1], $p[2] );
+		case 4:
+			return new $name( $p[0], $p[1], $p[2], $p[3] );
+		case 5:
+			return new $name( $p[0], $p[1], $p[2], $p[3], $p[4] );
+		case 6:
+			return new $name( $p[0], $p[1], $p[2], $p[3], $p[4], $p[5] );
+		default:
+			throw new MWException( "Too many arguments to construtor in wfCreateObject" );
+	}
+}
+
 ?>
Index: trunk/phase3/includes/MessageCache.php
===================================================================
--- trunk/phase3/includes/MessageCache.php	(revision 15030)
+++ trunk/phase3/includes/MessageCache.php	(revision 15031)
@@ -315,6 +315,11 @@
 				$this->mCache[$uckey] = false;
 			}
 		}
+
+		# Make sure all extension messages are available
+		wfLoadAllExtensions();
+
+		# Add them to the cache
 		foreach ( $this->mExtensionMessages as $key => $value ) {
 			$uckey = $wgLang->ucfirst( $key );
 			if ( !array_key_exists( $uckey, $this->mCache ) ) {
Index: trunk/phase3/includes/Setup.php
===================================================================
--- trunk/phase3/includes/Setup.php	(revision 15030)
+++ trunk/phase3/includes/Setup.php	(revision 15031)
@@ -66,7 +66,6 @@
 require_once( 'HistoryBlob.php' );
 require_once( 'ProxyTools.php' );
 require_once( 'ObjectCache.php' );
-require_once( 'SpecialPage.php' );
 
 if ( $wgUseDynamicDates ) {
 	require_once( 'DateFormatter.php' );
Index: trunk/phase3/includes/AutoLoader.php
===================================================================
--- trunk/phase3/includes/AutoLoader.php	(revision 15030)
+++ trunk/phase3/includes/AutoLoader.php	(revision 15031)
@@ -1,8 +1,10 @@
 <?php
 
 /* This defines autoloading handler for whole MediaWiki framework */
-function __autoload($class_name) {
-	$classes = array(
+function __autoload($className) {
+	global $wgAutoloadClasses;
+
+	static $localClasses = array(
 		'AjaxDispatcher' => 'AjaxDispatcher.php',
 		'AjaxCachePolicy' => 'AjaxFunctions.php',
 		'Article' => 'Article.php',
@@ -218,11 +220,22 @@
 		'memcached' => 'memcached-client.php',
 		'UtfNormal' => 'normal/UtfNormal.php'
 	);
-	if (array_key_exists($class_name, $classes)) {
-		require($classes[$class_name]);
+	if ( isset( $localClasses[$className] ) ) {
+		require($localClasses[$className]);
+	} elseif ( isset( $wgAutoloadClasses[$className] ) ) {
+		require( $wgAutoloadClasses[$className] );
 	} else {
 		return false;
 	}
 }
 
+function wfLoadAllExtensions() {
+	global $wgAutoloadClasses;
+	foreach( $wgAutoloadClasses as $class => $file ) {
+		if ( ! class_exists( $class ) ) {
+			require( $file );
+		}
+	}
+}
+
 ?>
Index: trunk/phase3/includes/SpecialAllmessages.php
===================================================================
--- trunk/phase3/includes/SpecialAllmessages.php	(revision 15030)
+++ trunk/phase3/includes/SpecialAllmessages.php	(revision 15031)
@@ -26,6 +26,8 @@
 
 	$navText = wfMsg( 'allmessagestext' );
 
+	# Make sure all extension messages are available
+	wfLoadAllExtensions();
 
 	$first = true;
 	$sortedArray = array_merge( $wgAllMessagesEn, $wgMessageCache->mExtensionMessages );
Index: trunk/phase3/includes/DefaultSettings.php
===================================================================
--- trunk/phase3/includes/DefaultSettings.php	(revision 15030)
+++ trunk/phase3/includes/DefaultSettings.php	(revision 15031)
@@ -1535,10 +1535,34 @@
 /** Use XML parser? */
 $wgUseXMLparser = false ;
 
-/** Extensions */
-$wgSkinExtensionFunctions = array();
+/*****************************************************************************
+ *  Extensions 
+ */
+
+/**
+ * A list of callback functions which are called once MediaWiki is fully initialised
+ */
 $wgExtensionFunctions = array();
+
 /**
+ * Extension functions for initialisation of skins. This is called somewhat earlier 
+ * than $wgExtensionFunctions.
+ */
+$wgSkinExtensionFunctions = array();
+
+
+/**
+ * Special page list.
+ * See the top of SpecialPage.php for documentation.
+ */
+$wgSpecialPages = array(); 
+
+/**
+ * Array mapping class names to filenames, for autoloading.
+ */
+$wgAutoloadClasses = array();
+
+/**
  * An array of extension types and inside that their names, versions, authors
  * and urls, note that the version and url key can be omitted.
  *
@@ -1554,6 +1578,9 @@
  * Where $type is 'specialpage', 'parserhook', or 'other'.
  */
 $wgExtensionCredits = array();
+/*
+ * end extensions
+ ******************************************************************************/
 
 /**
  * Allow user Javascript page?
Index: trunk/phase3/includes/SpecialPage.php
===================================================================
--- trunk/phase3/includes/SpecialPage.php	(revision 15030)
+++ trunk/phase3/includes/SpecialPage.php	(revision 15031)
@@ -1,101 +1,30 @@
 <?php
 /**
- * SpecialPage: handling special pages and lists thereof
- * $wgSpecialPages is a list of all SpecialPage objects. These objects are
- * either instances of SpecialPage or a sub-class thereof. They have an
- * execute() method, which sends the HTML for the special page to $wgOut.
- * The parent class has an execute() method which distributes the call to
- * the historical global functions. Additionally, execute() also checks if the
- * user has the necessary access privileges and bails out if not.
+ * SpecialPage: handling special pages and lists thereof.
  *
- * To add a special page at run-time, use SpecialPage::addPage().
- * DO NOT manipulate this array at run-time.
+ * To add a special page in an extension, add to $wgSpecialPages either 
+ * an object instance or an array containing the name and constructor 
+ * parameters. The latter is preferred for performance reasons. 
  *
+ * The object instantiated must be either an instance of SpecialPage or a 
+ * sub-class thereof. It must have an execute() method, which sends the HTML 
+ * for the special page to $wgOut. The parent class has an execute() method 
+ * which distributes the call to the historical global functions. Additionally, 
+ * execute() also checks if the user has the necessary access privileges 
+ * and bails out if not.
+ *
+ * To add a core special page, use the similar static list in 
+ * SpecialPage::$mList. To remove a core static special page at runtime, use
+ * a SpecialPage_initList hook.
+ *
  * @package MediaWiki
  * @subpackage SpecialPage
  */
 
-
 /**
  * @access private
  */
-$wgSpecialPages = array(
-	'DoubleRedirects'	=> new SpecialPage ( 'DoubleRedirects' ),
-	'BrokenRedirects'	=> new SpecialPage ( 'BrokenRedirects' ),
-	'Disambiguations'	=> new SpecialPage ( 'Disambiguations' ),
 
-	'Userlogin'         => new SpecialPage( 'Userlogin' ),
-	'Userlogout'        => new UnlistedSpecialPage( 'Userlogout' ),
-	'Preferences'       => new SpecialPage( 'Preferences' ),
-	'Watchlist'         => new SpecialPage( 'Watchlist' ),
-
-	'Recentchanges'     => new IncludableSpecialPage( 'Recentchanges' ),
-	'Upload'            => new SpecialPage( 'Upload' ),
-	'Imagelist'         => new SpecialPage( 'Imagelist' ),
-	'Newimages'         => new IncludableSpecialPage( 'Newimages' ),
-	'Listusers'         => new SpecialPage( 'Listusers' ),
-	'Statistics'        => new SpecialPage( 'Statistics' ),
-	'Random'        => new SpecialPage( 'Randompage' ),
-	'Lonelypages'       => new SpecialPage( 'Lonelypages' ),
-	'Uncategorizedpages'=> new SpecialPage( 'Uncategorizedpages' ),
-	'Uncategorizedcategories'=> new SpecialPage( 'Uncategorizedcategories' ),
-	'Uncategorizedimages' => new SpecialPage( 'Uncategorizedimages' ),
-	'Unusedcategories'	=> new SpecialPage( 'Unusedcategories' ),
-	'Unusedimages'      => new SpecialPage( 'Unusedimages' ),
-	'Wantedpages'	=> new IncludableSpecialPage( 'Wantedpages' ),
-	'Wantedcategories' => new SpecialPage( 'Wantedcategories' ),
-	'Mostlinked'	=> new SpecialPage( 'Mostlinked' ),
-	'Mostlinkedcategories' => new SpecialPage( 'Mostlinkedcategories' ),
-	'Mostcategories' => new SpecialPage( 'Mostcategories' ),
-	'Mostimages' => new SpecialPage( 'Mostimages' ),
-	'Mostrevisions' => new SpecialPage( 'Mostrevisions' ),
-	'Shortpages'	=> new SpecialPage( 'Shortpages' ),
-	'Longpages'		=> new SpecialPage( 'Longpages' ),
-	'Newpages'		=> new IncludableSpecialPage( 'Newpages' ),
-	'Ancientpages'	=> new SpecialPage( 'Ancientpages' ),
-	'Deadendpages'  => new SpecialPage( 'Deadendpages' ),
-	'Allpages'		=> new IncludableSpecialPage( 'Allpages' ),
-	'Prefixindex'	=> new IncludableSpecialPage( 'Prefixindex' ) ,
-	'Ipblocklist'	=> new SpecialPage( 'Ipblocklist' ),
-	'Specialpages'  => new UnlistedSpecialPage( 'Specialpages' ),
-	'Contributions' => new UnlistedSpecialPage( 'Contributions' ),
-	'Emailuser'		=> new UnlistedSpecialPage( 'Emailuser' ),
-	'Whatlinkshere' => new UnlistedSpecialPage( 'Whatlinkshere' ),
-	'Recentchangeslinked' => new UnlistedSpecialPage( 'Recentchangeslinked' ),
-	'Movepage'		=> new UnlistedSpecialPage( 'Movepage' ),
-	'Blockme'       => new UnlistedSpecialPage( 'Blockme' ),
-	'Booksources'	=> new SpecialPage( 'Booksources' ),
-	'Categories'	=> new SpecialPage( 'Categories' ),
-	'Export'		=> new SpecialPage( 'Export' ),
-	'Version'		=> new SpecialPage( 'Version' ),
-	'Allmessages'	=> new SpecialPage( 'Allmessages' ),
-	'Log'           => new SpecialPage( 'Log' ),
-	'Blockip'		=> new SpecialPage( 'Blockip', 'block' ),
-	'Undelete'		=> new SpecialPage( 'Undelete', 'deletedhistory' ),
-	"Import"		=> new SpecialPage( "Import", 'import' ),
-	'Lockdb'		=> new SpecialPage( 'Lockdb', 'siteadmin' ),
-	'Unlockdb'		=> new SpecialPage( 'Unlockdb', 'siteadmin' ),
-	'Userrights'	=> new SpecialPage( 'Userrights', 'userrights' ),
-	'MIMEsearch'    => new SpecialPage( 'MIMEsearch' ),
-	'Unwatchedpages' => new SpecialPage( 'Unwatchedpages', 'unwatchedpages' ),
-	'Listredirects' => new SpecialPage( 'Listredirects' ),
-	'Revisiondelete' => new SpecialPage( 'Revisiondelete', 'deleterevision' ),
-	'Unusedtemplates' => new SpecialPage( 'Unusedtemplates' ),
-	'Randomredirect' => new SpecialPage( 'Randomredirect' ),
-);
-
-if( !$wgDisableCounters ) {
-	$wgSpecialPages['Popularpages'] = new SpecialPage( 'Popularpages' );
-}
-
-if( !$wgDisableInternalSearch ) {
-	$wgSpecialPages['Search'] = new SpecialPage( 'Search' );
-}
-
-if( $wgEmailAuthentication ) {
-	$wgSpecialPages['Confirmemail'] = new UnlistedSpecialPage( 'Confirmemail' );
-}
-
 /**
  * Parent special page class, also static functions for handling the special
  * page list
@@ -137,29 +66,144 @@
 	 */
 	var $mIncludable;
 
+	static public $mList = array(
+		'DoubleRedirects'	=> array( 'SpecialPage', 'DoubleRedirects' ),
+		'BrokenRedirects'	=> array( 'SpecialPage', 'BrokenRedirects' ),
+		'Disambiguations'	=> array( 'SpecialPage', 'Disambiguations' ),
 
+		'Userlogin'         => array( 'SpecialPage', 'Userlogin' ),
+		'Userlogout'        => array( 'UnlistedSpecialPage', 'Userlogout' ),
+		'Preferences'       => array( 'SpecialPage', 'Preferences' ),
+		'Watchlist'         => array( 'SpecialPage', 'Watchlist' ),
+
+		'Recentchanges'     => array( 'IncludableSpecialPage', 'Recentchanges' ),
+		'Upload'            => array( 'SpecialPage', 'Upload' ),
+		'Imagelist'         => array( 'SpecialPage', 'Imagelist' ),
+		'Newimages'         => array( 'IncludableSpecialPage', 'Newimages' ),
+		'Listusers'         => array( 'SpecialPage', 'Listusers' ),
+		'Statistics'        => array( 'SpecialPage', 'Statistics' ),
+		'Random'            => array( 'SpecialPage', 'Randompage' ),
+		'Lonelypages'       => array( 'SpecialPage', 'Lonelypages' ),
+		'Uncategorizedpages'=> array( 'SpecialPage', 'Uncategorizedpages' ),
+		'Uncategorizedcategories'=> array( 'SpecialPage', 'Uncategorizedcategories' ),
+		'Uncategorizedimages' => array( 'SpecialPage', 'Uncategorizedimages' ),
+		'Unusedcategories'	=> array( 'SpecialPage', 'Unusedcategories' ),
+		'Unusedimages'      => array( 'SpecialPage', 'Unusedimages' ),
+		'Wantedpages'	    => array( 'IncludableSpecialPage', 'Wantedpages' ),
+		'Wantedcategories'  => array( 'SpecialPage', 'Wantedcategories' ),
+		'Mostlinked'	    => array( 'SpecialPage', 'Mostlinked' ),
+		'Mostlinkedcategories' => array( 'SpecialPage', 'Mostlinkedcategories' ),
+		'Mostcategories'    => array( 'SpecialPage', 'Mostcategories' ),
+		'Mostimages'        => array( 'SpecialPage', 'Mostimages' ),
+		'Mostrevisions'     => array( 'SpecialPage', 'Mostrevisions' ),
+		'Shortpages'	    => array( 'SpecialPage', 'Shortpages' ),
+		'Longpages'		    => array( 'SpecialPage', 'Longpages' ),
+		'Newpages'		    => array( 'IncludableSpecialPage', 'Newpages' ),
+		'Ancientpages'	    => array( 'SpecialPage', 'Ancientpages' ),
+		'Deadendpages'      => array( 'SpecialPage', 'Deadendpages' ),
+		'Allpages'		    => array( 'IncludableSpecialPage', 'Allpages' ),
+		'Prefixindex'	    => array( 'IncludableSpecialPage', 'Prefixindex' ) ,
+		'Ipblocklist'	    => array( 'SpecialPage', 'Ipblocklist' ),
+		'Specialpages'      => array( 'UnlistedSpecialPage', 'Specialpages' ),
+		'Contributions'     => array( 'UnlistedSpecialPage', 'Contributions' ),
+		'Emailuser'		    => array( 'UnlistedSpecialPage', 'Emailuser' ),
+		'Whatlinkshere'     => array( 'UnlistedSpecialPage', 'Whatlinkshere' ),
+		'Recentchangeslinked' => array( 'UnlistedSpecialPage', 'Recentchangeslinked' ),
+		'Movepage'		    => array( 'UnlistedSpecialPage', 'Movepage' ),
+		'Blockme'           => array( 'UnlistedSpecialPage', 'Blockme' ),
+		'Booksources'	    => array( 'SpecialPage', 'Booksources' ),
+		'Categories'	    => array( 'SpecialPage', 'Categories' ),
+		'Export'		    => array( 'SpecialPage', 'Export' ),
+		'Version'		    => array( 'SpecialPage', 'Version' ),
+		'Allmessages'	    => array( 'SpecialPage', 'Allmessages' ),
+		'Log'               => array( 'SpecialPage', 'Log' ),
+		'Blockip'		    => array( 'SpecialPage', 'Blockip', 'block' ),
+		'Undelete'		    => array( 'SpecialPage', 'Undelete', 'deletedhistory' ),
+		"Import"		    => array( 'SpecialPage', "Import", 'import' ),
+		'Lockdb'		    => array( 'SpecialPage', 'Lockdb', 'siteadmin' ),
+		'Unlockdb'		    => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ),
+		'Userrights'	    => array( 'SpecialPage', 'Userrights', 'userrights' ),
+		'MIMEsearch'        => array( 'SpecialPage', 'MIMEsearch' ),
+		'Unwatchedpages'    => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ),
+		'Listredirects'     => array( 'SpecialPage', 'Listredirects' ),
+		'Revisiondelete'    => array( 'SpecialPage', 'Revisiondelete', 'deleterevision' ),
+		'Unusedtemplates'   => array( 'SpecialPage', 'Unusedtemplates' ),
+		'Randomredirect'    => array( 'SpecialPage', 'Randomredirect' ),
+	);
+
+	static public $mListInitialised = false;
+
 	/**#@-*/
 
+	/**
+	 * Initialise the special page list
+	 * This must be called before accessing SpecialPage::$mList
+	 */
+	static function initList() {
+		global $wgSpecialPages;
+		global $wgDisableCounters, $wgDisableInternalSearch, $wgEmailAuthentication;
 
+		#throw new MWException( __METHOD__ );
+
+		if ( self::$mListInitialised ) {
+			return;
+		}
+		wfProfileIn( __METHOD__ );
+		
+		if( !$wgDisableCounters ) {
+			self::$mList['Popularpages'] = array( 'SpecialPage', 'Popularpages' );
+		}
+
+		if( !$wgDisableInternalSearch ) {
+			self::$mList['Search'] = array( 'SpecialPage', 'Search' );
+		}
+
+		if( $wgEmailAuthentication ) {
+			self::$mList['Confirmemail'] = array( 'UnlistedSpecialPage', 'Confirmemail' );
+		}
+
+		# Add extension special pages
+		self::$mList = array_merge( self::$mList, $wgSpecialPages );
+
+		# Better to set this now, to avoid infinite recursion in carelessly written hooks
+		self::$mListInitialised = true;
+
+		# Run hooks
+		# This hook can be used to remove undesired built-in special pages
+		wfRunHooks( 'SpecialPage_initList', array( &self::$mList ) );
+		wfProfileOut( __METHOD__ );
+	}
+
 	/**
-	 * Add a page to the list of valid special pages
-	 * $obj->execute() must send HTML to $wgOut then return
-	 * Use this for a special page extension
+	 * Add a page to the list of valid special pages. This used to be the preferred 
+	 * method for adding special pages in extensions. It's now suggested that you add 
+	 * an associative record to $wgSpecialPages. This avoids autoloading SpecialPage.
+	 *
+	 * @param mixed $page Must either be an array specifying a class name and 
+	 *                    constructor parameters, or an object. The object,
+	 *                    when constructed, must have an execute() method which
+	 *                    sends HTML to $wgOut.
 	 * @static
 	 */
-	static function addPage( &$obj ) {
-		global $wgSpecialPages;
-		$wgSpecialPages[$obj->mName] = $obj;
+	static function addPage( &$page ) {
+		if ( !self::$mListInitialised ) {
+			self::initList();
+		}
+		self::$mList[$page->mName] = $page;
 	}
 
 	/**
 	 * Remove a special page from the list
-	 * Occasionally used to disable expensive or dangerous special pages
+	 * Formerly used to disable expensive or dangerous special pages. The 
+	 * preferred method is now to add a SpecialPage_initList hook.
+	 * 
 	 * @static
 	 */
 	static function removePage( $name ) {
-		global $wgSpecialPages;
-		unset( $wgSpecialPages[$name] );
+		if ( !self::$mListInitialised ) {
+			self::initList();
+		}
+		unset( self::$mList[$name] );
 	}
 
 	/**
@@ -168,14 +212,25 @@
 	 * @param string $name
 	 */
 	static function getPage( $name ) {
-		global $wgSpecialPages;
-		if ( array_key_exists( $name, $wgSpecialPages ) ) {
-			return $wgSpecialPages[$name];
+		if ( !self::$mListInitialised ) {
+			self::initList();
+		}
+		if ( array_key_exists( $name, self::$mList ) ) {
+			$rec = self::$mList[$name];
+			if ( is_string( $rec ) ) {
+				$className = $rec;
+				self::$mList[$name] = new $className;
+			} elseif ( is_array( $rec ) ) {
+				$className = array_shift( $rec );
+				self::$mList[$name] = wfCreateObject( $className, $rec );
+			}
+			return self::$mList[$name];
 		} else {
 			return NULL;
 		}
 	}
 
+
 	/**
 	 * @static
 	 * @param string $name
@@ -231,16 +286,19 @@
 	 * @static
 	 */
 	static function getPages() {
-		global $wgSpecialPages;
+		if ( !self::$mListInitialised ) {
+			self::initList();
+		}
 		$pages = array(
 		  '' => array(),
 		  'sysop' => array(),
 		  'developer' => array()
 		);
 
-		foreach ( $wgSpecialPages as $name => $page ) {
+		foreach ( self::$mList as $name => $rec ) {
+			$page = self::getPage( $name );
 			if ( $page->isListed() ) {
-				$pages[$page->getRestriction()][$page->getName()] =& $wgSpecialPages[$name];
+				$pages[$page->getRestriction()][$page->getName()] = $page;
 			}
 		}
 		return $pages;
Views
Toolbox