| Index: trunk/phase3/includes/OutputPage.php |
| — | — | @@ -2462,14 +2462,29 @@ |
| 2463 | 2463 | |
| 2464 | 2464 | $links = ''; |
| 2465 | 2465 | foreach ( $groups as $group => $modules ) { |
| 2466 | | - $query['modules'] = implode( '|', array_keys( $modules ) ); |
| 2467 | 2466 | // Special handling for user-specific groups |
| 2468 | 2467 | if ( ( $group === 'user' || $group === 'private' ) && $wgUser->isLoggedIn() ) { |
| 2469 | 2468 | $query['user'] = $wgUser->getName(); |
| 2470 | 2469 | } |
| | 2470 | + |
| | 2471 | + // Create a fake request based on the one we are about to make so modules return |
| | 2472 | + // correct timestamp and emptiness data |
| | 2473 | + $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) ); |
| | 2474 | + // Drop modules that know they're empty |
| | 2475 | + foreach ( $modules as $key => $module ) { |
| | 2476 | + if ( $module->isKnownEmpty( $context ) ) { |
| | 2477 | + unset( $modules[$key] ); |
| | 2478 | + } |
| | 2479 | + } |
| | 2480 | + // If there are no modules left, skip this group |
| | 2481 | + if ( $modules === array() ) { |
| | 2482 | + continue; |
| | 2483 | + } |
| | 2484 | + |
| | 2485 | + $query['modules'] = implode( '|', array_keys( $modules ) ); |
| | 2486 | + |
| 2471 | 2487 | // Support inlining of private modules if configured as such |
| 2472 | 2488 | if ( $group === 'private' && $wgResourceLoaderInlinePrivateModules ) { |
| 2473 | | - $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) ); |
| 2474 | 2489 | if ( $only == ResourceLoaderModule::TYPE_STYLES ) { |
| 2475 | 2490 | $links .= Html::inlineStyle( |
| 2476 | 2491 | $resourceLoader->makeModuleResponse( $context, $modules ) |
| — | — | @@ -2487,9 +2502,6 @@ |
| 2488 | 2503 | // on-wiki like site or user pages, or user preferences; we need to find the highest |
| 2489 | 2504 | // timestamp of these user-changable modules so we can ensure cache misses on change |
| 2490 | 2505 | if ( $group === 'user' || $group === 'site' ) { |
| 2491 | | - // Create a fake request based on the one we are about to make so modules return |
| 2492 | | - // correct times |
| 2493 | | - $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) ); |
| 2494 | 2506 | // Get the maximum timestamp |
| 2495 | 2507 | $timestamp = 1; |
| 2496 | 2508 | foreach ( $modules as $module ) { |
| Index: trunk/phase3/includes/resourceloader/ResourceLoaderModule.php |
| — | — | @@ -279,4 +279,17 @@ |
| 280 | 280 | // 0 would mean now |
| 281 | 281 | return 1; |
| 282 | 282 | } |
| | 283 | + |
| | 284 | + /** |
| | 285 | + * Check whether this module is known to be empty. If a child class |
| | 286 | + * has an easy and cheap way to determine that this module is |
| | 287 | + * definitely going to be empty, it should override this method to |
| | 288 | + * return true in that case. Callers may optimize the request for this |
| | 289 | + * module away if this function returns true. |
| | 290 | + * @param $context ResourceLoaderContext: Context object |
| | 291 | + * @return Boolean |
| | 292 | + */ |
| | 293 | + public function isKnownEmpty( ResourceLoaderContext $context ) { |
| | 294 | + return false; |
| | 295 | + } |
| 283 | 296 | } |
| Index: trunk/phase3/includes/resourceloader/ResourceLoaderWikiModule.php |
| — | — | @@ -36,8 +36,8 @@ |
| 37 | 37 | # Origin is user-supplied code |
| 38 | 38 | protected $origin = self::ORIGIN_USER_SITEWIDE; |
| 39 | 39 | |
| 40 | | - // In-object cache for modified time |
| 41 | | - protected $modifiedTime = array(); |
| | 40 | + // In-object cache for title mtimes |
| | 41 | + protected $titleMtimes = array(); |
| 42 | 42 | |
| 43 | 43 | /* Abstract Protected Methods */ |
| 44 | 44 | |
| — | — | @@ -120,32 +120,18 @@ |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | 123 | public function getModifiedTime( ResourceLoaderContext $context ) { |
| 124 | | - $hash = $context->getHash(); |
| 125 | | - if ( isset( $this->modifiedTime[$hash] ) ) { |
| 126 | | - return $this->modifiedTime[$hash]; |
| 127 | | - } |
| 128 | | - |
| 129 | | - $batch = new LinkBatch; |
| 130 | | - foreach ( $this->getPages( $context ) as $titleText => $options ) { |
| 131 | | - $batch->addObj( Title::newFromText( $titleText ) ); |
| 132 | | - } |
| 133 | | - |
| 134 | 124 | $modifiedTime = 1; // wfTimestamp() interprets 0 as "now" |
| 135 | | - if ( !$batch->isEmpty() ) { |
| 136 | | - $dbr = wfGetDB( DB_SLAVE ); |
| 137 | | - $latest = $dbr->selectField( 'page', 'MAX(page_touched)', |
| 138 | | - $batch->constructSet( 'page', $dbr ), |
| 139 | | - __METHOD__ ); |
| 140 | | - |
| 141 | | - if ( $latest ) { |
| 142 | | - $modifiedTime = wfTimestamp( TS_UNIX, $latest ); |
| 143 | | - } |
| | 125 | + $mtimes = $this->getTitleMtimes( $context ); |
| | 126 | + if ( count( $mtimes ) ) { |
| | 127 | + $modifiedTime = max( $modifiedTime, max( $mtimes ) ); |
| 144 | 128 | } |
| 145 | | - |
| 146 | | - $this->modifiedTime[$hash] = $modifiedTime; |
| 147 | 129 | return $modifiedTime; |
| 148 | 130 | } |
| 149 | | - |
| | 131 | + |
| | 132 | + public function isKnownEmpty( ResourceLoaderContext $context ) { |
| | 133 | + return count( $this->getTitleMtimes( $context ) ) == 0; |
| | 134 | + } |
| | 135 | + |
| 150 | 136 | /** |
| 151 | 137 | * @param $context ResourceLoaderContext |
| 152 | 138 | * @return bool |
| — | — | @@ -155,4 +141,38 @@ |
| 156 | 142 | |
| 157 | 143 | return $wgContLang->getDir() !== $context->getDirection(); |
| 158 | 144 | } |
| | 145 | + |
| | 146 | + /** |
| | 147 | + * Get the modification times of all titles that would be loaded for |
| | 148 | + * a given context. |
| | 149 | + * @param $context ResourceLoaderContext: Context object |
| | 150 | + * @return array( prefixed DB key => UNIX timestamp ), nonexistent titles are dropped |
| | 151 | + */ |
| | 152 | + protected function getTitleMtimes( ResourceLoaderContext $context ) { |
| | 153 | + $hash = $context->getHash(); |
| | 154 | + if ( isset( $this->titleMtimes[$hash] ) ) { |
| | 155 | + return $this->titleMtimes[$hash]; |
| | 156 | + } |
| | 157 | + |
| | 158 | + $this->titleMtimes[$hash] = array(); |
| | 159 | + $batch = new LinkBatch; |
| | 160 | + foreach ( $this->getPages( $context ) as $titleText => $options ) { |
| | 161 | + $batch->addObj( Title::newFromText( $titleText ) ); |
| | 162 | + } |
| | 163 | + |
| | 164 | + if ( !$batch->isEmpty() ) { |
| | 165 | + $dbr = wfGetDB( DB_SLAVE ); |
| | 166 | + $res = $dbr->select( 'page', |
| | 167 | + array( 'page_namespace', 'page_title', 'page_touched' ), |
| | 168 | + $batch->constructSet( 'page', $dbr ), |
| | 169 | + __METHOD__ |
| | 170 | + ); |
| | 171 | + foreach ( $res as $row ) { |
| | 172 | + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); |
| | 173 | + $this->titleMtimes[$hash][$title->getPrefixedDBkey()] = |
| | 174 | + wfTimestamp( TS_UNIX, $row->page_touched ); |
| | 175 | + } |
| | 176 | + } |
| | 177 | + return $this->titleMtimes[$hash]; |
| | 178 | + } |
| 159 | 179 | } |