MediaWiki
REL1_24
|
00001 <?php 00028 abstract class ResourceLoaderModule { 00029 # Type of resource 00030 const TYPE_SCRIPTS = 'scripts'; 00031 const TYPE_STYLES = 'styles'; 00032 const TYPE_MESSAGES = 'messages'; 00033 const TYPE_COMBINED = 'combined'; 00034 00035 # sitewide core module like a skin file or jQuery component 00036 const ORIGIN_CORE_SITEWIDE = 1; 00037 00038 # per-user module generated by the software 00039 const ORIGIN_CORE_INDIVIDUAL = 2; 00040 00041 # sitewide module generated from user-editable files, like MediaWiki:Common.js, or 00042 # modules accessible to multiple users, such as those generated by the Gadgets extension. 00043 const ORIGIN_USER_SITEWIDE = 3; 00044 00045 # per-user module generated from user-editable files, like User:Me/vector.js 00046 const ORIGIN_USER_INDIVIDUAL = 4; 00047 00048 # an access constant; make sure this is kept as the largest number in this group 00049 const ORIGIN_ALL = 10; 00050 00051 # script and style modules form a hierarchy of trustworthiness, with core modules like 00052 # skins and jQuery as most trustworthy, and user scripts as least trustworthy. We can 00053 # limit the types of scripts and styles we allow to load on, say, sensitive special 00054 # pages like Special:UserLogin and Special:Preferences 00055 protected $origin = self::ORIGIN_CORE_SITEWIDE; 00056 00057 /* Protected Members */ 00058 00059 protected $name = null; 00060 protected $targets = array( 'desktop' ); 00061 00062 // In-object cache for file dependencies 00063 protected $fileDeps = array(); 00064 // In-object cache for message blob mtime 00065 protected $msgBlobMtime = array(); 00066 00070 protected $config; 00071 00072 /* Methods */ 00073 00080 public function getName() { 00081 return $this->name; 00082 } 00083 00090 public function setName( $name ) { 00091 $this->name = $name; 00092 } 00093 00101 public function getOrigin() { 00102 return $this->origin; 00103 } 00104 00111 public function setOrigin( $origin ) { 00112 $this->origin = $origin; 00113 } 00114 00119 public function getFlip( $context ) { 00120 global $wgContLang; 00121 00122 return $wgContLang->getDir() !== $context->getDirection(); 00123 } 00124 00132 public function getScript( ResourceLoaderContext $context ) { 00133 // Stub, override expected 00134 return ''; 00135 } 00136 00141 public function getConfig() { 00142 if ( $this->config === null ) { 00143 // Ugh, fall back to default 00144 $this->config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' ); 00145 } 00146 00147 return $this->config; 00148 } 00149 00154 public function setConfig( Config $config ) { 00155 $this->config = $config; 00156 } 00157 00172 public function getScriptURLsForDebug( ResourceLoaderContext $context ) { 00173 $resourceLoader = $context->getResourceLoader(); 00174 $derivative = new DerivativeResourceLoaderContext( $context ); 00175 $derivative->setModules( array( $this->getName() ) ); 00176 $derivative->setOnly( 'scripts' ); 00177 $derivative->setDebug( true ); 00178 00179 $url = $resourceLoader->createLoaderURL( 00180 $this->getSource(), 00181 $derivative 00182 ); 00183 00184 return array( $url ); 00185 } 00186 00193 public function supportsURLLoading() { 00194 return true; 00195 } 00196 00205 public function getStyles( ResourceLoaderContext $context ) { 00206 // Stub, override expected 00207 return array(); 00208 } 00209 00219 public function getStyleURLsForDebug( ResourceLoaderContext $context ) { 00220 $resourceLoader = $context->getResourceLoader(); 00221 $derivative = new DerivativeResourceLoaderContext( $context ); 00222 $derivative->setModules( array( $this->getName() ) ); 00223 $derivative->setOnly( 'styles' ); 00224 $derivative->setDebug( true ); 00225 00226 $url = $resourceLoader->createLoaderURL( 00227 $this->getSource(), 00228 $derivative 00229 ); 00230 00231 return array( 'all' => array( $url ) ); 00232 } 00233 00241 public function getMessages() { 00242 // Stub, override expected 00243 return array(); 00244 } 00245 00251 public function getGroup() { 00252 // Stub, override expected 00253 return null; 00254 } 00255 00261 public function getSource() { 00262 // Stub, override expected 00263 return 'local'; 00264 } 00265 00273 public function getPosition() { 00274 return 'bottom'; 00275 } 00276 00284 public function isRaw() { 00285 return false; 00286 } 00287 00293 public function getLoaderScript() { 00294 // Stub, override expected 00295 return false; 00296 } 00297 00308 public function getDependencies() { 00309 // Stub, override expected 00310 return array(); 00311 } 00312 00318 public function getTargets() { 00319 return $this->targets; 00320 } 00321 00336 public function getSkipFunction() { 00337 return null; 00338 } 00339 00347 public function getFileDependencies( $skin ) { 00348 // Try in-object cache first 00349 if ( isset( $this->fileDeps[$skin] ) ) { 00350 return $this->fileDeps[$skin]; 00351 } 00352 00353 $dbr = wfGetDB( DB_SLAVE ); 00354 $deps = $dbr->selectField( 'module_deps', 'md_deps', array( 00355 'md_module' => $this->getName(), 00356 'md_skin' => $skin, 00357 ), __METHOD__ 00358 ); 00359 if ( !is_null( $deps ) ) { 00360 $this->fileDeps[$skin] = (array)FormatJson::decode( $deps, true ); 00361 } else { 00362 $this->fileDeps[$skin] = array(); 00363 } 00364 return $this->fileDeps[$skin]; 00365 } 00366 00373 public function setFileDependencies( $skin, $deps ) { 00374 $this->fileDeps[$skin] = $deps; 00375 } 00376 00383 public function getMsgBlobMtime( $lang ) { 00384 if ( !isset( $this->msgBlobMtime[$lang] ) ) { 00385 if ( !count( $this->getMessages() ) ) { 00386 return 0; 00387 } 00388 00389 $dbr = wfGetDB( DB_SLAVE ); 00390 $msgBlobMtime = $dbr->selectField( 'msg_resource', 'mr_timestamp', array( 00391 'mr_resource' => $this->getName(), 00392 'mr_lang' => $lang 00393 ), __METHOD__ 00394 ); 00395 // If no blob was found, but the module does have messages, that means we need 00396 // to regenerate it. Return NOW 00397 if ( $msgBlobMtime === false ) { 00398 $msgBlobMtime = wfTimestampNow(); 00399 } 00400 $this->msgBlobMtime[$lang] = wfTimestamp( TS_UNIX, $msgBlobMtime ); 00401 } 00402 return $this->msgBlobMtime[$lang]; 00403 } 00404 00411 public function setMsgBlobMtime( $lang, $mtime ) { 00412 $this->msgBlobMtime[$lang] = $mtime; 00413 } 00414 00415 /* Abstract Methods */ 00416 00435 public function getModifiedTime( ResourceLoaderContext $context ) { 00436 // 0 would mean now 00437 return 1; 00438 } 00439 00447 public function getHashMtime( ResourceLoaderContext $context ) { 00448 $hash = $this->getModifiedHash( $context ); 00449 if ( !is_string( $hash ) ) { 00450 return 0; 00451 } 00452 00453 $cache = wfGetCache( CACHE_ANYTHING ); 00454 $key = wfMemcKey( 'resourceloader', 'modulemodifiedhash', $this->getName(), $hash ); 00455 00456 $data = $cache->get( $key ); 00457 if ( is_array( $data ) && $data['hash'] === $hash ) { 00458 // Hash is still the same, re-use the timestamp of when we first saw this hash. 00459 return $data['timestamp']; 00460 } 00461 00462 $timestamp = wfTimestamp(); 00463 $cache->set( $key, array( 00464 'hash' => $hash, 00465 'timestamp' => $timestamp, 00466 ) ); 00467 00468 return $timestamp; 00469 } 00470 00480 public function getModifiedHash( ResourceLoaderContext $context ) { 00481 return null; 00482 } 00483 00493 public function getDefinitionMtime( ResourceLoaderContext $context ) { 00494 wfProfileIn( __METHOD__ ); 00495 $summary = $this->getDefinitionSummary( $context ); 00496 if ( $summary === null ) { 00497 wfProfileOut( __METHOD__ ); 00498 return 0; 00499 } 00500 00501 $hash = md5( json_encode( $summary ) ); 00502 00503 $cache = wfGetCache( CACHE_ANYTHING ); 00504 00505 // Embed the hash itself in the cache key. This allows for a few nifty things: 00506 // - During deployment, servers with old and new versions of the code communicating 00507 // with the same memcached will not override the same key repeatedly increasing 00508 // the timestamp. 00509 // - In case of the definition changing and then changing back in a short period of time 00510 // (e.g. in case of a revert or a corrupt server) the old timestamp and client-side cache 00511 // url will be re-used. 00512 // - If different context-combinations (e.g. same skin, same language or some combination 00513 // thereof) result in the same definition, they will use the same hash and timestamp. 00514 $key = wfMemcKey( 'resourceloader', 'moduledefinition', $this->getName(), $hash ); 00515 00516 $data = $cache->get( $key ); 00517 if ( is_int( $data ) && $data > 0 ) { 00518 // We've seen this hash before, re-use the timestamp of when we first saw it. 00519 wfProfileOut( __METHOD__ ); 00520 return $data; 00521 } 00522 00523 wfDebugLog( 'resourceloader', __METHOD__ . ": New definition hash for module " 00524 . "{$this->getName()} in context {$context->getHash()}: $hash." ); 00525 00526 $timestamp = time(); 00527 $cache->set( $key, $timestamp ); 00528 00529 wfProfileOut( __METHOD__ ); 00530 return $timestamp; 00531 } 00532 00560 public function getDefinitionSummary( ResourceLoaderContext $context ) { 00561 return array( 00562 'class' => get_class( $this ), 00563 ); 00564 } 00565 00575 public function isKnownEmpty( ResourceLoaderContext $context ) { 00576 return false; 00577 } 00578 00580 private static $jsParser; 00581 private static $parseCacheVersion = 1; 00582 00591 protected function validateScriptFile( $fileName, $contents ) { 00592 if ( $this->getConfig()->get( 'ResourceLoaderValidateJS' ) ) { 00593 // Try for cache hit 00594 // Use CACHE_ANYTHING since filtering is very slow compared to DB queries 00595 $key = wfMemcKey( 'resourceloader', 'jsparse', self::$parseCacheVersion, md5( $contents ) ); 00596 $cache = wfGetCache( CACHE_ANYTHING ); 00597 $cacheEntry = $cache->get( $key ); 00598 if ( is_string( $cacheEntry ) ) { 00599 return $cacheEntry; 00600 } 00601 00602 $parser = self::javaScriptParser(); 00603 try { 00604 $parser->parse( $contents, $fileName, 1 ); 00605 $result = $contents; 00606 } catch ( Exception $e ) { 00607 // We'll save this to cache to avoid having to validate broken JS over and over... 00608 $err = $e->getMessage(); 00609 $result = "throw new Error(" . Xml::encodeJsVar( "JavaScript parse error: $err" ) . ");"; 00610 } 00611 00612 $cache->set( $key, $result ); 00613 return $result; 00614 } else { 00615 return $contents; 00616 } 00617 } 00618 00622 protected static function javaScriptParser() { 00623 if ( !self::$jsParser ) { 00624 self::$jsParser = new JSParser(); 00625 } 00626 return self::$jsParser; 00627 } 00628 00635 protected static function safeFilemtime( $filename ) { 00636 if ( file_exists( $filename ) ) { 00637 return filemtime( $filename ); 00638 } else { 00639 // We only ever map this function on an array if we're gonna call max() after, 00640 // so return our standard minimum timestamps here. This is 1, not 0, because 00641 // wfTimestamp(0) == NOW 00642 return 1; 00643 } 00644 } 00645 }