Advanced CSS/JS aggregation module.

Functions used for clearing caches and killing files.

Fichier

./advagg.cache.inc

View source
<?php


/**
 * @file
 * Advanced CSS/JS aggregation module.
 *
 * Functions used for clearing caches and killing files.
 */

/**
 * Uses the database to scan CSS/JS files for changes.
 *
 * @return array
 *   Array of files that have changed.
 */
function advagg_scan_for_changes() {
    // Get all files stored in the database and filesystem.
    $files_that_have_changed = array();
    $result = db_select('advagg_files', 'af')->fields('af')
        ->execute();
    if (!empty($result)) {
        module_load_include('inc', 'advagg', 'advagg');
        $filenames = array();
        $data = array();
        foreach ($result as $row) {
            $filenames[] = $row->filename;
            $data[$row->filename] = (array) $row;
        }
        // Get filesystem data.
        $files_info = advagg_get_info_on_files($filenames, TRUE);
        foreach ($files_info as $info) {
            if (!isset($data[$info['data']])) {
                continue;
            }
            $row = $data[$info['data']];
            // Select the keys to compare.
            $keys_to_compare = array(
                'filesize',
                'content_hash',
                'linecount',
            );
            $changed = array();
            foreach ($keys_to_compare as $key) {
                if ($row[$key] != $info[$key]) {
                    $changed[] = $key . ' db:' . $row[$key] . ' file:' . $info[$key];
                    break;
                }
            }
            // Compare mtime if it is not zero.
            if (empty($info['split']) && !empty($info['mtime'])) {
                if (variable_get('advagg_strict_mtime_check', ADVAGG_STRICT_MTIME_CHECK) && $row['mtime'] != $info['mtime']) {
                    $changed[] = 'mtime db:' . $row['mtime'] . ' file:' . $info['mtime'];
                }
                elseif ($row['mtime'] < $info['mtime']) {
                    $changed[] = 'mtime db:' . $row['mtime'] . ' file:' . $info['mtime'];
                }
            }
            if (empty($changed)) {
                // Call hook_advagg_scan_for_changes().
                $changes_array = module_invoke_all('advagg_scan_for_changes', $row['filename']);
                if (is_array($changes_array)) {
                    foreach ($changes_array as $value) {
                        if (!empty($value)) {
                            $changed[] = $value;
                            break;
                        }
                    }
                }
            }
            // If file has changed, add it to the array.
            if (!empty($changed)) {
                $info['changes'] = $changed;
                $files_that_have_changed[$row['filename']] = $info;
            }
        }
    }
    return $files_that_have_changed;
}

/**
 * Flush the correct caches so CSS/JS changes go live.
 *
 * @return array
 *   Array of files that have changed and caches flushed.
 */
