function drupal_build_js_cache

Aggregates JavaScript files into a cache file in the files directory.

The file name for the JavaScript cache file is generated from the hash of the aggregated contents of the files in $files. This forces proxies and browsers to download new JavaScript when the JavaScript changes.

The cache file name is retrieved on a page load via a lookup variable that contains an associative array. The array key is the hash of the names in $files while the value is the cache file name. The cache file is generated in two cases. First, if there is no file name value for the key, which will happen if a new file name has been added to $files or after the lookup variable is emptied to force a rebuild of the cache. Second, the cache file is generated if it is missing on disk. Old cache files are not deleted immediately when the lookup variable is emptied, but are deleted after a set period by drupal_delete_file_if_stale(). This ensures that files referenced by a cached page will still be available.

Parameters

$files: An array of JavaScript files to aggregate and compress into one file.

Return value

The URI of the cache file, or FALSE if the file could not be saved.

3 calls to drupal_build_js_cache()
drupal_aggregate_js in drupal/core/includes/common.inc
Default callback to aggregate JavaScript files.
JavaScriptTest::testAggregation in drupal/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
Tests JavaScript grouping and aggregation.
JavaScriptTest::testAggregationOrder in drupal/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
Tests JavaScript aggregation when files are added to a different scope.

File

drupal/core/includes/common.inc, line 3923
Common functions that many Drupal modules will need to reference.

Code

function drupal_build_js_cache($files) {
  $contents = '';
  $uri = '';
  $map = Drupal::state()
    ->get('system.js_cache_files') ?: array();

  // Create a new array so that only the file names are used to create the hash.
  // This prevents new aggregates from being created unnecessarily.
  $js_data = array();
  foreach ($files as $file) {
    $js_data[] = $file['data'];
  }
  $key = hash('sha256', serialize($js_data));
  if (isset($map[$key])) {
    $uri = $map[$key];
  }
  if (empty($uri) || !file_exists($uri)) {

    // Build aggregate JS file.
    foreach ($files as $info) {
      if ($info['preprocess']) {

        // Append a ';' and a newline after each JS file to prevent them from running together.
        $contents .= file_get_contents($info['data']) . ";\n";
      }
    }

    // Prefix filename to prevent blocking by firewalls which reject files
    // starting with "ad*".
    $filename = 'js_' . Crypt::hashBase64($contents) . '.js';

    // Create the js/ within the files folder.
    $jspath = 'public://js';
    $uri = $jspath . '/' . $filename;

    // Create the JS file.
    file_prepare_directory($jspath, FILE_CREATE_DIRECTORY);
    if (!file_exists($uri) && !file_unmanaged_save_data($contents, $uri, FILE_EXISTS_REPLACE)) {
      return FALSE;
    }

    // If JS gzip compression is enabled and the zlib extension is available
    // then create a gzipped version of this file. This file is served
    // conditionally to browsers that accept gzip using .htaccess rules.
    // It's possible that the rewrite rules in .htaccess aren't working on this
    // server, but there's no harm (other than the time spent generating the
    // file) in generating the file anyway. Sites on servers where rewrite rules
    // aren't working can set js.gzip to FALSE in order to skip
    // generating a file that won't be used.
    if (config('system.performance')
      ->get('js.gzip') && extension_loaded('zlib')) {
      if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($contents, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
        return FALSE;
      }
    }
    $map[$key] = $uri;
    Drupal::state()
      ->set('system.js_cache_files', $map);
  }
  return $uri;
}