You are here

function agrcache_generate_aggregate in Aggregate cache 7

Menu callback to generate a css aggregate.

1 string reference to 'agrcache_generate_aggregate'
agrcache_menu in ./agrcache.module
Implements hook_menu().

File

./agrcache.module, line 390
Provides imagecache style generation of css/js aggregates.

Code

function agrcache_generate_aggregate($filename, $type) {

  // Recreate the full uri from the filename.
  $path = "public://{$type}";
  $uri = $path . '/' . $filename;
  $map = agrcache_get_file_list($type);
  $compression = variable_get($type . '_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib');

  // This callback should only be called if the file does not already exist on
  // disk since the webserver will serve the file directly, bypassing PHP when it does.
  // However it is possible that the file was created during bootstrap by another request.
  if (file_exists($uri)) {
    $data = file_get_contents($uri);
  }
  elseif (isset($map['callbacks'][$uri])) {

    // There is a possible race condition in the following deployment case with
    // multiple web heads.
    //   - server A has updated code, and gets a page request - i.e. builds a
    //     filename.
    //   - server B (this server) is not updated yet, and gets a request for an
    //   aggregate with that filename.
    // In this case, server 2 cannot assume that the filename matches the
    // actual contents of the files on disk, since the files on disk are in
    // fact different, given the filename was generated on a different server,
    // with different files. Based on the information in the map, build the
    // filename again, and ensure they match. Since the filename depends on
    // the contents of the files, if the filenames don't match we know we've
    // hit this race condition. When that happens, log an error, don't write
    // the aggregate, but still serve the contents of the old file, which is
    // the best we can do.
    $write = TRUE;
    $function = 'agrcache_collect_' . $type . '_group';
    $files = $map['callbacks'][$uri];
    $this_data = $function($files);
    $this_filename = $type . '_' . drupal_hash_base64(agrcache_serialize_files($files) . $this_data) . ".{$type}";
    $path = "public://{$type}";
    $this_uri = $path . '/' . $this_filename;
    if ($this_uri !== $uri) {
      watchdog('agrcache', 'Attempted to create an aggregate based on a set of files that does not match the filename provided.');
      $write = FALSE;
    }

    // @todo: consider adding locking here.
    // @see drupal.org/node/886488
    $function = 'agrcache_process_' . $type . '_group';
    $data = $function($map['callbacks'][$uri]);

    // Check file_exists() again, in case the file was built during processing.
    if (!file_exists($uri) && $write) {
      _agrcache_file_unmanaged_save_data($data, $path, $uri);

      // If gzip compression is enabled, clean URLs are enabled (which means
      // that rewrite rules are working) 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.
      if ($compression) {
        $compressed = gzencode($data, 9, FORCE_GZIP);
        if (!file_exists($uri . '.gz')) {
          _agrcache_file_unmanaged_save_data($compressed, $path, $uri . '.gz');
        }
      }
    }
  }
  else {
    $data = '';
    watchdog('agrcache', 'Received request for a non-existent css aggregate @uri', array(
      '@uri' => $uri,
    ));
  }
  $content_type = $type == 'css' ? 'text/css' : 'application/javascript';
  $headers = array();
  $headers['Content-Type'] = $content_type;

  // Ensure that only the request that is hitting PHP sees this file,
  // otherwise we want to use the file on disk for consistency.
  $headers['Cache-Control'] = 'private, no-cache, no-store';
  $headers['Edge-Control'] = 'bypass-cache';
  foreach ($headers as $key => $value) {
    drupal_add_http_header($key, $value);
  }
  print $data;
  drupal_exit();
}