function advagg_push_new_changes(array $files = array()) {
    $results = array();
    // Scan the file system for changes to CSS/JS files.
    if (empty($files)) {
        $files = advagg_scan_for_changes();
        if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
            $variables = array(
                '@files' => print_r($files, TRUE),
            );
            watchdog('advagg-debug', 'Changes detected in <pre>@files</pre>.', $variables, WATCHDOG_DEBUG);
        }
    }
    // Clear some static caches.
    drupal_static_reset('advagg_get_info_on_file');
    drupal_static_reset('advagg_drupal_hash_base64');
    drupal_static_reset('advagg_current_hooks_hash_array');
    drupal_static_reset('advagg_get_current_hooks_hash');
    if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
        // Exception used to get a compact stack trace.
        $e = new Exception();
        $variables = array(
            '@changes' => print_r($e->getTraceAsString(), TRUE),
        );
        watchdog('advagg-debug', 'New changes called by: <pre>@changes</pre>', $variables, WATCHDOG_DEBUG);
    }
    // If something changed, flush the correct caches so that change goes out.
    if (!empty($files)) {
        $types = array();
        module_load_include('inc', 'advagg', 'advagg');
        foreach ($files as $filename => $meta_data) {
            // Lookup the aggregates/cache ids that use this file.
            $cache_ids = advagg_get_aggregates_using_file($meta_data['filename_hash'], TRUE);
            $cache_hits = array();
            $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
            $types[$ext] = TRUE;
            if (!empty($cache_ids)) {
                $cache_hits = cache_get_multiple($cache_ids, 'cache_advagg_info');
                foreach ($cache_hits as $cid => $data) {
                    if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
                        watchdog('advagg-debug', 'Clearing cache @cid.', array(
                            '@cid' => $cid,
                        ), WATCHDOG_DEBUG);
                    }
                    cache_clear_all($cid, 'cache_advagg_info', FALSE);
                }
            }
            $changes = array();
            if (!empty($meta_data['changes'])) {
                $changes = $meta_data['changes'];
                unset($meta_data['changes']);
            }
            $results[$filename] = array(
                count($cache_ids),
                count($cache_hits),
                $changes,
            );
            // Update database.
            advagg_insert_update_files(array(
                $filename => $meta_data,
            ), $ext);
        }
        // Change query-strings on css/js files to enforce reload for all users.
        // Change css_js_query_string variable.
        _drupal_flush_css_js();
        // Let other modules know about the changed files.
        // Call hook_advagg_changed_files().
        module_invoke_all('advagg_changed_files', $files, $types);
        // Clear out the full aggregates cache.
        foreach ($types as $ext => $bool) {
            if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
                $variables = array(
                    '@ext' => print_r($ext, TRUE),
                );
                watchdog('advagg-debug', 'Clearing cache advagg:@ext: in cache_advagg_aggregates.', $variables, WATCHDOG_DEBUG);
            }
            cache_clear_all('advagg:' . $ext . ':', 'cache_advagg_aggregates', TRUE);
        }
    }
    // Return what was done.
    return $results;
}

/**
 * Given a filename hash get back all aggregates that include it.
 *
 * @param string $filename_hash
 *   Hash of the filename.
 * @param bool $cid_only
 *   Set to TRUE to only have cache ids returned.
 *
 * @return array
 *   Array of aggregates that use this file.
 */
function advagg_get_aggregates_using_file($filename_hash, $cid_only = FALSE) {
    // Create main query for the advagg_aggregates table.
    $query = db_select('advagg_aggregates', 'aa')->condition('aa.filename_hash', $filename_hash);
    // Create join query for the advagg_aggregates_versions table.
    $query->join('advagg_aggregates_versions', 'aav', 'aa.aggregate_filenames_hash = aav.aggregate_filenames_hash AND aav.atime > 0');
    $query = $query->fields('aav', array(
        'aggregate_filenames_hash',
        'aggregate_contents_hash',
    ));
    $query->comment('Query called from ' . __FUNCTION__ . '()');
    $results = $query->execute();
    // Put results into $aggregates array.
    $aggregates = array();
    foreach ($results as $row) {
        $row = (array) $row;
        $cid = 'advagg:db:' . $row['aggregate_filenames_hash'] . ADVAGG_SPACE . $row['aggregate_contents_hash'];
        if ($cid_only) {
            $aggregates[] = $cid;
        }
        else {
            $row['cid'] = $cid;
            $aggregates[] = $row;
        }
    }
    return $aggregates;
}

/**
 * Get all CSS/JS advagg files.
 *
 * @param array $options
 *   Array of options to pass along to file_scan_directory().
 *
 * @return array
 *   Array of css and js files.
 */
function advagg_get_all_files(array $options = array()) {
    list($css_path, $js_path) = advagg_get_root_files_dir();
    $options += array(
        'nomask' => '/(\\.\\.?|CVS|\\.gz|\\.br)$/',
    );
    // Get a list of files.
    $css_files = file_scan_directory($css_path[0], '/.*/', $options);
    $js_files = file_scan_directory($js_path[0], '/.*/', $options);
    return array(
        $css_files,
        $js_files,
    );
}

