You are here

boost.api.inc in Boost 5

Implements the Boost API for static page caching.

File

boost.api.inc
View source
<?php

/**
 * @file
 * Implements the Boost API for static page caching.
 */

//////////////////////////////////////////////////////////////////////////////

// BOOST API

/**
 * Determines whether a given Drupal page can be cached or not.
 *
 * To avoid potentially troublesome situations, the user login page is never
 * cached, nor are any admin pages. At present, we also refuse to cache any
 * RSS feeds provided by Drupal, since they would require special handling
 * in the mod_rewrite ruleset as they shouldn't be sent out using the
 * text/html content type.
 */
function boost_is_cacheable($path) {
  $alias = drupal_get_path_alias($path);
  $path = drupal_get_normal_path($path);

  // normalize path
  // Never cache the basic user login/registration pages or any administration pages
  if ($path == 'user' || preg_match('!^user/(login|register|password)!', $path) || preg_match('!^admin!', $path)) {
    return FALSE;
  }

  // At present, RSS feeds are not cacheable due to content type restrictions
  if ($path == 'rss.xml' || preg_match('!/feed$!', $path)) {
    return FALSE;
  }

  // Don't cache comment reply pages
  if (preg_match('!^comment/reply!', $path)) {
    return FALSE;
  }

  // Match the user's cacheability settings against the path
  if (BOOST_CACHEABILITY_OPTION == 2) {
    $result = drupal_eval(BOOST_CACHEABILITY_PAGES);
    return !empty($result);
  }
  $regexp = '/^(' . preg_replace(array(
    '/(\\r\\n?|\\n)/',
    '/\\\\\\*/',
    '/(^|\\|)\\\\<front\\\\>($|\\|)/',
  ), array(
    '|',
    '.*',
    '\\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\\2',
  ), preg_quote(BOOST_CACHEABILITY_PAGES, '/')) . ')$/';
  return !(BOOST_CACHEABILITY_OPTION xor preg_match($regexp, $alias));
}

/**
 * Determines whether a given Drupal page is currently cached or not.
 */
function boost_is_cached($path) {
  $path = empty($path) ? BOOST_FRONTPAGE : $path;
  $alias = drupal_get_path_alias($path);
  $path = drupal_get_normal_path($path);

  // normalize path
  // TODO: also determine if alias/symlink exists?
  return file_exists(boost_file_path($path));
}

/**
 * Deletes all static files currently in the cache.
 */
function boost_cache_clear_all() {
  clearstatcache();
  if (($cache_dir = boost_cache_directory()) && file_exists($cache_dir)) {
    return _boost_rmdir_rf($cache_dir);
  }
}

/**
 * Deletes all expired static files currently in the cache.
 */
function boost_cache_expire_all() {
  clearstatcache();
  if (($cache_dir = boost_cache_directory()) && file_exists($cache_dir)) {
    _boost_rmdir_rf($cache_dir, 'boost_file_is_expired');
  }
  return TRUE;
}

/**
 * Expires the static file cache for a given page, or multiple pages
 * matching a wildcard.
 */
function boost_cache_expire($path, $wildcard = FALSE) {

  // TODO: handle wildcard.
  $alias = drupal_get_path_alias($path);
  $path = drupal_get_normal_path($path);

  // normalize path
  if (($filename = boost_file_path($path)) && file_exists($filename)) {
    @unlink($filename);
  }
  if ($alias != $path && ($symlink = boost_file_path($alias)) && is_link($symlink)) {
    @unlink($symlink);
  }
  return TRUE;
}

/**
 * Returns the cached contents of the specified page, if available.
 */
function boost_cache_get($path) {
  $path = drupal_get_normal_path($path);

  // normalize path
  if ($filename = boost_file_path($path)) {
    if (file_exists($filename) && is_readable($filename)) {
      return file_get_contents($filename);
    }
  }
  return NULL;
}

/**
 * Replaces the cached contents of the specified page, if stale.
 */
