From adaf1951481a3bd3620064ff69c87c80ea3ebe92 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Sun, 25 Jun 2006 08:38:17 +0000 Subject: [PATCH] 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. --- includes/AutoLoader.php | 21 ++- includes/DefaultSettings.php | 31 +++- includes/Defines.php | 5 + includes/GlobalFunctions.php | 27 ++++ includes/MessageCache.php | 5 + includes/Setup.php | 1 - includes/SpecialAllmessages.php | 2 + includes/SpecialPage.php | 260 +++++++++++++++++++------------- 8 files changed, 244 insertions(+), 108 deletions(-) diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 019d2d97e4..5d66ea9db1 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -1,8 +1,10 @@ 'AjaxDispatcher.php', 'AjaxCachePolicy' => 'AjaxFunctions.php', 'Article' => 'Article.php', @@ -218,11 +220,22 @@ function __autoload($class_name) { '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 ); + } + } +} + ?> diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 3202bf5f60..b5ec472b19 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -1535,9 +1535,33 @@ $wgAllowRealName = true; /** 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 @@ $wgExtensionFunctions = array(); * Where $type is 'specialpage', 'parserhook', or 'other'. */ $wgExtensionCredits = array(); +/* + * end extensions + ******************************************************************************/ /** * Allow user Javascript page? diff --git a/includes/Defines.php b/includes/Defines.php index e684625cd0..9ff8303bea 100644 --- a/includes/Defines.php +++ b/includes/Defines.php @@ -4,6 +4,11 @@ * @package MediaWiki */ +/** + * Version constants for the benefit of extensions + */ +define( 'MW_SPECIALPAGE_VERSION', 2 ); + /**#@+ * Database related constants */ diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index e8f721f156..d1a23943b8 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -1937,4 +1937,31 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) { 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" ); + } +} + ?> diff --git a/includes/MessageCache.php b/includes/MessageCache.php index ed9e5ec6da..1e6fe8ba46 100644 --- a/includes/MessageCache.php +++ b/includes/MessageCache.php @@ -315,6 +315,11 @@ class MessageCache { $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 ) ) { diff --git a/includes/Setup.php b/includes/Setup.php index 390114227c..ea70968df1 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -66,7 +66,6 @@ require_once( 'LoadBalancer.php' ); require_once( 'HistoryBlob.php' ); require_once( 'ProxyTools.php' ); require_once( 'ObjectCache.php' ); -require_once( 'SpecialPage.php' ); if ( $wgUseDynamicDates ) { require_once( 'DateFormatter.php' ); diff --git a/includes/SpecialAllmessages.php b/includes/SpecialAllmessages.php index 926a06245c..ccb5883641 100644 --- a/includes/SpecialAllmessages.php +++ b/includes/SpecialAllmessages.php @@ -26,6 +26,8 @@ function wfSpecialAllmessages() { $navText = wfMsg( 'allmessagestext' ); + # Make sure all extension messages are available + wfLoadAllExtensions(); $first = true; $sortedArray = array_merge( $wgAllMessagesEn, $wgMessageCache->mExtensionMessages ); diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php index 5ab0987d33..10beb276b9 100644 --- a/includes/SpecialPage.php +++ b/includes/SpecialPage.php @@ -1,100 +1,29 @@ 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 @@ -137,29 +66,144 @@ class SpecialPage */ 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 @@ class SpecialPage * @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 @@ class SpecialPage * @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; -- 2.20.1