/**
 * Scan CSS/JS advagg dir and remove that file if atime is grater than 30 days.
 *
 * @return array
 *   Array of files that got removed.
 */
function advagg_delete_stale_aggregates() {
    list($css_files, $js_files) = advagg_get_all_files();
    // Make the advagg_get_hashes_from_filename() function available.
    module_load_include('inc', 'advagg', 'advagg.missing');
    $css_files = advagg_delete_files_if_stale($css_files);
    $js_files = advagg_delete_files_if_stale($js_files);
    return array(
        $css_files,
        $js_files,
    );
}

/**
 * Given an array of files remove that file if atime is grater than 30 days.
 *
 * @param array $files
 *   Array of files returned by file_scan_directory.
 *
 * @return array
 *   Array of files that got removed.
 */
function advagg_delete_files_if_stale(array $files) {
    // Array used to record what files were deleted.
    $kill_list = array();
    foreach ($files as $uri => $file) {
        // Get info on file.
        $filename = $file->filename;
        $data = advagg_get_hashes_from_filename($filename);
        if (is_array($data)) {
            list(, $aggregate_filenames_hash, $aggregate_contents_hash) = $data;
        }
        else {
            // Can not get data on file, remove it.
            $kill_list[] = advagg_delete_file_by_uri($uri);
            continue;
        }
        // Get atime of file.
        $atime = advagg_get_atime($aggregate_filenames_hash, $aggregate_contents_hash, $uri);
        if (empty($atime)) {
            $kill_list[] = advagg_delete_file_by_uri($uri);
            continue;
        }
        // Default stale file threshold is 30 days.
        if (REQUEST_TIME - $atime > variable_get('drupal_stale_file_threshold', 2592000)) {
            $kill_list[] = advagg_delete_file_by_uri($uri);
            continue;
        }
    }
    // Let other modules know about the removed files.
    // Call hook_advagg_removed_aggregates().
    module_invoke_all('advagg_removed_aggregates', $kill_list);
    return $kill_list;
}

/**
 * Scan CSS/JS advagg dir and remove that file if it is empty.
 *
 * @return array
 *   Array of files that got removed.
 */
function advagg_delete_empty_aggregates() {
    list($css_files, $js_files) = advagg_get_all_files();
    $css_files = advagg_delete_files_if_empty($css_files);
    $js_files = advagg_delete_files_if_empty($js_files);
    return array(
        $css_files,
        $js_files,
    );
}

/**
 * Given an array of files remove that file if it is empty.
 *
 * @param array $files
 *   Array of files returned by file_scan_directory.
 *
 * @return array
 *   Array of files that got removed.
 */
function advagg_delete_files_if_empty(array $files) {
    // Array used to record what files were deleted.
    $kill_list = array();
    foreach ($files as $uri => $file) {
        // Ignore temp files.  There's a separate process for cleaning those up.
        if (strpos($uri, '/advagg_file_') !== FALSE) {
            continue;
        }
        $size = filesize($uri);
        if ($size === 0) {
            $kill_list[] = advagg_delete_file_by_uri($uri);
            continue;
        }
    }
    // Let other modules know about the removed files.
    // Call hook_advagg_removed_aggregates().
    module_invoke_all('advagg_removed_aggregates', $kill_list);
    return $kill_list;
}

/**
 * Delete a file, and any compressed versions.
 *
 * @param string $uri
 *   URI of the file to delete.
 *
 * @return string
 *   The given URI.
 */
function advagg_delete_file_by_uri($uri) {
    if (file_exists($uri)) {
        file_unmanaged_delete($uri);
    }
    if (file_exists($uri . '.gz')) {
        file_unmanaged_delete($uri . '.gz');
    }
    if (file_exists($uri . '.br')) {
        file_unmanaged_delete($uri . '.br');
    }
    return $uri;
}

/**
 * Perform a cache_clear_all on all bins returned by advagg_flush_caches(TRUE).
 *
 * @param bool $push_new_changes
 *   FALSE: Do not scan for changes.
 */