function boost_cache_set($path, $data = '') {

  // Append the Boost footer with the relevant timestamps
  $time = time();
  $cached_at = date('Y-m-d H:i:s', $time);
  $expires_at = date('Y-m-d H:i:s', $time + variable_get('cache_lifetime', 600));
  $data = rtrim($data) . "\n" . str_replace(array(
    '%cached_at',
    '%expires_at',
  ), array(
    $cached_at,
    $expires_at,
  ), BOOST_BANNER);

  // Execute the pre-process function if one has been defined
  if (function_exists(BOOST_PRE_PROCESS_FUNCTION)) {
    $data = call_user_func(BOOST_PRE_PROCESS_FUNCTION, $data);
  }
  $alias = drupal_get_path_alias($path);
  $path = drupal_get_normal_path($path);

  // normalize path
  // Create or update the static file as needed
  if ($filename = boost_file_path($path)) {
    _boost_mkdir_p(dirname($filename));
    if (!file_exists($filename) || boost_file_is_expired($filename)) {
      if (file_put_contents($filename, $data) === FALSE) {
        watchdog('boost', t('Unable to write file: %file', array(
          '%file' => $filename,
        )), WATCHDOG_WARNING);
      }
    }

    // If a URL alias is defined, create that as a symlink to the actual file
    if ($alias != $path && ($symlink = boost_file_path($alias))) {
      _boost_mkdir_p(dirname($symlink));
      if (!is_link($symlink) || realpath(readlink($symlink)) != realpath($filename)) {
        if (file_exists($symlink)) {
          @unlink($symlink);
        }
        if (!_boost_symlink($filename, $symlink)) {
          watchdog('boost', t('Unable to create symlink: %link to %target', array(
            '%link' => $symlink,
            '%target' => $filename,
          )), WATCHDOG_WARNING);
        }
      }
    }
  }
  return TRUE;
}

/**
 * Returns the full directory path to the static file cache directory.
 */
function boost_cache_directory($user_id = 0, $host = NULL) {
  global $user, $base_url;
  $user_id = 0;

  //(!is_null($user_id) ? $user_id : BOOST_USER_ID);
  $parts = parse_url($base_url);
  $host = !empty($host) ? $host : $parts['host'];

  // FIXME: correctly handle Drupal subdirectory installations.
  return implode('/', array(
    getcwd(),
    BOOST_FILE_PATH,
    $host,
    $user_id,
  ));
}

/**
 * Returns the static file path for a Drupal page.
 */
function boost_file_path($path) {
  if (empty($path) || $path == BOOST_FRONTPAGE) {
    $path = 'index';

    // special handling for Drupal's front page
  }

  // Under no circumstances should the incoming path contain '..' or null
  // bytes; we also limit the maximum directory nesting depth of the path
  if (strpos($path, '..') !== FALSE || strpos($path, "\0") !== FALSE || count(explode('/', $path)) > BOOST_MAX_PATH_DEPTH) {
    return FALSE;
  }

  // Convert any other undesirable characters in the path to underscores
  $path = preg_replace('@[^/a-z0-9_-]@i', '_', $path);
  return boost_cache_directory() . '/' . $path . BOOST_FILE_EXTENSION;
}

/**
 * Returns the age of a cached file, measured in seconds since it was last
 * updated.
 */
function boost_file_get_age($filename) {
  return time() - filemtime($filename);
}

/**
 * Determines whether a cached file has expired, i.e. whether its age exceeds
 * the maximum cache lifetime as defined by Drupal's system settings.
 */
function boost_file_is_expired($filename) {
  if (is_link($filename)) {
    return FALSE;
  }
  return boost_file_get_age($filename) > variable_get('cache_lifetime', 600);
}

//////////////////////////////////////////////////////////////////////////////

Functions

Namesort descending Description
boost_cache_clear_all Deletes all static files currently in the cache.
boost_cache_directory Returns the full directory path to the static file cache directory.
boost_cache_expire Expires the static file cache for a given page, or multiple pages matching a wildcard.
boost_cache_expire_all Deletes all expired static files currently in the cache.
boost_cache_get Returns the cached contents of the specified page, if available.
boost_cache_set Replaces the cached contents of the specified page, if stale.
boost_file_get_age Returns the age of a cached file, measured in seconds since it was last updated.
boost_file_is_expired Determines whether a cached file has expired, i.e. whether its age exceeds the maximum cache lifetime as defined by Drupal's system settings.
boost_file_path Returns the static file path for a Drupal page.
boost_is_cacheable Determines whether a given Drupal page can be cached or not.
boost_is_cached Determines whether a given Drupal page is currently cached or not.