Newer
Older
Arto Bendiken
committed
<?php
/**
* @file
Mike Carper
committed
* Provides static file caching for Drupal text output. Pages, Feeds, ect...
Arto Bendiken
committed
*/
//////////////////////////////////////////////////////////////////////////////
// Module settings
Arto Bendiken
committed
define('BOOST_MAX_TIMESTAMP', variable_get('boost_max_timestamp', BOOST_TIME));
define('BOOST_ENABLED', variable_get('boost_enabled', CACHE_NORMAL));
define('BOOST_GZIP', function_exists('gzencode') ? variable_get('page_compression', TRUE) : FALSE);
// This cookie is set for all authenticated users, so that they can be
// excluded from caching (or in the future get a user-specific cached page):
define('BOOST_COOKIE', variable_get('boost_cookie', 'DRUPAL_UID'));
// This line is appended to the generated static files; it is very useful
// for troubleshooting (e.g. determining whether one got the dynamic or
// static version):
define('BOOST_BANNER', variable_get('boost_banner', "Page cached by Boost @ %cached_at, expires @ %expires_at"));
// Caching Options
define('BOOST_CACHE_LIFETIME', variable_get('boost_cache_lifetime', 3600));
define('BOOST_CACHE_XML_LIFETIME', variable_get('boost_cache_xml_lifetime', 3600));
define('BOOST_CACHE_JSON_LIFETIME', variable_get('boost_cache_json_lifetime', 3600));
define('BOOST_CACHE_QUERY', variable_get('boost_cache_query', TRUE));
define('BOOST_CACHE_HTML', variable_get('boost_cache_html', TRUE));
define('BOOST_CACHE_XML', variable_get('boost_cache_xml', FALSE));
define('BOOST_CACHE_JSON', variable_get('boost_cache_json', FALSE));
define('BOOST_CACHE_CSS', variable_get('boost_cache_css', TRUE));
define('BOOST_CACHE_JS', variable_get('boost_cache_js', TRUE));
define('BOOST_CACHEABILITY_OPTION', variable_get('boost_cacheability_option', 0));
define('BOOST_CACHEABILITY_PAGES', variable_get('boost_cacheability_pages', ''));
// Views
define('BOOST_VIEWS_LIST_BEHAVIOR', variable_get('boost_views_list_behavior', 0));
define('BOOST_ROOT_CACHE_DIR', variable_get('boost_root_cache_dir', 'cache'));
define('BOOST_MULTISITE_SINGLE_DB', variable_get('boost_multisite_single_db', TRUE));
define('BOOST_NORMAL_DIR', variable_get('boost_normal_dir', 'normal'));
define('BOOST_GZIP_DIR', variable_get('boost_gzip_dir', 'normal'));
define('BOOST_PERM_NORMAL_DIR', variable_get('boost_perm_normal_dir', 'perm'));
define('BOOST_PERM_GZ_DIR', variable_get('boost_perm_gz_dir', 'perm'));
define('BOOST_CHAR', variable_get('boost_char', '_'));
define('BOOST_PERM_CHAR', variable_get('boost_perm_char', '_'));
define('BOOST_HOST', variable_get('boost_host', ''));
Arto Bendiken
committed
define('BOOST_FILE_EXTENSION', variable_get('boost_file_extension', '.html'));
define('BOOST_XML_EXTENSION', variable_get('boost_xml_extension', '.xml'));
define('BOOST_JSON_EXTENSION', variable_get('boost_json_extension', '.json'));
define('BOOST_CSS_EXTENSION', variable_get('boost_css_extension', '.css'));
define('BOOST_JS_EXTENSION', variable_get('boost_js_extension', '.js'));
define('BOOST_GZIP_EXTENSION', variable_get('boost_gzip_extension', '.gz'));
define('BOOST_ROOT_FILE', variable_get('boost_root_file', '.boost'));
define('BOOST_MAX_PATH_DEPTH', 10);
// Advanced Settings
define('BOOST_CHECK_BEFORE_CRON_EXPIRE', variable_get('boost_check_before_cron_expire', FALSE));
Arto Bendiken
committed
define('BOOST_PRE_PROCESS_FUNCTION', variable_get('boost_pre_process_function', ''));
define('BOOST_EXIT_IN_HOOK_EXIT', variable_get('boost_exit_in_hook_exit', TRUE));
Mike Carper
committed
define('BOOST_FLUSH_ALL_MULTISITE', variable_get('boost_flush_all_multisite', TRUE));
define('BOOST_ONLY_ASCII_PATH', variable_get('boost_only_ascii_path', TRUE));
define('BOOST_SET_FILE_ENCODING', variable_get('boost_set_file_encoding', ''));
define('BOOST_AGGRESSIVE_COOKIE', variable_get('boost_aggressive_cookie', TRUE));
define('BOOST_ASYNCHRONOUS_OUTPUT', variable_get('boost_asynchronous_output', TRUE));
define('BOOST_PAGER_CLEAN', variable_get('boost_pager_clean', FALSE));
define('BOOST_FLUSH_DIR', variable_get('boost_flush_dir', FALSE));
define('BOOST_FLUSH_CCK_REFERENCES', variable_get('boost_flush_cck_references', TRUE));
define('BOOST_FLUSH_FRONT', variable_get('boost_flush_front', TRUE));
define('BOOST_FLUSH_NODE_TERMS', variable_get('boost_flush_node_terms', TRUE));
define('BOOST_FLUSH_MENU_ITEMS', variable_get('boost_flush_menu_items', 0));
define('BOOST_FLUSH_VIEWS', variable_get('boost_flush_views', TRUE));
define('BOOST_FLUSH_VIEWS_INSERT', variable_get('boost_flush_views_insert', TRUE));
define('BOOST_FLUSH_VIEWS_UPDATE', variable_get('boost_flush_views_update', FALSE));
define('BOOST_CLEAR_CACHE_OFFLINE', variable_get('boost_clear_cache_offline', FALSE));
define('BOOST_OVERWRITE_FILE', variable_get('boost_overwrite_file', FALSE));
Mike Carper
committed
define('BOOST_HALT_ON_ERRORS', variable_get('boost_halt_on_errors', FALSE));
Mike Carper
committed
define('BOOST_HALT_ON_MESSAGES', variable_get('boost_halt_on_messages', TRUE));
define('BOOST_DISABLE_CLEAN_URL', variable_get('boost_disable_clean_url', FALSE));
Mike Carper
committed
define('BOOST_AGGRESSIVE_GZIP', BOOST_GZIP ? variable_get('boost_aggressive_gzip', TRUE) : FALSE);
define('BOOST_PERMISSIONS_FILE', variable_get('boost_permissions_file', ''));
define('BOOST_PERMISSIONS_DIR', variable_get('boost_permissions_dir', ''));
define('BOOST_EXPIRE_NO_FLUSH', variable_get('boost_expire_no_flush', FALSE));
define('BOOST_VERBOSE', variable_get('boost_verbose', 5));
define('BOOST_IGNORE_SAFE_WARNING', variable_get('boost_ignore_safe_warning', FALSE));
define('BOOST_IGNORE_SUBDIR_LIMIT', variable_get('boost_ignore_subdir_limit', TRUE));
Mike Carper
committed
define('BOOST_IGNORE_HTACCESS_WARNING', variable_get('boost_ignore_htaccess_warning', FALSE));
define('BOOST_NO_DATABASE', variable_get('boost_no_database', FALSE));
// Domain Whitelist/Blacklist Settings
define('BOOST_DOMAIN_NO_LISTS', 0);
define('BOOST_DOMAIN_WHITELIST_ONLY',1);
define('BOOST_DOMAIN_BLACKLIST_ONLY',2);
define('BOOST_DOMAIN_BOTH_LISTS', 3);
define('BOOST_CRAWL_ON_CRON', variable_get('boost_crawl_on_cron', FALSE));
Mike Carper
committed
define('BOOST_LOOPBACK_BYPASS', BOOST_CRAWL_ON_CRON ? variable_get('boost_loopback_bypass', FALSE) : FALSE);
define('BOOST_PUSH_HTML', variable_get('boost_push_html', TRUE));
define('BOOST_PUSH_XML', variable_get('boost_push_xml', TRUE));
define('BOOST_PUSH_JSON', variable_get('boost_push_json', TRUE));
define('BOOST_CRAWL_URL_ALIAS', variable_get('boost_crawl_url_alias', FALSE));
define('BOOST_CRAWL_DB_IMPORT_SIZE', variable_get('boost_crawl_db_import_size', 10000));
define('BOOST_CRAWLER_THROTTLE', variable_get('boost_crawler_throttle', 0));
define('BOOST_CRAWLER_THREADS', variable_get('boost_crawler_threads', 2));
Arto Bendiken
committed
// Requires Boost Functions or global scope, Define These Last
global $base_url;
define('BOOST_CRAWLER_SELF', $base_url . '/' . 'boost-crawler?nocache=1&key=' . variable_get('boost_crawler_key', FALSE));
define('BOOST_FILE_PATH', BOOST_MULTISITE_SINGLE_DB ? boost_cache_directory(NULL, FALSE) : variable_get('boost_file_path', boost_cache_directory(BOOST_HOST, FALSE)));
define('BOOST_GZIP_FILE_PATH', implode('/', array_filter(explode('/', str_replace(BOOST_ROOT_CACHE_DIR . '/' . BOOST_NORMAL_DIR, BOOST_ROOT_CACHE_DIR . '/' . BOOST_GZIP_DIR . '/', BOOST_FILE_PATH)))));
define('BOOST_PERM_GZIP_FILE_PATH', implode('/', array_filter(explode('/', str_replace(BOOST_ROOT_CACHE_DIR . '/' . BOOST_NORMAL_DIR, BOOST_ROOT_CACHE_DIR . '/' . BOOST_PERM_GZ_DIR . '/', BOOST_FILE_PATH)))));
define('BOOST_PERM_FILE_PATH', implode('/', array_filter(explode('/', str_replace(BOOST_ROOT_CACHE_DIR . '/' . BOOST_NORMAL_DIR, BOOST_ROOT_CACHE_DIR . '/' . BOOST_PERM_NORMAL_DIR . '/', BOOST_FILE_PATH)))));
define('BOOST_CRAWLER_BATCH_SIZE', variable_get('boost_crawler_batch_size', min(15, (ini_get('max_execution_time') + 0.1)/(2 * boost_average_time()))));
define('BOOST_MAX_THREAD_TIME', max(300, 2 * boost_average_time() * BOOST_CRAWLER_THREADS * BOOST_CRAWLER_BATCH_SIZE));
Mike Carper
committed
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// define doesn't work with an array
$GLOBALS['_boost']['verbose_option_defaults'] = array(
'boost_init_404',
'boost_exit_headers',
'boost_cron_expire',
'boost_view_insert',
'boost_expire_node',
'boost_user_op',
'boost_user_delete',
'boost_block_flush_submit',
'boost_block_settings_submit',
'boost_block_rm_settings_submit',
'boost_ob_handler_info',
'boost_ob_handler_redirect',
'boost_cache_clear_all',
'boost_cache_flush_filename',
'boost_cache_expire_router',
'boost_cache_expire_all_db_list',
'boost_cache_expire_all_db_count',
'boost_cache_set_node_relationships',
'boost_crawler_run_stop',
'boost_crawler_run_rogue',
'boost_crawler_run_sleep',
'boost_crawler_run_shutdown',
'boost_crawler_run_kill',
'boost_crawler_run_startup',
'boost_crawler_run_restart',
'boost_crawler_run_done',
'boost_crawler_run_start',
);
$GLOBALS['_boost']['verbose_option_selected'] = array_flip(variable_get('boost_verbose_refined', $GLOBALS['_boost']['verbose_option_defaults']));
Arto Bendiken
committed
Mike Carper
committed
//////////////////////////////////////////////////////////////////////////////
// Global variables
//$GLOBALS['_boost_path'] = '';
//$GLOBALS['_boost_query'] = '';
//$GLOBALS['_boost_message_count'] = '';
//$GLOBALS['_boost_cache_this'] = '';
//$GLOBALS['_boost_max_execution_time'] = '';
//$GLOBALS['_boost_output_buffering'] = '';
//$GLOBALS['_boost_default_socket_timeout'] = '';
//$GLOBALS['_boost_router_item'] = '';
//$GLOBALS['_boost_relationships'] = '';
Mike Carper
committed
//$_boost
Mike Carper
committed
Arto Bendiken
committed
//////////////////////////////////////////////////////////////////////////////
// Core API hooks
Arto Bendiken
committed
/**
* Implementation of hook_help(). Provides online user help.
*/
function boost_help($path, $arg) {
switch ($path) {
Arto Bendiken
committed
case 'admin/help#boost':
if (file_exists($file = drupal_get_path('module', 'boost') . '/README.txt')) {
Arto Bendiken
committed
return '<pre>' . implode("\n", array_slice(explode("\n", @file_get_contents($file)), 2)) . '</pre>';
Arto Bendiken
committed
break;
case 'admin/settings/performance/boost':
return '<p>' . '' . '</p>'; // TODO: add help text.
Arto Bendiken
committed
}
Mike Carper
committed
//hack to get drupal_get_messages before they are destroyed.
$GLOBALS['_boost_message_count'] = count(drupal_get_messages(NULL, FALSE));
Arto Bendiken
committed
}
/**
* Get some basic node attribues from the database given node id.
*
* @param $nid
* node ID
* @return node object
* node->nid
* node->type
* node->language
* node->path
* node->domain
* node->tids
*/
function boost_node_get_basics($nid) {
static $nodes = array();
// Is the node statically cached?
if (isset($nodes[$nid])) {
return $nodes[$nid];
}
// Retrieve node nid, type, language.
$op = 'load';
$node = db_fetch_object(db_query("SELECT nid, type, language FROM {node} WHERE nid = %d", $nid));
// Get path info
if (module_exists('path')) {
path_nodeapi($node, $op, NULL, NULL);
}
// Get domain access info
if (module_exists('domain')) {
domain_nodeapi($node, $op, NULL, NULL);
}
// Get taxonomy tid info
if (module_exists('taxonomy')) {
$node->tids = boost_taxonomy_node_get_tids($node->nid);
}
$nodes[$nid] = $node;
return $nodes[$nid];
}
Mike Carper
committed
/**
* Implementation of hook_ctools_render_alter().
*
* Needed to get the nodes that are inside of a panel's contex
*/
function boost_ctools_render_alter($info, $page, $args, $contexts, $task, $subtask, $handler) {
// return if page not going to be cached or if database turned off
if (!$GLOBALS['_boost_cache_this'] || BOOST_NO_DATABASE) {
return;
}
Mike Carper
committed
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
foreach ($handler->conf['display']->context as $context) {
if ($context->type == 'node') {
$node = $context->data;
// skip if node type is not set
if (!isset($node->type)) {
continue;
}
// set data
$relationship = array(
'child_page_callback' => 'node',
'child_page_type' => $node->type,
'child_page_id' => $node->nid,
);
if (BOOST_VERBOSE >= 9) {
$relationship['debug'] = array(
'panel-task' => $handler->task,
'panel-subtask' => $handler->subtask,
'node-path' => $node->path,
);
}
// send to global
$GLOBALS['_boost_relationships'][] = $relationship;
}
}
}
/**
* Implementation of hook_views_pre_render().
*
* This is called right before the render process. Used to grab the NID's listed
* in this view, and set the view node relationship in the database.
*
* @param &$view
* reference to the view being worked on
*/
function boost_views_pre_render(&$view) {
// return if not a view, page not going to be cached, or if database turned off
if (is_null($view) || !$GLOBALS['_boost_cache_this'] || BOOST_NO_DATABASE) {
return;
}
// return if view doesn't belong in the set of views to search
$views = boost_views_get_valid_array();
$hash = $view->name . ' - ' . $view->current_display;
if (!array_key_exists($hash, $views)) {
return;
}
foreach ($view->result as $item) {
// skip if nid is not a number
if (!is_numeric($item->nid)) {
continue;
}
$node = boost_node_get_basics($item->nid);
// skip if node type is not set
if (!isset($node->type)) {
continue;
}
// set data
Mike Carper
committed
$relationship = array(
'child_page_callback' => 'node',
'child_page_type' => $node->type,
'child_page_id' => $item->nid,
);
if (BOOST_VERBOSE >= 9) {
Mike Carper
committed
$relationship['debug'] = array(
'view-name' => $view->name,
'view-display' => $view->current_display,
'node-path' => $node->path,
);
}
// send to global
Mike Carper
committed
$GLOBALS['_boost_relationships'][] = $relationship;
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
}
}
/**
* Genrate a list of views based off of defaults.
*
* Views that have a path, can be cached by boost & anonymous users can access
*
* @return array
* (view_name . ' - ' . display_name . ' - /' . path => Enabled/Disabled)
*/
function boost_views_generate_default_list() {
$account = user_load(0);
$views = views_get_all_views();
$enabled = array();
$disabled = array();
foreach ($views as $view_name => $view) {
// disabled views get nothing.
if (!empty($view->disabled)) {
unset($views[$view_name]);
continue;
}
$view->init_display();
foreach ($view->display as $display_id => $display) {
// Anonymous users can access view
if (!$view->access($display_id, $account)) {
continue;
}
$hash = $view_name . ' - ' . $display_id;
// Use the default view
if (strcmp($display_id, 'default') == 0) {
$enabled[$hash] = $hash;
}
// View with a path
elseif (isset($display->display_options['path'])) {
$path = $display->display_options['path'];
$hash = $view_name . ' - ' . $display_id . ' - /' . $path;
// Path is cacheable via boost & does not take arguments
if (boost_is_cacheable($path) && !stristr($path, '%')) {
$enabled[$hash] = $hash;
}
// Path is not cacheable via boost or view takes arguments
else {
$disabled[$hash] = 0;
Mike Carper
committed
}
}
// All other displays mark as disabled
else {
$disabled[$hash] = 0;
}
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
ksort($disabled);
natcasesort($enabled);
$checkboxes = array_merge(array('line-break' => 0), $disabled, $enabled);
return $checkboxes;
}
/**
* Genrate a list of valid views that boost will search for new content.
*
* @return array
*/
function boost_views_get_valid_list($fresh = FALSE) {
// Load saved default values; if not saved, save it.
$defaults = variable_get('boost_views_list_default', FALSE);
if (!$defaults || $fresh) {
$defaults = boost_views_generate_default_list();
variable_set('boost_views_list_default', $defaults);
}
// Load custom settings
$list = variable_get('boost_views_list_custom', array());
// Load defaults. http://php.net/operators.array
$list += $defaults;
return $list;
}
/**
* Genrate a list of valid views that boost will search for new content.
*
* @return array
*/
function boost_views_get_valid_array() {
$list = boost_views_get_valid_list();
$views = array();
foreach ($list as $hash => $enabled) {
if ($enabled) {
$info = explode(' - ', $hash);
$page_type = $info[0];
$page_id = $info[1];
$key = $page_type . ' - ' . $page_id;
$views[$key]['page_type'] = $page_type;
$views[$key]['page_id'] = $page_id;
}
}
return $views;
/**
* Implementation of hook_views_pre_view().
*
* Hack due to this issue: http://drupal.org/node/619852
*/
function boost_views_pre_view(&$view) {
}
Mike Carper
committed
/**
* Send out a fast 404 and exit.
*/
function boost_fast404() {
global $base_path;
if (!headers_sent()) {
header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
}
print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
print '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">';
print '<head><title>' . t('404 Not Found') . '</title></head>';
print '<body><h1>' . t('Not Found') . '</h1>';
print '<p>' . t('The requested URL was not found on this server.') . '</p>';
print '<p><a href="' . $base_path . '">' . t('Home') . '</a></p>';
print '</body></html>';
exit();
}
Arto Bendiken
committed
/**
* Implementation of hook_init(). Performs page setup tasks if page not cached.
Arto Bendiken
committed
*/
function boost_init() {
global $user, $base_path, $base_root, $base_url;
// Force lowercase host name
$base_root = strtolower($base_root);
$parts = parse_url($base_url);
$parts['host'] = strtolower($parts['host']);
$base_url = boost_glue_url($parts);
// Make sure this is the correct domain
// Only works if $base_url is set in settings.php
if (strcmp($parts['host'], strtolower($_SERVER['HTTP_HOST'])) != 0) {
$GLOBALS['conf']['cache'] = CACHE_DISABLED;
$GLOBALS['_boost_cache_this'] = FALSE;
return;
}
Mike Carper
committed
// Check if Drupal is started from index.php - could cause problems with other
// contrib modules like ad module.
if (strpos($_SERVER['SCRIPT_FILENAME'], 'index.php') !== FALSE) {
$uid = isset($user->uid) ? $user->uid : 0;
// Remove Boost cookie at logout if it still exists
if (BOOST_AGGRESSIVE_COOKIE && isset($_COOKIE[BOOST_COOKIE]) && $uid == 0) {
boost_set_cookie($uid, BOOST_TIME - 86400);
}
// Remove Boost cookie if set to -1
elseif (isset($_COOKIE[BOOST_COOKIE]) && $_COOKIE[BOOST_COOKIE] == '-1') {
boost_set_cookie($uid, BOOST_TIME - 86400);
}
// Set Boost cookie if it doesn't exists and user is logged in
elseif (BOOST_AGGRESSIVE_COOKIE && !isset($_COOKIE[BOOST_COOKIE]) && $uid != 0) {
boost_set_cookie($uid);
}
// Disable all caches when nocache is set
if (isset($_GET['nocache'])) {
$GLOBALS['conf']['cache'] = CACHE_DISABLED;
$GLOBALS['_boost_cache_this'] = FALSE;
return;
}
Mike Carper
committed
// Make sure this is not a 404 redirect from the htaccesss file
$path = explode($base_path, request_uri());
array_shift($path);
$path = implode($base_path, $path);
$path = explode('?', $path);
$path = array_shift($path);
if ($path != '' && empty($_REQUEST['q']) && !stristr($path, '.php') && isset($_SERVER['REDIRECT_STATUS']) && $_SERVER['REDIRECT_STATUS'] == 404) {
Mike Carper
committed
$GLOBALS['conf']['cache'] = CACHE_DISABLED;
$GLOBALS['_boost_cache_this'] = FALSE;
Mike Carper
committed
if (BOOST_VERBOSE >= 7 && isset($_boost['verbose_option_selected']['boost_init_404'])) {
watchdog('boost', '404 received from server via redirect, going to send a 404. Info: !output', array('!output' => boost_print_r($_SERVER, TRUE, TRUE)));
}
Mike Carper
committed
boost_fast404();
Mike Carper
committed
}
//set variables
Mike Carper
committed
//front page
$GLOBALS['_boost_path'] = '';
}
else {
$GLOBALS['_boost_path'] = $_REQUEST['q'];
}
// Remove anchor tags from url.
if (stristr($GLOBALS['_boost_path'], '#')) {
$GLOBALS['_boost_path'] = array_shift(explode('#', $GLOBALS['_boost_path']));
}
// Make the proper filename for our query
$GLOBALS['_boost_query'] = BOOST_CHAR;
foreach ($_GET as $key => $val) {
if (BOOST_PAGER_CLEAN && $key == 'page') {
$GLOBALS['_boost_path'] .= '/page/' . $val;
continue;
}
if ($key != 'q' && $key != 'destination') {
Arto Bendiken
committed
}
}
$GLOBALS['_boost_query'] .= str_replace('&', '&', urldecode(http_build_query($query)));
if (!empty($user->uid)) {
boost_set_cookie($user->uid);
if (BOOST_DISABLE_CLEAN_URL) {
$GLOBALS['conf']['clean_url'] = 0;
db_query('TRUNCATE {cache_filter}');
db_query('TRUNCATE {cache_menu}');
cache_clear_all('*', 'cache_menu');
cache_clear_all('*', 'cache_filter');
}
}
// Make sure the page is/should be cached according to our current configuration
if ( strpos($_SERVER['SCRIPT_FILENAME'], 'index.php') === FALSE
|| variable_get('site_offline', 0)
|| ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD')
|| $_SERVER['SERVER_SOFTWARE'] === 'PHP CLI'
|| !variable_get('boost_enabled', CACHE_NORMAL)
|| !boost_is_cacheable($GLOBALS['_boost_path'])
) {
Mike Carper
committed
$GLOBALS['_boost_cache_this'] = FALSE;
return;
}
// We only generate cached pages for anonymous visitors.
if (empty($user->uid)) {
if (variable_get('boost_enabled', CACHE_NORMAL) != CACHE_AGGRESSIVE) {
Mike Carper
committed
$GLOBALS['conf']['cache'] = CACHE_DISABLED;
}
Mike Carper
committed
$GLOBALS['_boost_cache_this'] = TRUE;
Mike Carper
committed
register_shutdown_function('_boost_ob_handler');
ob_start();
}
Arto Bendiken
committed
}
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
/**
* Implementation of hook_domainupdate() - keeps domain whitelist variable
* current
*/
function boost_domainupdate($op, $domain, $form_state = array()) {
switch ($op) {
case 'create':
if (variable_get('boost_domain_whitelist_use_domain', FALSE)) {
$whitelist = variable_get('boost_domain_whitelist', array());
$whitelist[ $domain['subdomain'] ] = $domain['subdomain'];
asort($whitelist);
variable_set('boost_domain_whitelist', $whitelist);
}
break;
case 'update':
if (variable_get('boost_domain_whitelist_use_domain', FALSE)) {
$whitelist = variable_get('boost_domain_whitelist', array());
unset($whitelist[ $domain['subdomain'] ]);
$new_name = $form_state['values']['subdomain'];
$whitelist[ $new_name ] = $new_name;
asort($whitelist);
variable_set('boost_domain_whitelist', $whitelist);
}
break;
case 'delete':
if (variable_get('boost_domain_whitelist_use_domain', FALSE)) {
$whitelist = variable_get('boost_domain_whitelist', array());
unset($whitelist[ $domain['subdomain'] ]);
variable_set('boost_domain_whitelist', $whitelist);
}
break;
}
}
/**
* Grabs drupal_goto requests via boost_exit and looks for redirects.
*
* Looks at the current page and the destination, seeing if the internal name
* is the same; node/8 == node/8.
*
* @param $destination
* URL that user will be sent to soon.
*/
function boost_redirect_handler($destination) {
global $base_path, $base_root;
if (empty($destination)) {
return;
}
$source = $base_root . request_uri();
// Parse the URLs
$new_parts = parse_url($destination);
$current_parts = parse_url($source);
// Get paths
$current_path = ltrim($current_parts['path'], $base_path);
$current_path_system = $_GET['q'];
$new_path = ltrim($new_parts['path'], $base_path);
$new_path_system = drupal_get_normal_path($new_path);
if (empty($new_path)) {
$new_path_system = variable_get('site_frontpage', 'node');
}
// Build alt source url
$alt_parts = $current_parts;
$alt_parts['path'] = $current_path_system;
$alt_src = boost_glue_url($alt_parts);
$urls = array($alt_src, $source);
Mike Carper
committed
$debug = array(
'destination' => $destination,
'source' => $source,
'alt_src' => $alt_src,
'current_path' => $current_path,
'current_path_system' => $current_path_system,
'new_path' => $new_path,
'new_path_system' => $new_path_system,
);
// Handle domain alias redirects
if ( module_exists('domain_alias')
&& isset($_domain['redirect'])
&& $_domain['redirect'] == TRUE
boost_cache_kill_url($urls);
return;
}
// Redirect is not to the same domain
elseif (strcmp($new_parts['host'], $current_parts['host']) != 0) {
//watchdog('boost-redirect', 'redirect is not to the same domain' . str_replace(' ', ' ', nl2br(htmlentities(print_r(array($debug), TRUE)))));
boost_cache_kill_url($urls);
return;
}
// Check for globalredirect internal to alias redirect
if (strcmp($current_path, $new_path_system) == 0) {
boost_cache_kill_url($urls);
return;
}
// Check for globalredirect alias to alias redirect (deslashing)
// Also grabs not clean to clean redirects
if (strcmp($current_path, $_REQUEST['q']) != 0) {
if (strcmp($current_path_system, $new_path_system) == 0) {
boost_cache_kill_url($urls);
return;
}
}
if (module_exists('path_redirect')) {
// Check for normal path_redirect alias to alias redirect
$path_redirects = boost_path_redirect_load(array('source' => $current_path));
if (isset($path_redirects)) {
foreach ($path_redirects as $path_redirect) {
$current_path_system = $path_redirect['redirect'];
Mike Carper
committed
$debug['new_current_path_system'] = $current_path_system;
break;
}
}
if (strcmp($current_path_system, $new_path_system) == 0) {
boost_cache_kill_url($urls);
return;
}
// Check for alt path_redirect alias to alias redirect
$path_redirects = boost_path_redirect_load(array('source' => $current_path_system));
if (isset($path_redirects)) {
foreach ($path_redirects as $path_redirect) {
$current_path_system = $path_redirect['redirect'];
Mike Carper
committed
$debug['new_current_path_system'] = $current_path_system;
break;
}
}
if (strcmp($current_path_system, $new_path_system) == 0) {
boost_cache_kill_url($urls);
return;
}
}
// Last attempt of getting a "match" for this redirect
$result = db_query("SELECT page_callback, page_type, page_id FROM {boost_cache} WHERE expire = 0 AND (hash_url = '%s' OR hash_url = '%s')", md5($source), md5($alt_src));
while ($row = db_fetch_array($result)) {
// Handle node redirects
if ($row['page_callback'] == 'node') {
$current_path_system = $row['page_callback'] . '/' . $row['page_id'];
if (strcmp($current_path_system, $new_path_system) == 0) {
boost_cache_kill_url($urls);
return;
}
}
}
Mike Carper
committed
//watchdog('boost-redirect', 'Nothing Done' . str_replace(' ', ' ', nl2br(htmlentities(print_r($debug, TRUE)))));
Arto Bendiken
committed
/**
* Implementation of hook_exit(). Performs cleanup tasks.
*
* For POST requests by anonymous visitors, this adds a dummy query string
* to any URL being redirected to using drupal_goto().
*
* This is pretty much a hack that assumes a bit too much familiarity with
* what happens under the hood of the Drupal core function drupal_goto().
*
* It's necessary, though, in order for any session messages set on form
* submission to actually show up on the next page if that page has been
* cached by Boost.
*
* @param $destination
* URL that user will be sent to soon.
Arto Bendiken
committed
*/
function boost_exit($destination = NULL) {
Mike Carper
committed
// Check for redirects.
if (!empty($destination) && $_SERVER['REQUEST_METHOD'] != 'POST' && empty($_GET['destination'])) {
Mike Carper
committed
// Make sure path functions are available.
drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);
boost_redirect_handler($destination);
}
Mike Carper
committed
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
// 404, 403 dection and removal from the boost cache.
// Only run if user is anonymous.
if (empty($user->uid)) {
$status = boost_get_http_status();
if ($status == 404 || $status == 403) {
// Bail out of caching
$GLOBALS['_boost_cache_this'] = FALSE;
// Get content type
$types = boost_get_content_type();
$types = array_pop($types);
// Match extension
if (stristr($types, 'text/javascript')) {
$extension = BOOST_JSON_EXTENSION;
}
elseif ( stristr($types, 'application/rss')
|| stristr($types, 'text/xml')
|| stristr($types, 'application/rss+xml')
) {
$extension = BOOST_JSON_EXTENSION;
}
elseif (stristr($types, 'text/html') && BOOST_CACHE_HTML) {
$extension = BOOST_FILE_EXTENSION;
}
// Get filename
if (!empty($extension)) {
$filename = boost_file_path($GLOBALS['_boost_path'], TRUE, $extension);
// Remove dead item from the cache (file & db);
if ($filename) {
$files = array(array('filename' => $filename));
boost_cache_kill($files, TRUE);
boost_remove_db($files);
}
}
}
}
// Check that hook_exit() was invoked by drupal_goto() for a POST request:
// Check that we're dealing with an anonymous visitor. and that some
// session messages have actually been set during this page request:
if ( !empty($destination)
&& $_SERVER['REQUEST_METHOD'] == 'POST'
&& empty($user->uid)
&& $messages = drupal_set_message()
) {
$query_parts = parse_url($destination);
// Add a nocache parameter to query. Such pages will never be cached
$query_parts['query'] .= (empty($query_parts['query']) ? '' : '&') . 'nocache=1';
// Rebuild the URL with the new query string. Do not use url() since
// destination has presumably already been run through url().
$destination = boost_glue_url($query_parts);
// Do what drupal_goto() would do if we were to return to it:
if (BOOST_EXIT_IN_HOOK_EXIT) {
// FIXME: call any remaining exit hooks since we're about to terminate?
exit(header('Location: ' . $destination));
}
else {
header('Location: ' . $destination);
Arto Bendiken
committed
}
Mike Carper
committed
// Set watchdog error if headers already sent
if ( BOOST_ASYNCHRONOUS_OUTPUT
&& isset($GLOBALS['_boost_cache_this'])
&& $GLOBALS['_boost_cache_this']
&& headers_sent($filename, $linenum)
&& !boost_headers_contain('Location: ')
&& BOOST_VERBOSE >= 7
Mike Carper
committed
&& isset($_boost['verbose_option_selected']['boost_exit_headers'])
Mike Carper
committed
watchdog('boost', 'boost_exit() Debug: Headers already sent in @filename on line @linenum. Asynchronous Operation will not be used.', array('@filename' => $filename, '@linenum' => $linenum));
}
Arto Bendiken
committed
}
/**
* Implementation of hook_menu().
*/
function boost_menu() {
$items['admin/settings/performance/default'] = array(
'title' => 'Performance',
'type' => MENU_DEFAULT_LOCAL_TASK,
'file path' => drupal_get_path('module', 'system'),
);
$items['admin/settings/performance/boost'] = array(
'title' => 'Boost Settings',
'description' => 'Advanced boost configuration.',
'page callback' => 'drupal_get_form',
'page arguments' => array('boost_admin_boost_performance_page'),
'access arguments' => array('administer site configuration'),
'weight' => 10,
'type' => MENU_LOCAL_TASK,
'file' => 'boost.admin.inc',
);
$items['admin/settings/performance/boost-rules'] = array(
'title' => 'Boost htaccess rules generation',
'description' => 'htaccess boost rules.',
'page callback' => 'drupal_get_form',
'page arguments' => array('boost_admin_htaccess_page'),
'access arguments' => array('administer site configuration'),
'weight' => 12,
'type' => MENU_LOCAL_TASK,
'file' => 'boost.admin.inc',
);
$items['boost_stats.php'] = array(
'page callback' => 'boost_stats_ajax_callback',
'type' => MENU_CALLBACK,
'access callback' => 1,
'access arguments' => array('access content'),
'file path' => drupal_get_path('module', 'boost'),
$items['boost-crawler'] = array(
Mike Carper
committed
'page callback' => 'boost_crawler_run',
'type' => MENU_CALLBACK,
'access callback' => 1,
'access arguments' => array('access content'),
'file path' => drupal_get_path('module', 'boost'),
);
$items['boost_views.php'] = array(
'page callback' => 'boost_views_async',
'type' => MENU_CALLBACK,
'access arguments' => array('access content'),
'file path' => drupal_get_path('module', 'boost'),
);
return $items;
}
Arto Bendiken
committed
/**
* Implementation of hook_form_alter(). Performs alterations before a form
* is rendered.
*/
function boost_form_alter(&$form, $form_state, $form_id) {
Arto Bendiken
committed
switch ($form_id) {
// Alter Drupal's system performance settings form by hiding the default
// cache enabled/disabled control (which will now always default to
// CACHE_DISABLED), and inject our own settings in its stead.
case 'system_performance_settings':
module_load_include('inc', 'boost', 'boost.admin');
$form['page_cache'] = boost_admin_performance_page($form['page_cache']);
$form['#submit'][] = 'boost_admin_performance_page_submit';
Arto Bendiken
committed
$form['clear_cache']['clear']['#submit'][0] = 'boost_admin_clear_cache_submit';
break;
// Alter Drupal's site maintenance settings form in order to ensure that
// the static page cache gets wiped if the administrator decides to take
// the site offline.
case 'system_site_maintenance_settings':
module_load_include('inc', 'boost', 'boost.admin');
$form['#submit'][] = 'boost_admin_site_offline_submit';
break;
// Alter Drupal's modules build form in order to ensure that
// the static page cache gets wiped if the administrator decides to
// change enabled modules
case 'system_modules':
module_load_include('inc', 'boost', 'boost.admin');
$form['#submit'][] = 'boost_admin_modules_submit';
break;
// Alter Drupal's theme build form in order to ensure that
// the static page cache gets wiped if the administrator decides to
// change theme
case 'system_themes_form':
module_load_include('inc', 'boost', 'boost.admin');
$form['#submit'][] = 'boost_admin_themes_submit';
Mike Carper
committed
// Added below due to this bug: http://drupal.org/node/276615
Mike Carper
committed
if ( variable_get('preprocess_css', FALSE)==TRUE
&& version_compare(VERSION, 6.13, '<=')
Mike Carper
committed
&& boost_cache_clear_all()
) {
drupal_set_message(t('Boost: Static page cache cleared. See <a href="http://drupal.org/node/276615">http://drupal.org/node/276615</a> for reason why (core bug that is fixed in 6.14+).'), 'warning');
Mike Carper
committed
}
Arto Bendiken
committed
}
}
/**
* Implementation of hook_cron(). Performs periodic actions.
*/
function boost_cron() {
if (!variable_get('boost_enabled', CACHE_NORMAL)) {
Mike Carper
committed
return;
}
Mike Carper
committed
global $_boost;
Arto Bendiken
committed
// Remove old domains if they are now a redirect
if (module_exists('domain_alias')) {
$domains = domain_domains();
$old_domains = array();
foreach ($domains as $key => $value) {
foreach ($value['aliases'] as $alias) {
if ($alias['redirect'] == 1) {
$old_domains[] = boost_cache_directory($alias['pattern'], FALSE);
}
}
}
if (!empty($old_domains)) {
$result = boost_db_multi_select_in('boost_cache', 'base_dir', "'%s'", $old_domains);
$files = array();
while ($row = db_fetch_array($result)) {
$files[] = array('filename' => $row['filename'], 'hash' => $row['hash']);
}
if (!empty($files)) {
boost_cache_kill($files, TRUE);
boost_remove_db($files);
}
}
}
// Remove old domains if they are "inactive"
if (module_exists('domain')) {
$domains = domain_domains();
$old_domains = array();
foreach ($domains as $key => $value) {
if ($value['valid'] == 0) {
$old_domains[] = boost_cache_directory($value['subdomain'], FALSE);
}
}
if (!empty($old_domains)) {
$result = boost_db_multi_select_in('boost_cache', 'base_dir', "'%s'", $old_domains);
$files = array();
while ($row = db_fetch_array($result)) {
$files[] = array('filename' => $row['filename'], 'hash' => $row['hash']);
}
if (!empty($files)) {
boost_cache_kill($files, TRUE);
boost_remove_db($files);
}
}
}
// Check for new views on cron
if (module_exists('views') && BOOST_VIEWS_LIST_BEHAVIOR == 0) {
$defaults = boost_views_generate_default_list();
variable_set('boost_views_list_default', $defaults);
}
$expire = TRUE;
if (BOOST_CHECK_BEFORE_CRON_EXPIRE) {
$expire = boost_has_site_changed(TRUE);
}
Mike Carper
committed
// Expire old content
if (!BOOST_LOOPBACK_BYPASS && variable_get('boost_expire_cron', TRUE) && $expire && boost_cache_expire_all()) {
Mike Carper
committed
if (BOOST_VERBOSE >= 5 && isset($_boost['verbose_option_selected']['boost_cron_expire'])) {
watchdog('boost', 'Expired stale files from static page cache.', array(), WATCHDOG_NOTICE);
}