function advagg_flush_all_cache_bins($push_new_changes = TRUE) {
    $bins = advagg_flush_caches(TRUE, $push_new_changes);
    foreach ($bins as $bin) {
        cache_clear_all('*', $bin, TRUE);
    }
}

/**
 * Remove all files from the advagg CSS/JS directories.
 *
 * @param bool $kill_htaccess
 *   Set to TRUE to remove the htaccess files as well.
 *
 * @return array
 *   Array of all files removed.
 */
function advagg_remove_all_aggregated_files($kill_htaccess = FALSE) {
    $options = array(
        'callback' => 'file_unmanaged_delete',
        'nomask' => '/(\\.\\.?|CVS)$/',
    );
    list($css_files, $js_files) = advagg_get_all_files($options);
    // Let other modules know about the removed files.
    // Call hook_advagg_removed_aggregates().
    module_invoke_all('advagg_removed_aggregates', $css_files);
    module_invoke_all('advagg_removed_aggregates', $js_files);
    // Remove the htaccess files as well.
    if ($kill_htaccess) {
        list($css_path, $js_path) = advagg_get_root_files_dir();
        if (file_exists($css_path[0] . '/.htaccess')) {
            file_unmanaged_delete($css_path[0] . '/.htaccess');
            $css_files[] = $css_path[0] . '/.htaccess';
        }
        if (file_exists($js_path[0] . '/.htaccess')) {
            file_unmanaged_delete($js_path[0] . '/.htaccess');
            $js_files[] = $js_path[0] . '/.htaccess';
        }
    }
    return array(
        $css_files,
        $js_files,
    );
}

/**
 * Increment the advagg_global_counter variable by one.
 *
 * @todo Allow this value to be kept in sync across a multisite.
 *
 * @return int
 *   New value of advagg_global_counter.
 */
function advagg_increment_global_counter() {
    $new_value = advagg_get_global_counter() + 1;
    variable_set('advagg_global_counter', $new_value);
    return $new_value;
}

/**
 * Scan for missing files and remove the associated entries in the database.
 *
 * @return array
 *   Array of what files were cleared out of the database.
 */
function advagg_remove_missing_files_from_db() {
    $missing_files = array();
    $deleted = array();
    // Get all files stored in the database.
    $result = db_select('advagg_files', 'af')->fields('af')
        ->execute();
    if (empty($result)) {
        return $deleted;
    }
    // Find missing files.
    module_load_include('inc', 'advagg', 'advagg');
    foreach ($result as $row) {
        $row = (array) $row;
        $info = advagg_get_info_on_file($row['filename'], TRUE);
        // Make sure file exists.
        if (empty($info['content_hash'])) {
            $info += advagg_get_aggregates_using_file($info['filename_hash']);
            $missing_files[$row['filename']] = $info;
            continue;
        }
    }
    if (empty($missing_files)) {
        return $deleted;
    }
    // Remove missing file database entries.
    $types = array();
    foreach ($missing_files as $filename => $data) {
        // Setup this run.
        $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
        $advagg_files_del = 0;
        $advagg_aggregates_del = 0;
        $advagg_aggregates_versions_del = 0;
        $clean_sweep = TRUE;
        $filename_hash = '';
        // Scan the data.
        foreach ($data as $key => $values) {
            if (!is_numeric($key)) {
                $filename_hash = $values;
            }
            else {
                // Remove the entry from the database if this aggregate has not been
                // accessed in the last 2 weeks.
                $can_delete = db_delete('advagg_aggregates_versions')->condition('aggregate_filenames_hash', $values['aggregate_filenames_hash'])
                    ->condition('atime', REQUEST_TIME - variable_get('advagg_remove_missing_files_from_db_time', ADVAGG_REMOVE_MISSING_FILES_FROM_DB_TIME), '<')
                    ->execute();
                if ($can_delete > 0) {
                    $advagg_aggregates_versions_del += $can_delete;
                    $advagg_aggregates_del += db_delete('advagg_aggregates')->condition('aggregate_filenames_hash', $values['aggregate_filenames_hash'])
                        ->execute();
                }
                else {
                    $clean_sweep = FALSE;
                }
                // Clear the cache.
                cache_clear_all($values['cid'], 'cache_advagg_info', FALSE);
            }
        }
        // Remove the file entry if all aggregates referencing it have been removed.
        if ($clean_sweep) {
            $advagg_files_del += db_delete('advagg_files')->condition('filename_hash', $filename_hash)
                ->execute();
            // Add info to array.
            if (!empty($advagg_files_del) || !empty($advagg_aggregates_versions_del) || !empty($advagg_aggregates_del)) {
                $types[$ext] = TRUE;
                $deleted[$filename] = array(
                    'advagg_files' => $advagg_files_del,
                    'advagg_aggregates_versions' => $advagg_aggregates_versions_del,
                    'advagg_aggregates' => $advagg_aggregates_del,
                );
            }
        }
    }
    // If something was deleted, clear the full aggregates cache.
    if (!empty($deleted)) {
        foreach ($types as $ext => $bool) {
            cache_clear_all('advagg:' . $ext . ':', 'cache_advagg_aggregates', TRUE);
        }
    }
    // Return what was deleted.
    return $deleted;
}

