diff --git a/includes/common.inc b/includes/common.inc index e36d0ae035f..be85c68a7d4 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -3610,7 +3610,7 @@ function drupal_build_css_cache($css) { $uri = $map[$key]; } - if (empty($uri) || !drupal_aggregated_file_exists($uri)) { + if (empty($uri) || !drupal_aggregated_file_exists($uri, 'css')) { // Build aggregate CSS file. foreach ($css as $stylesheet) { // Only 'file' stylesheets can be aggregated. @@ -3835,6 +3835,7 @@ function _drupal_load_stylesheet($matches) { function drupal_clear_css_cache() { variable_del('drupal_css_cache_files'); file_scan_directory('public://css', '/.*/', array('callback' => 'drupal_delete_file_if_stale')); + drupal_aggregated_file_flush('css'); } /** @@ -4961,20 +4962,72 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgro drupal_add_js($settings, 'setting'); } -function drupal_aggregated_file_exists($uri) { - if (function_exists('apc_fetch')) { - $exists = apc_fetch('file_exists_' . $uri); - if (!$exists && file_exists($uri)) { - $exists = TRUE; - apc_store('file_exists_' . $uri, TRUE, 86400); - } +/** + * Checks if an aggregated file exists and caches the results in a APC variable. + * + * @param string $uri + * The file URI to check. + * @param string $type + * The type of aggregated file. Either 'css' or 'js'. + * + * @return bool + * TRUE if the file exists. FALSE otherwise. + */ +function drupal_aggregated_file_exists($uri, $type = 'css') { + if (!function_exists('apc_fetch')) { + return file_exists($uri); } - else { - $exists = file_exists($uri); + $cache_key = 'drupal_aggregated_files'; + + // Try the static cache first. + $files = &drupal_static($cache_key); + if (!empty($files[$type][$uri])) { + return $files[$type][$uri]; + } + + // Try the APC cache. + $files = apc_fetch($cache_key); + if (isset($files[$type][$uri])) { + return $files[$type][$uri]; } + $files = $files ? $files : array(); + $files[$type] = isset($files[$type]) ? $files[$type] : array(); + $exists = file_exists($uri); + $files[$type][$uri] = $exists; + apc_store($cache_key, $files, 86400); return $exists; } +/** + * Clears the cached information about file existence. + * + * @param string $type + * The type of aggregated file. Either 'css' or 'js'. + */ +function drupal_aggregated_file_flush($type = 'css') { + if (!function_exists('apc_fetch')) { + return; + } + $cache_key = 'drupal_aggregated_files'; + $files = &drupal_static($cache_key); + if (empty($files)) { + $files = apc_fetch($cache_key); + } + $save = FALSE; + if (!empty($files[$type])) { + unset($files[$type]); + $save = TRUE; + } + if (empty($files)) { + apc_delete($cache_key); + drupal_static_reset($cache_key); + return; + } + if ($save) { + apc_store('drupal_aggregated_files', $files); + } +} + /** * Aggregates JavaScript files into a cache file in the files directory. * @@ -5014,7 +5067,7 @@ function drupal_build_js_cache($files) { $uri = $map[$key]; } - if (empty($uri) || !drupal_aggregated_file_exists($uri)) { + if (empty($uri) || !drupal_aggregated_file_exists($uri, 'js')) { // Build aggregate JS file. foreach ($files as $path => $info) { if ($info['preprocess']) { @@ -5059,6 +5112,7 @@ function drupal_clear_js_cache() { variable_del('javascript_parsed'); variable_del('drupal_js_cache_files'); file_scan_directory('public://js', '/.*/', array('callback' => 'drupal_delete_file_if_stale')); + drupal_aggregated_file_flush('js'); } /** diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test index f1a743e00c0..d568e396277 100644 --- a/modules/simpletest/tests/theme.test +++ b/modules/simpletest/tests/theme.test @@ -24,6 +24,46 @@ class ThemeTestCase extends DrupalWebTestCase { theme_enable(array('test_theme')); } + /** + * Test the file exists APC cache. + */ + public function testFileExistsCache() { + // Allow asset aggregation. + variable_set('preprocess_css', 1); + variable_set('preprocess_js', 1); + + if (!variable_get('preprocess_css', FALSE) || !variable_get('preprocess_js', FALSE)) { + watchdog('theme.test', 'The aggregation variables are being forced via settings.php. Unable to test.'); + return; + } + // If there is no APC, there is nothing to test. + if (!function_exists('apc_fetch')) { + watchdog('APC', 'APC is not enabled, therefore the file existence will not be cached.'); + return; + } + + $account = $this->drupalCreateUser(array('access content')); + $this->drupalLogin($account); + + // Visit the newly created node. + $this->drupalGet('theme-test/suggestion'); + + // At this point since aggregation is on we should have some file_exists + // URIs cached in APC. + $files = apc_fetch('drupal_aggregated_files'); + $this->assertTrue(!empty($files), 'File exists calls were cached in APC.'); + + // Test cache clearing. + drupal_clear_js_cache(); + $files = apc_fetch('drupal_aggregated_files'); + $this->assertTrue(empty($files['js']), 'File exists calls were cleared for JS.'); + + $this->drupalGet('theme-test/suggestion'); + drupal_flush_all_caches(); + $files = apc_fetch('drupal_aggregated_files'); + $this->assert(!$files, 'All cache cleared from APC.'); + } + /** * Test function theme_get_suggestions() for SA-CORE-2009-003. */