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 | } |