/**
 * Scan CSS/JS advagg dir and remove file if there is no associated db record.
 *
 * @return array
 *   Array of files that got removed.
 */
function advagg_delete_orphaned_aggregates() {
    list($css_files, $js_files) = advagg_get_all_files();
    // Make the advagg_get_hashes_from_filename() function available.
    module_load_include('inc', 'advagg', 'advagg.missing');
    $css_files = advagg_delete_files_if_orphaned($css_files);
    $js_files = advagg_delete_files_if_orphaned($js_files);
    return array(
        $css_files,
        $js_files,
    );
}

/**
 * Given an array of files remove that file if there is no associated db record.
 *
 * @param array $files
 *   Array of files returned by file_scan_directory.
 *
 * @return array
 *   Array of files that got removed.
 */
function advagg_delete_files_if_orphaned(array $files) {
    // Get the uri for the advagg_css/parts directory.
    list($css_path) = advagg_get_root_files_dir();
    $parts_uri = $css_path[0] . '/parts/';
    // Array used to record what files were deleted.
    $kill_list = $keyed_file_list = array();
    // Create a listing of all file names and associated hashes.
    foreach ($files as $uri => $file) {
        // Get info on file.
        $data = advagg_get_hashes_from_filename($file->filename, TRUE);
        if (is_array($data)) {
            list(, $aggregate_filenames_hash) = $data;
            // Check to see if the file is in the database.
            $keyed_file_list[$aggregate_filenames_hash] = $uri;
        }
        else {
            // Check to see if this is a parts css file.
            $start = strpos($file->uri, $parts_uri);
            if ($start !== FALSE) {
                // Get the original filename.
                $original_file = substr($file->uri, $start + strlen($parts_uri));
                $original_file = preg_replace('/(.\\d+\\.css)$/i', '.css', $original_file);
                if (file_exists($original_file)) {
                    // Original file exists, do not delete.
                    continue;
                }
            }
            // Can not get data on file, remove it.
            $kill_list[] = $uri;
            continue;
        }
    }
    if (!empty($keyed_file_list)) {
        $filenames_hash = array_keys($keyed_file_list);
        $aggregates_in_database = array();
        // Process in chunks when a large array is passed.
        do {
            // Check if the aggregate_filenames_hash exists in the database.
            $aggregates_in_database += db_select('advagg_aggregates_versions', 'av')->fields('av', array(
                'aggregate_filenames_hash',
            ))
                ->condition('av.aggregate_filenames_hash', array_splice($filenames_hash, 0, 1000), 'IN')
                ->distinct()
                ->execute()
                ->fetchAllAssoc('aggregate_filenames_hash');
        } while (count($filenames_hash));
        // Get values not found in the database.
        $to_delete = array_values(array_diff_key($keyed_file_list, $aggregates_in_database));
        // Add the file uri to the kill list.
        $kill_list = array_merge($kill_list, $to_delete);
    }
    if (!empty($kill_list)) {
        foreach ($kill_list as $uri) {
            advagg_delete_file_by_uri($uri);
        }
    }
    // Let other modules know about the removed files.
    // Call hook_advagg_removed_aggregates().
    module_invoke_all('advagg_removed_aggregates', $kill_list);
    return $kill_list;
}

