You are here

function _authcache_shutdown_save_page in Authenticated User Page Caching (Authcache) 6

Same name and namespace in other branches
  1. 7 authcache.helpers.inc \_authcache_shutdown_save_page()

Save page to cache

Called using PHP's register_shutdown_function(). This is better than an ob_start callback since global variables are not deconstructed and the function is executed later.

1 string reference to '_authcache_shutdown_save_page'
authcache_init in ./authcache.module
Implements hook_init().

File

./authcache.helpers.inc, line 132
Helper functions for the Authcache module (no Drupal hooks here).

Code

function _authcache_shutdown_save_page() {
  global $user, $base_root, $is_page_authcache;
  $content_type = _authcache_get_content_type();

  // Find user-specified non-html pages.
  $alias = drupal_get_path_alias($_GET['q']);
  $regexp = '/^(' . preg_replace(array(
    '/(\\r\\n?|\\n)/',
    '/\\\\\\*/',
    '/(^|\\|)\\\\<front\\\\>($|\\|)/',
  ), array(
    '|',
    '.*',
    '\\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\\2',
  ), preg_quote(variable_get('authcache_nonhtml', AUTHCACHE_NONHTML_DEFAULT), '/')) . ')$/';
  $is_cached_nonhtml = preg_match($regexp, $alias) || in_array($content_type, array(
    'text/javascript',
    'text/plain',
    'application/xml',
    'application/atom+xml',
  )) || isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';

  //
  // Last minute check to cancel saving to page cache.
  //
  if (!$is_cached_nonhtml && $content_type != 'text/html' || variable_get('authcache_http200', FALSE) && _authcache_get_http_status() != 200 || headers_sent() || empty($_SERVER['REQUEST_METHOD'])) {
    return;
  }

  // Make sure "Location" redirect isn't used
  foreach (headers_list() as $header) {
    if (strpos($header, "Location:") !== FALSE) {
      return;
    }
  }

  // Don't cache pages with PHP errors (Drupal can't catch fatal errors)
  if (function_exists('error_get_last') && ($error = error_get_last())) {
    switch ($error['type']) {

      // Ignore these errors:
      case E_NOTICE:

      // run-time notices
      case E_USER_NOTICE:

      // user-generated notice message
      case E_DEPRECATED:

      // run-time notices
      case E_USER_DEPRECATED:

        // user-generated notice message
        break;
      default:

        // Let user know there is PHP error and return
        print '<script type="text/javascript">
        Authcache.info = ' . drupal_to_js(array_merge(array(
          'Reason' => 'PHP Error',
        ), error_get_last())) . ';
        </script>';
        return;
        break;
    }
  }

  // Cache key, constructed from user role and URL
  $key = _authcache_key($user) . $base_root . request_uri();

  // Authcache info JSON
  $authcache_info = array(
    'page_render' => timer_read('page'),
    // Benchmark
    'page_queries' => '-1',
    // Database benchmark, if enabled
    'cache_render' => '-1',
    // Filled by cookie via JS on cache request
    'cache_uid' => $user->uid,
    // Required by JS for HTML updates
    'cache_inc' => variable_get('authcache_handler', 'unknown'),
    'cache_time' => time(),
  );

  // Hide sensitive info from anonymous users
  if (!$user->uid && !variable_get('authcache_debug_all', FALSE)) {
    unset($authcache_info['cache_uid']);
    unset($authcache_info['cache_inc']);
  }

  // Final check, in case variable was modified
  if (!$is_page_authcache) {
    global $authcache_debug_info;
    if (!$authcache_debug_info) {
      $authcache_debug_info = '$is_page_authcache = false';
    }
    print '<script type="text/javascript">
    Authcache.info = ' . drupal_to_js(array(
      'Reason' => $authcache_debug_info,
    )) . ';
    </script>';
    return;
  }

  // Database benchmarks
  if (variable_get('dev_query', 0)) {
    global $queries;
    $time_query = 0;
    foreach ($queries as $q) {
      $time_query += $q[1];
    }
    $time_query = round($time_query * 1000, 2);

    // Convert seconds to milliseconds
    $percent_query = round($time_query / $authcache_info['page_render'] * 100);
    $authcache_info['page_queries'] = count($queries) . " queries @ {$time_query} ms ({$percent_query}%)";
  }
  else {
    unset($authcache_info['page_queries']);
  }

  // JSON to send via Ajax
  // The "q" key is need during Ajax phase
  $authcache_ajax = array(
    'q' => $_GET['q'],
  );

  // Invoke hook_authcache_info() operation to allow modules to modify info array
  _authcache_invoke_hook('authcache_info', $authcache_info);

  // Invoke hook_authcache() operation to allow modules to modify ajax array
  _authcache_invoke_hook('authcache_ajax', $authcache_ajax);

  // Get buffered HTML
  $buffer = ob_get_contents();
  ob_end_clean();

  // Final check, in case variable was modified
  if (!$is_page_authcache) {
    return;
  }

  // Don't cache empty/dead pages
  if (!$buffer) {
    return;
  }
  $path = drupal_get_normal_path($_GET['q']);

  // normalize path
  if (substr($buffer, 0, 5) == '<?xml') {
    $is_cached_nonhtml = TRUE;

    // don't append JS to XML pages
  }

  // Only place JSON info for HTML pages
  if (!$is_cached_nonhtml) {
    $authcache_footer['info'] = $authcache_info;
    $authcache_footer['ajax'] = $authcache_ajax;
    $authcache_json = "\n<!-- Authcache Footer JSON -->\n" . "<script type=\"text/javascript\">\nvar authcacheFooter = " . drupal_to_js($authcache_footer) . ";\n</script>\n";

    // Insert JSON before </body>, otherwise just append
    if (strripos($buffer, '</body>') !== FALSE) {
      $buffer = str_replace('</body>', $authcache_json . '</body>', $buffer);
    }
    else {
      $buffer .= $authcache_json;
    }
  }

  // Dump to browser, then max gzip & save to cache in background
  $output = $buffer;

  // Fast compression
  if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE && variable_get('page_compression', TRUE) && function_exists('gzencode') && zlib_get_coding_type() == FALSE) {
    $output = gzencode($output, 1, FORCE_GZIP);
    header('Content-Encoding: gzip');
  }

  // Authcache debugging
  if (isset($_COOKIE['authcache_debug'])) {
    setcookie('cache_render', 'First_Page_Request');
  }
  header("Content-Length: " . strlen($output));
  print $output;
  flush();

  // Check for page compression
  if (variable_get('page_compression', TRUE) && function_exists('gzencode')) {

    // We do not store the data in case the zlib mode is deflate.
    // This should be rarely happening.
    if (zlib_get_coding_type() == 'deflate') {
      $cache = FALSE;
    }
    elseif (zlib_get_coding_type() == FALSE) {
      $buffer = gzencode($buffer, 9, FORCE_GZIP);
    }
  }

  // Save to cache
  cache_set($key, $buffer, 'cache_page', CACHE_TEMPORARY, drupal_get_headers());
}