/**
  * Output a response to a load request, including the content-type header.
  *
  * @param ResourceLoaderContext $context Context in which a response should be formed
  */
 public function respond(ResourceLoaderContext $context)
 {
     // Use file cache if enabled and available...
     if ($this->config->get('UseFileCache')) {
         $fileCache = ResourceFileCache::newFromContext($context);
         if ($this->tryRespondFromFileCache($fileCache, $context)) {
             return;
             // output handled
         }
     }
     // Buffer output to catch warnings. Normally we'd use ob_clean() on the
     // top-level output buffer to clear warnings, but that breaks when ob_gzhandler
     // is used: ob_clean() will clear the GZIP header in that case and it won't come
     // back for subsequent output, resulting in invalid GZIP. So we have to wrap
     // the whole thing in our own output buffer to be sure the active buffer
     // doesn't use ob_gzhandler.
     // See http://bugs.php.net/bug.php?id=36514
     ob_start();
     // Find out which modules are missing and instantiate the others
     $modules = array();
     $missing = array();
     foreach ($context->getModules() as $name) {
         $module = $this->getModule($name);
         if ($module) {
             // Do not allow private modules to be loaded from the web.
             // This is a security issue, see bug 34907.
             if ($module->getGroup() === 'private') {
                 wfDebugLog('resourceloader', __METHOD__ . ": request for private module '{$name}' denied");
                 $this->errors[] = "Cannot show private module \"{$name}\"";
                 continue;
             }
             $modules[$name] = $module;
         } else {
             $missing[] = $name;
         }
     }
     // Preload information needed to the mtime calculation below
     try {
         $this->preloadModuleInfo(array_keys($modules), $context);
     } catch (Exception $e) {
         MWExceptionHandler::logException($e);
         wfDebugLog('resourceloader', __METHOD__ . ": preloading module info failed: {$e}");
         $this->errors[] = self::formatExceptionNoComment($e);
     }
     // To send Last-Modified and support If-Modified-Since, we need to detect
     // the last modified time
     $mtime = wfTimestamp(TS_UNIX, $this->config->get('CacheEpoch'));
     foreach ($modules as $module) {
         /**
          * @var $module ResourceLoaderModule
          */
         try {
             // Calculate maximum modified time
             $mtime = max($mtime, $module->getModifiedTime($context));
         } catch (Exception $e) {
             MWExceptionHandler::logException($e);
             wfDebugLog('resourceloader', __METHOD__ . ": calculating maximum modified time failed: {$e}");
             $this->errors[] = self::formatExceptionNoComment($e);
         }
     }
     // If there's an If-Modified-Since header, respond with a 304 appropriately
     if ($this->tryRespondLastModified($context, $mtime)) {
         return;
         // output handled (buffers cleared)
     }
     // Generate a response
     $response = $this->makeModuleResponse($context, $modules, $missing);
     // Capture any PHP warnings from the output buffer and append them to the
     // error list if we're in debug mode.
     if ($context->getDebug() && strlen($warnings = ob_get_contents())) {
         $this->errors[] = $warnings;
     }
     // Save response to file cache unless there are errors
     if (isset($fileCache) && !$this->errors && !count($missing)) {
         // Cache single modules and images...and other requests if there are enough hits
         if (ResourceFileCache::useFileCache($context)) {
             if ($fileCache->isCacheWorthy()) {
                 $fileCache->saveText($response);
             } else {
                 $fileCache->incrMissesRecent($context->getRequest());
             }
         }
     }
     // Send content type and cache related headers
     $this->sendResponseHeaders($context, $mtime, (bool) $this->errors);
     // Remove the output buffer and output the response
     ob_end_clean();
     if ($context->getImageObj() && $this->errors) {
         // We can't show both the error messages and the response when it's an image.
         $errorText = '';
         foreach ($this->errors as $error) {
             $errorText .= $error . "\n";
         }
         $response = $errorText;
     } elseif ($this->errors) {
         // Prepend comments indicating errors
         $errorText = '';
         foreach ($this->errors as $error) {
             $errorText .= self::makeComment($error);
         }
         $response = $errorText . $response;
     }
     $this->errors = array();
     echo $response;
 }