/**
 * Delete aggregates that have not been accessed in the last 6 weeks.
 *
 * @return int
 *   Count of the number of rows removed from the databases.
 */
function advagg_remove_old_unused_aggregates() {
    $advagg_aggregates_versions_del = 0;
    $advagg_aggregates_del = 0;
    // Find orphaned aggregate versions entries.
    // Create main query.
    $query = db_select('advagg_aggregates_versions', 'aav')->fields('aav', array(
        'aggregate_filenames_hash',
    ))
        ->groupBy('aav.aggregate_filenames_hash');
    // Create join and add in query comment.
    $query->leftjoin('advagg_aggregates', 'aa', 'aa.aggregate_filenames_hash=aav.aggregate_filenames_hash');
    $query->isNull('aa.aggregate_filenames_hash');
    $query->comment('Query called from ' . __FUNCTION__ . '()');
    $results = $query->execute();
    // If we have an orphaned db entry, delete it.
    if (!empty($results)) {
        foreach ($results as $row) {
            $advagg_aggregates_versions_del += db_delete('advagg_aggregates_versions')->condition('aggregate_filenames_hash', $row->aggregate_filenames_hash)
                ->execute();
        }
    }
    // Delete aggregate versions that have not been accessed in the last 45 days.
    $advagg_aggregates_versions_del += db_delete('advagg_aggregates_versions')->condition('atime', REQUEST_TIME - variable_get('advagg_remove_old_unused_aggregates_time', ADVAGG_REMOVE_OLD_UNUSED_AGGREGATES_TIME), '<')
        ->execute();
    // See if any aggregates are orphaned now.
    // Create main query.
    $query = db_select('advagg_aggregates', 'aa')->fields('aa', array(
        'aggregate_filenames_hash',
    ))
        ->groupBy('aa.aggregate_filenames_hash');
    // Create join and add in query comment.
    $query->leftjoin('advagg_aggregates_versions', 'aav', 'aa.aggregate_filenames_hash=aav.aggregate_filenames_hash');
    $query->isNull('aav.aggregate_filenames_hash');
    $query->comment('Query called from ' . __FUNCTION__ . '()');
    $results = $query->execute();
    // If we have an orphaned db entry, delete it.
    if (!empty($results)) {
        foreach ($results as $row) {
            $advagg_aggregates_del += db_delete('advagg_aggregates')->condition('aggregate_filenames_hash', $row->aggregate_filenames_hash)
                ->execute();
        }
    }
    // Return the total count of entires removed from the database.
    return $advagg_aggregates_versions_del + $advagg_aggregates_del;
}

/**
 * Delete orphaned/expired advagg locks from the semaphore database table.
 *
 * @return int
 *   Count of the number of rows removed from the databases.
 */
function advagg_cleanup_semaphore_table() {
    // Let expiration times vary by 5 minutes.
    $fuzz_factor = 300;
    $results = db_delete('semaphore')->condition('name', db_like('advagg_') . '%', 'LIKE')
        ->condition('expire', REQUEST_TIME - $fuzz_factor, '<')
        ->execute();
    return $results;
}

/**
 * Delete leftover temp files.
 *
 * @return int
 *   Count of the number of files removed
 */