Exemple #2
0
 /**
  * Output a response to a load request, including the content-type header.
  *
  * @param ResourceLoaderContext $context Context in which a response should be formed
  */
 public function respond(ResourceLoaderContext $context)
 {
     // Buffer output to catch warnings. Normally we'd use ob_clean() on the
     // top-level output buffer to clear warnings, but that breaks when ob_gzhandler
     // is used: ob_clean() will clear the GZIP header in that case and it won't come
     // back for subsequent output, resulting in invalid GZIP. So we have to wrap
     // the whole thing in our own output buffer to be sure the active buffer
     // doesn't use ob_gzhandler.
     // See http://bugs.php.net/bug.php?id=36514
     ob_start();
     // Find out which modules are missing and instantiate the others
     $modules = array();
     $missing = array();
     foreach ($context->getModules() as $name) {
         $module = $this->getModule($name);
         if ($module) {
             // Do not allow private modules to be loaded from the web.
             // This is a security issue, see bug 34907.
             if ($module->getGroup() === 'private') {
                 $this->logger->debug("Request for private module '{$name}' denied");
                 $this->errors[] = "Cannot show private module \"{$name}\"";
                 continue;
             }
             $modules[$name] = $module;
         } else {
             $missing[] = $name;
         }
     }
     try {
         // Preload for getCombinedVersion()
         $this->preloadModuleInfo(array_keys($modules), $context);
     } catch (Exception $e) {
         MWExceptionHandler::logException($e);
         $this->logger->warning('Preloading module info failed: {exception}', array('exception' => $e));
         $this->errors[] = self::formatExceptionNoComment($e);
     }
     // Combine versions to propagate cache invalidation
     $versionHash = '';
     try {
         $versionHash = $this->getCombinedVersion($context, array_keys($modules));
     } catch (Exception $e) {
         MWExceptionHandler::logException($e);
         $this->logger->warning('Calculating version hash failed: {exception}', array('exception' => $e));
         $this->errors[] = self::formatExceptionNoComment($e);
     }
     // See RFC 2616 § 3.11 Entity Tags
     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11
     $etag = 'W/"' . $versionHash . '"';
     // Try the client-side cache first
     if ($this->tryRespondNotModified($context, $etag)) {
         return;
         // output handled (buffers cleared)
     }
     // Use file cache if enabled and available...
     if ($this->config->get('UseFileCache')) {
         $fileCache = ResourceFileCache::newFromContext($context);
         if ($this->tryRespondFromFileCache($fileCache, $context, $etag)) {
             return;
             // output handled
         }
     }
     // Generate a response
     $response = $this->makeModuleResponse($context, $modules, $missing);
     // Capture any PHP warnings from the output buffer and append them to the
     // error list if we're in debug mode.
     if ($context->getDebug() && strlen($warnings = ob_get_contents())) {
         $this->errors[] = $warnings;
     }
     // Save response to file cache unless there are errors
     if (isset($fileCache) && !$this->errors && !count($missing)) {
         // Cache single modules and images...and other requests if there are enough hits
         if (ResourceFileCache::useFileCache($context)) {
             if ($fileCache->isCacheWorthy()) {
                 $fileCache->saveText($response);
             } else {
                 $fileCache->incrMissesRecent($context->getRequest());
             }
         }
     }
     $this->sendResponseHeaders($context, $etag, (bool) $this->errors);
     // Remove the output buffer and output the response
     ob_end_clean();
     if ($context->getImageObj() && $this->errors) {
         // We can't show both the error messages and the response when it's an image.
         $errorText = '';
         foreach ($this->errors as $error) {
             $errorText .= $error . "\n";
         }
         $response = $errorText;
     } elseif ($this->errors) {
         // Prepend comments indicating errors
         $errorText = '';
         foreach ($this->errors as $error) {
             $errorText .= self::makeComment($error);
         }
         $response = $errorText . $response;
     }
     $this->errors = array();
     echo $response;
 }
Exemple #3
0
 /**
  * Outputs a response to a resource load-request, including a content-type header.
  *
  * @param $context ResourceLoaderContext: Context in which a response should be formed
  */
 public function respond(ResourceLoaderContext $context)
 {
     global $wgCacheEpoch, $wgUseFileCache;
     // Use file cache if enabled and available...
     if ($wgUseFileCache) {
         $fileCache = ResourceFileCache::newFromContext($context);
         if ($this->tryRespondFromFileCache($fileCache, $context)) {
             return;
             // output handled
         }
     }
     // Buffer output to catch warnings. Normally we'd use ob_clean() on the
     // top-level output buffer to clear warnings, but that breaks when ob_gzhandler
     // is used: ob_clean() will clear the GZIP header in that case and it won't come
     // back for subsequent output, resulting in invalid GZIP. So we have to wrap
     // the whole thing in our own output buffer to be sure the active buffer
     // doesn't use ob_gzhandler.
     // See http://bugs.php.net/bug.php?id=36514
     ob_start();
     wfProfileIn(__METHOD__);
     $errors = '';
     // Split requested modules into two groups, modules and missing
     $modules = array();
     $missing = array();
     foreach ($context->getModules() as $name) {
         if (isset($this->moduleInfos[$name])) {
             $module = $this->getModule($name);
             // Do not allow private modules to be loaded from the web.
             // This is a security issue, see bug 34907.
             if ($module->getGroup() === 'private') {
                 $errors .= $this->makeComment("Cannot show private module \"{$name}\"");
                 continue;
             }
             $modules[$name] = $this->getModule($name);
         } else {
             $missing[] = $name;
         }
     }
     // Preload information needed to the mtime calculation below
     try {
         $this->preloadModuleInfo(array_keys($modules), $context);
     } catch (Exception $e) {
         // Add exception to the output as a comment
         $errors .= $this->makeComment($e->__toString());
     }
     wfProfileIn(__METHOD__ . '-getModifiedTime');
     // To send Last-Modified and support If-Modified-Since, we need to detect
     // the last modified time
     $mtime = wfTimestamp(TS_UNIX, $wgCacheEpoch);
     foreach ($modules as $module) {
         /**
          * @var $module ResourceLoaderModule
          */
         try {
             // Calculate maximum modified time
             $mtime = max($mtime, $module->getModifiedTime($context));
         } catch (Exception $e) {
             // Add exception to the output as a comment
             $errors .= $this->makeComment($e->__toString());
         }
     }
     wfProfileOut(__METHOD__ . '-getModifiedTime');
     // Send content type and cache related headers
     $this->sendResponseHeaders($context, $mtime);
     // If there's an If-Modified-Since header, respond with a 304 appropriately
     if ($this->tryRespondLastModified($context, $mtime)) {
         wfProfileOut(__METHOD__);
         return;
         // output handled (buffers cleared)
     }
     // Generate a response
     $response = $this->makeModuleResponse($context, $modules, $missing);
     // Prepend comments indicating exceptions
     $response = $errors . $response;
     // Capture any PHP warnings from the output buffer and append them to the
     // response in a comment if we're in debug mode.
     if ($context->getDebug() && strlen($warnings = ob_get_contents())) {
         $response = $this->makeComment($warnings) . $response;
     }
     // Save response to file cache unless there are errors
     if (isset($fileCache) && !$errors && !$missing) {
         // Cache single modules...and other requests if there are enough hits
         if (ResourceFileCache::useFileCache($context)) {
             if ($fileCache->isCacheWorthy()) {
                 $fileCache->saveText($response);
             } else {
                 $fileCache->incrMissesRecent($context->getRequest());
             }
         }
     }
     // Remove the output buffer and output the response
     ob_end_clean();
     echo $response;
     wfProfileOut(__METHOD__);
 }