function advagg_remove_temp_files() {
    // Make sure advagg_get_root_files_dir() is available.
    drupal_load('module', 'advagg');
    // Make sure advagg_install_delete_empty_file_if_stale() is available.
    module_load_include('install', 'advagg', 'advagg');
    // Get the advagg paths.
    $advagg_path = advagg_get_root_files_dir();
    $total_count = 0;
    // Get the top level path.
    $top_level = substr($advagg_path[0][0], 0, strpos($advagg_path[0][0], 'advagg_css'));
    // Remove empty temp files from public://.
    $files = file_scan_directory($top_level, '/(?!files$)file.*|fil.*\\.tmp/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_install_delete_empty_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Remove empty temp files from public://advagg_css.
    $files = file_scan_directory($advagg_path[0][0], '/file.*|fil.*\\.tmp/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_install_delete_empty_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Remove empty temp files from public://advagg_js.
    $files = file_scan_directory($advagg_path[1][0], '/file.*|fil.*\\.tmp/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_install_delete_empty_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Remove empty temp files from public://.
    $files = file_scan_directory($top_level, '/file_advagg_.*/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_delete_temp_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Remove empty temp files from public://advagg_css.
    $files = file_scan_directory($advagg_path[0][0], '/file_advagg_.*/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_delete_temp_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Remove empty temp files from public://advagg_js.
    $files = file_scan_directory($advagg_path[1][0], '/file_advagg_.*/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_delete_temp_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Remove empty temp files from public://.
    $files = file_scan_directory($top_level, '/advagg_file_.*/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_delete_temp_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Remove empty temp files from public://advagg_css.
    $files = file_scan_directory($advagg_path[0][0], '/advagg_file_.*/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_delete_temp_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Remove empty temp files from public://advagg_js.
    $files = file_scan_directory($advagg_path[1][0], '/advagg_file_.*/', array(
        'recurse' => FALSE,
        'callback' => 'advagg_delete_temp_file_if_stale',
    ));
    foreach ($files as $key => $file) {
        if (file_exists($file->uri)) {
            unset($files[$key]);
        }
    }
    $total_count += count($files);
    // Output info.
    return $total_count;
}

/**
 * Refresh all locale files.
 *
 * @return int
 *   Count of the number of files removed
 */
function advagg_refresh_all_locale_files() {
    $locale_files = array();
    if (!module_exists('locale')) {
        return $locale_files;
    }
    $results = db_select('advagg_files', 'af')->fields('af')
        ->condition('af.filetype', 'js')
        ->condition('af.filesize', 0, '>')
        ->execute();
    $javascript = array();
    foreach ($results as $row) {
        $javascript[] = array(
            'type' => 'file',
            'data' => $row->filename,
        );
    }
    if (!empty($javascript)) {
        $javascript_before = $javascript;
        $language_before = $GLOBALS['language'];
        $language_list = language_list();
        foreach ($language_list as $lang) {
            if ($lang->enabled) {
                $GLOBALS['language'] = $lang;
                $javascript = $javascript_before;
                locale_js_alter($javascript);
                $locale_file = array_diff_key($javascript, $javascript_before);
                $locale_files += $locale_file;
            }
        }
        $GLOBALS['language'] = $language_before;
    }
    return $locale_files;
}

/**
 * Callback to delete files if modified more than 60 seconds ago.
 *
 * @param string $uri
 *   Location of the file to check.
 */
function advagg_delete_temp_file_if_stale($uri) {
    // Set stale file threshold to 60 seconds.
    if (REQUEST_TIME - filemtime($uri) > 60) {
        file_unmanaged_delete($uri);
    }
}

/**
 * See if any of the subfiles has changed.
 *
 * @param string $filename
 *   Name of the file that is related to the subfiles.
 * @param array $subfiles
 *   An array of files to check for changes.
 * @param string $keyname
 *   Under what key to save the info on the files.
 * @param bool $save_changes
 *   If TRUE then the changes will be updated in the cache.
 *
 * @return bool
 *   TRUE if one of the subfiles has changed.
 */
function advagg_detect_subfile_changes($filename, array $subfiles, $keyname, $save_changes = FALSE) {
    // Get the info on this file from the cache.
    module_load_include('inc', 'advagg', 'advagg');
    $info = advagg_get_info_on_file($filename);
    $hash_id = 'advagg:subfiles:' . $keyname . ':' . $info['filename_hash'];
    if (!isset($info[$keyname])) {
        // Pull up the info from the database if missing from the cache.
        $info[$keyname] = advagg_get_hash_settings($hash_id);
    }
    $subfile_changed = array();
    // Check every subfile seeing if they have changed.
    foreach ($subfiles as $subfile) {
        $current_file_info = $defaults = array(
            'hash' => '',
            'size' => 0,
            'mtime' => 0,
        );
        // Get the currently saved info on this file.
        $saved_file_info = isset($info[$keyname][$subfile]) ? $info[$keyname][$subfile] : array();
        $saved_file_info += $defaults;
        // Get the current info on the file.
        if (file_exists($subfile)) {
            $current_file_info = array(
                'hash' => drupal_hash_base64((string) @advagg_file_get_contents($subfile)),
                'size' => filesize($subfile),
                'mtime' => filemtime($subfile),
            );
        }
        // Set the info in case a save happens.
        $info[$keyname][$subfile] = $current_file_info;
        // Check for any differences.
        $diff = array_diff_assoc($saved_file_info, $current_file_info);
        if (!empty($diff)) {
            $subfile_changed[$subfile] = $diff;
        }
    }
    if (!empty($subfile_changed) && $save_changes) {
        $cache_id = 'advagg:file:' . $info['filename_hash'];
        // Set static cache.
        $filename_hashes =& drupal_static('advagg_get_info_on_file');
        $filename_hashes[$cache_id] = $info;
        // Set drupal cache.
        cache_set($cache_id, $info, 'cache_advagg_info', CACHE_PERMANENT);
        // Save to database.
        advagg_set_hash_settings($hash_id, $info[$keyname]);
    }
    return $subfile_changed;
}

Functions

Titre Deprecated Résumé
advagg_cleanup_semaphore_table Delete orphaned/expired advagg locks from the semaphore database table.
advagg_delete_empty_aggregates Scan CSS/JS advagg dir and remove that file if it is empty.
advagg_delete_files_if_empty Given an array of files remove that file if it is empty.
advagg_delete_files_if_orphaned Given an array of files remove that file if there is no associated db record.
advagg_delete_files_if_stale Given an array of files remove that file if atime is grater than 30 days.
advagg_delete_file_by_uri Delete a file, and any compressed versions.
advagg_delete_orphaned_aggregates Scan CSS/JS advagg dir and remove file if there is no associated db record.
advagg_delete_stale_aggregates Scan CSS/JS advagg dir and remove that file if atime is grater than 30 days.
advagg_delete_temp_file_if_stale Callback to delete files if modified more than 60 seconds ago.
advagg_detect_subfile_changes See if any of the subfiles has changed.
advagg_flush_all_cache_bins Perform a cache_clear_all on all bins returned by advagg_flush_caches(TRUE).
advagg_get_aggregates_using_file Given a filename hash get back all aggregates that include it.
advagg_get_all_files Get all CSS/JS advagg files.
advagg_increment_global_counter Increment the advagg_global_counter variable by one.
advagg_push_new_changes Flush the correct caches so CSS/JS changes go live.
advagg_refresh_all_locale_files Refresh all locale files.
advagg_remove_all_aggregated_files Remove all files from the advagg CSS/JS directories.
advagg_remove_missing_files_from_db Scan for missing files and remove the associated entries in the database.
advagg_remove_old_unused_aggregates Delete aggregates that have not been accessed in the last 6 weeks.
advagg_remove_temp_files Delete leftover temp files.
advagg_scan_for_changes Uses the database to scan CSS/JS files for changes.