Advanced CSS/JS aggregation module.

These functions are needed for cache misses.

Fichier

./advagg.inc

View source
<?php


/**
 * @file
 * Advanced CSS/JS aggregation module.
 *
 * These functions are needed for cache misses.
 */

/**
 * Insert/Update data in advagg tables.
 *
 * Tables: advagg_files, advagg_aggregates, advagg_aggregates_versions.
 *
 * @param array $files
 *   List of files in the aggregate as well as the aggregate name.
 * @param string $type
 *   String; css or js.
 * @param int $root
 *   Is this a root aggregate.
 *
 * @return bool
 *   Return TRUE if anything was written to the database.
 */
function advagg_insert_update_db(array $files, $type, $root) {
    // Record if a database write was done.
    $write_done = FALSE;
    // Loop through all files.
    foreach ($files as $values) {
        // Insert files into the advagg_files table if it doesn't exist.
        // Update if needed.
        if (advagg_insert_update_files($values['files'], $type)) {
            $write_done = TRUE;
        }
        // Insert aggregate into the advagg_aggregates table if it doesn't exist.
        if (advagg_insert_aggregate($values['files'], $values['aggregate_filenames_hash'])) {
            $write_done = TRUE;
        }
        // Insert aggregate version information into advagg_aggregates_versions.
        if (advagg_insert_aggregate_version($values['aggregate_filenames_hash'], $values['aggregate_contents_hash'], $root)) {
            $write_done = TRUE;
        }
    }
    return $write_done;
}

/**
 * Insert data in the advagg_aggregates_versions table.
 *
 * @param string $aggregate_filenames_hash
 *   Hash of the groupings of files.
 * @param string $aggregate_contents_hash
 *   Hash of the files contents.
 * @param int $root
 *   Is this a root aggregate.
 *
 * @return bool
 *   Return TRUE if anything was written to the database.
 */
function advagg_insert_aggregate_version($aggregate_filenames_hash, $aggregate_contents_hash, $root) {
    // Info for the DB.
    $record = array(
        'aggregate_filenames_hash' => $aggregate_filenames_hash,
        'aggregate_contents_hash' => $aggregate_contents_hash,
        'atime' => 0,
        'root' => $root,
    );
    // Save new aggregate into the database if it does not exist.
    $return = db_merge('advagg_aggregates_versions')->key(array(
        'aggregate_filenames_hash' => $record['aggregate_filenames_hash'],
        'aggregate_contents_hash' => $record['aggregate_contents_hash'],
    ))
        ->insertFields($record)
        ->execute();
    return $return;
}

/**
 * Insert/Update data in the advagg_aggregates table.
 *
 * @param array $files
 *   List of files in the aggregate including files meta data.
 * @param string $aggregate_filenames_hash
 *   Hash of the groupings of files.
 *
 * @return bool
 *   Return TRUE if anything was written to the database.
 */
function advagg_insert_aggregate(array $files, $aggregate_filenames_hash) {
    // Record if a database write was done.
    $write_done = FALSE;
    // Check if the aggregate is in the database.
    $files_in_db = array();
    $query = db_select('advagg_aggregates', 'aa')->fields('aa', array(
        'filename_hash',
    ))
        ->condition('aggregate_filenames_hash', $aggregate_filenames_hash)
        ->orderBy('aa.porder', 'ASC')
        ->execute();
    foreach ($query as $row) {
        $files_in_db[$row->filename_hash] = (array) $row;
    }
    $count = 0;
    foreach ($files as $file_meta_data) {
        ++$count;
        // Skip if the file already exists in the aggregate.
        if (!empty($files_in_db[$file_meta_data['filename_hash']])) {
            continue;
        }
        // Store settings for this file that depend on how it was added.
        $settings = array();
        if (isset($file_meta_data['media_query'])) {
            $settings['media'] = $file_meta_data['media_query'];
        }
        // Write record into the database.
        $record = array(
            'aggregate_filenames_hash' => $aggregate_filenames_hash,
            'filename_hash' => $file_meta_data['filename_hash'],
            'porder' => $count,
            'settings' => serialize($settings),
        );
        $return = db_merge('advagg_aggregates')->key(array(
            'aggregate_filenames_hash' => $record['aggregate_filenames_hash'],
            'filename_hash' => $record['filename_hash'],
        ))
            ->insertFields($record)
            ->execute();
        if ($return) {
            $write_done = TRUE;
        }
    }
    return $write_done;
}

/**
 * Insert/Update data in the advagg_files table.
 *
 * @param array $files
 *   List of files in the aggregate including files meta data.
 * @param string $type
 *   String; css or js.
 *
 * @return bool
 *   Return TRUE if anything was written to the database.
 */
function advagg_insert_update_files(array $files, $type) {
    // Record if a database write was done.
    $write_done = FALSE;
    $filename_hashes = array();
    foreach ($files as $file_meta_data) {
        $filename_hashes[] = $file_meta_data['filename_hash'];
    }
    $files_in_db = array();
    if (!empty($filename_hashes)) {
        $query = db_select('advagg_files', 'af')->fields('af')
            ->condition('filename_hash', $filename_hashes)
            ->execute();
        foreach ($query as $row) {
            $files_in_db[$row->filename] = (array) $row;
        }
    }
    // Make drupal_get_installed_schema_version() available.
    include_once DRUPAL_ROOT . '/includes/install.inc';
    foreach ($files as $filename => $file_meta_data) {
        // Create record.
        $record = array(
            'filename' => $filename,
            'filename_hash' => $file_meta_data['filename_hash'],
            'content_hash' => $file_meta_data['content_hash'],
            'filetype' => $type,
            'filesize' => $file_meta_data['filesize'],
            'mtime' => $file_meta_data['mtime'],
            'linecount' => $file_meta_data['linecount'],
        );
        try {
            // Check the file in the database.
            if (empty($files_in_db[$filename])) {
                // Add in filesize_processed if the schema is 7210 or higher.
                if (drupal_get_installed_schema_version('advagg') >= 7210) {
                    $record['filesize_processed'] = (int) advagg_generate_filesize_processed($filename, $type);
                }
                // Add in use_strict if the schema is 7212 or higher.
                if (drupal_get_installed_schema_version('advagg') >= 7212) {
                    $record['use_strict'] = 0;
                    if ($type === 'js') {
                        $record['use_strict'] = (int) advagg_does_js_start_with_use_strict($filename);
                    }
                }
                // Insert into database.
                $record['changes'] = 1;
                $return = db_merge('advagg_files')->key(array(
                    'filename_hash' => $record['filename_hash'],
                ))
                    ->insertFields($record)
                    ->execute();
                if ($return) {
                    if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
                        $variables = array(
                            '@record' => print_r($record, TRUE),
                        );
                        watchdog('advagg-debug', 'Inserting into db <pre>@record</pre>.', $variables, WATCHDOG_DEBUG);
                    }
                    $write_done = TRUE;
                }
            }
            else {
                // Take changes counter out of the diff equation.
                $changes = $files_in_db[$filename]['changes'];
                unset($files_in_db[$filename]['changes']);
                // If not in strict mode, only use mtime if newer than the existing one.
                if (!variable_get('advagg_strict_mtime_check', ADVAGG_STRICT_MTIME_CHECK)) {
                    // Make sure mtime only moves forward.
                    if ($record['mtime'] <= $files_in_db[$filename]['mtime']) {
                        $record['mtime'] = $files_in_db[$filename]['mtime'];
                    }
                }
                // If something is different, update.
                $diff = array_diff_assoc($record, $files_in_db[$filename]);
                if (!empty($diff)) {
                    $diff['changes'] = $changes + 1;
                    $diff['filename_hash'] = $record['filename_hash'];
                    // Add in filesize_processed if the schema is 7210 or higher.
                    if (drupal_get_installed_schema_version('advagg') >= 7210) {
                        $diff['filesize_processed'] = (int) advagg_generate_filesize_processed($filename, $type);
                    }
                    if (drupal_get_installed_schema_version('advagg') >= 7212) {
                        $diff['use_strict'] = 0;
                        if ($type === 'js') {
                            $diff['use_strict'] = (int) advagg_does_js_start_with_use_strict($filename);
                            if (empty($diff['use_strict'])) {
                                $diff['use_strict'] = 0;
                            }
                        }
                    }
                    $return = db_merge('advagg_files')->key(array(
                        'filename_hash' => $diff['filename_hash'],
                    ))
                        ->fields($diff)
                        ->execute();
                    if ($return) {
                        if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
                            $variables = array(
                                '@diff' => print_r($diff, TRUE),
                            );
                            watchdog('advagg-debug', 'Updating db <pre>@diff</pre>.', $variables, WATCHDOG_DEBUG);
                        }
                        $write_done = TRUE;
                    }
                }
            }
        } catch (PDOException $e) {
            // If it fails we don't care, the file was added to the table by another
            // process then.
            // Still log it if in development mode.
            if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
                watchdog('advagg', 'Development Mode - Caught PDO Exception: <code>@info</code>', array(
                    '@info' => $e,
                ));
            }
        }
    }
    return $write_done;
}

/**
 * Given a filename calculate the processed filesize.
 *
 * @param string $filename
 *   String; filename containing path information as well.
 * @param string $type
 *   String; css or js.
 *
 * @return int
 *   Processed filesize.
 */
function advagg_generate_filesize_processed($filename, $type) {
    $files =& drupal_static(__FUNCTION__, array());
    if (!isset($files[$type][$filename])) {
        // Make advagg_get_*_aggregate_contents() available.
        module_load_include('inc', 'advagg', 'advagg.missing');
        $aggregate_settings = advagg_current_hooks_hash_array();
        $file_aggregate = array(
            $filename => array(),
        );
        if ($type === 'css') {
            list($contents) = advagg_get_css_aggregate_contents($file_aggregate, $aggregate_settings);
        }
        elseif ($type === 'js') {
            list($contents) = advagg_get_js_aggregate_contents($file_aggregate, $aggregate_settings);
        }
        if (!empty($contents)) {
            $files[$type][$filename] = strlen(gzencode($contents, 9, FORCE_GZIP));
        }
        else {
            $files[$type][$filename] = 0;
        }
    }
    return $files[$type][$filename];
}

/**
 * Given a js string, see if "use strict"; is the first thing ran.
 *
 * @param string $filename
 *   String; filename containing path information as well.
 *
 * @return bool
 *   True if "use strict"; is the first thing ran.
 */
function advagg_does_js_start_with_use_strict($filename) {
    $files =& drupal_static(__FUNCTION__, array());
    if (!isset($files[$filename])) {
        // Make advagg_get_*_aggregate_contents() available.
        module_load_include('inc', 'advagg', 'advagg.missing');
        $aggregate_settings = advagg_current_hooks_hash_array();
        $file_aggregate = array(
            $filename => array(),
        );
        list($contents) = advagg_get_js_aggregate_contents($file_aggregate, $aggregate_settings);
        // See if the js file starts with "use strict";.
        // Trim the JS down to 24kb.
        $length = variable_get('advagg_js_header_length', ADVAGG_JS_HEADER_LENGTH);
        $header = advagg_get_js_header($contents, $length);
        // Look for the string.
        $use_strict = stripos($header, '"use strict";');
        $strict_js = FALSE;
        if ($use_strict === FALSE) {
            $use_strict = stripos($header, "'use strict';");
        }
        if ($use_strict !== FALSE) {
            if ($use_strict == 0) {
                $strict_js = TRUE;
            }
            else {
                // Get all text before "use strict";.
                $substr = substr($header, 0, $use_strict);
                // Check if there are any comments.
                $single_line_comment = strpos($substr, '//');
                $multi_line_comment = strpos($substr, '/*');
                $in_function = strpos($substr, '{');
                if ($single_line_comment !== FALSE || $multi_line_comment !== FALSE) {
                    // Remove js comments and try again.
                    advagg_remove_js_comments($header);
                    // Look for the string.
                    $use_strict = stripos($header, '"use strict";');
                    if ($use_strict === FALSE) {
                        $use_strict = stripos($header, "'use strict';");
                    }
                    // Get all text before "use strict"; with comments removed.
                    $substr = substr($header, 0, $use_strict);
                    // Check if there is a function before use strict.
                    $in_function = strpos($substr, '{');
                }
                if ($in_function === FALSE) {
                    $strict_js = TRUE;
                }
            }
        }
        $files[$filename] = $strict_js;
    }
    return $files[$filename];
}

/**
 * Read only the first 8192 bytes to get the file header.
 *
 * @param string $content
 *   JS string to cut.
 * @param int $length
 *   The number of bytes to grab. See advagg_js_header_length variable.
 *
 * @return string
 *   The shortened JS string.
 */
function advagg_get_js_header($content, $length) {
    $content = trim($content);
    // Only grab the first X bytes.
    if (function_exists('mb_strcut')) {
        $header = mb_strcut($content, 0, $length);
    }
    else {
        $header = substr($content, 0, $length);
    }
    return $header;
}

/**
 * Remove comments from JavaScript.
 *
 * @param string $content
 *   JS string to minify.
 */
function advagg_remove_js_comments(&$content) {
    // Remove comments.
    $content = preg_replace('/(?:(?:\\/\\*(?:[^*]|(?:\\*+[^*\\/]))*\\*+\\/)|(?:(?<!\\:|\\\\|\'|\\")\\/\\/.*))/', '', $content);
    // Remove space after colons.
    // Remove space before equal signs.
    // Remove space after equal signs.
    $content = str_replace(array(
        ': ',
        ' =',
        '= ',
    ), array(
        ':',
        '=',
        '=',
    ), $content);
    // Remove excessive whitespace.
    $content = str_replace(array(
        "\r\n\r\n",
        "\n\n",
        "\r\r",
        '\\t',
        '  ',
        '    ',
        '    ',
    ), '', $content);
}

/**
 * Given a group of files calculate what the aggregate filename will be.
 *
 * @param array $groups
 *   An array of CSS/JS groups.
 * @param string $type
 *   String; css or js.
 *
 * @return array
 *   Files array.
 */
function advagg_generate_filenames(array $groups, $type) {
    $files = array();
    foreach ($groups as $data) {
        foreach ($data as $files_with_meta_data) {
            // Get the aggregate filename and info about each file.
            $aggregate_info = advagg_get_aggregate_info_from_files($type, $files_with_meta_data);
            $values['files'] = $aggregate_info[1];
            $values['aggregate_filenames_hash'] = $aggregate_info[2];
            $values['aggregate_contents_hash'] = $aggregate_info[3];
            // Add information to the files array.
            $files[$aggregate_info[0]] = $values;
        }
    }
    return $files;
}

/**
 * Given a group of files calculate various hashes and gather meta data.
 *
 * @param string $type
 *   String; css or js.
 * @param array $files_with_meta_data
 *   An array of CSS/JS files.
 *
 * @return array
 *   array containing $aggregate_filename, $filenames,
 *   $aggregate_filenames_hash, $aggregate_contents_hash
 */
function advagg_get_aggregate_info_from_files($type, array $files_with_meta_data) {
    $filename_hashes = array();
    $content_hashes = array();
    $filenames = array();
    $files_info_filenames = array();
    foreach ($files_with_meta_data as $info) {
        if (!empty($info['data']) && is_string($info['data'])) {
            $files_info_filenames[] = $info['data'];
        }
        else {
            watchdog('advagg', 'Bad data key. File info: <code>@finfo</code>', array(
                '@finfo' => var_export($info, TRUE),
            ));
        }
    }
    // Get filesystem data.
    $files_info = advagg_get_info_on_files($files_info_filenames);
    foreach ($files_with_meta_data as $info) {
        // Skip if not a string or key doesn't exist.
        if (!is_string($info['data']) || !array_key_exists($info['data'], $files_info)) {
            continue;
        }
        $filename = $info['data'];
        $info += $files_info[$filename];
        // Skip if file doesn't exist.
        if (empty($info['content_hash'])) {
            continue;
        }
        // Add info to arrays.
        $filename_hashes[] = $info['filename_hash'];
        $content_hashes[] = $info['content_hash'];
        $filenames[$filename] = $info;
    }
    // Generate filename.
    $aggregate_filenames_hash = drupal_hash_base64(implode('', $filename_hashes));
    $aggregate_contents_hash = drupal_hash_base64(implode('', $content_hashes));
    $aggregate_filename = advagg_build_filename($type, $aggregate_filenames_hash, $aggregate_contents_hash);
    return array(
        $aggregate_filename,
        $filenames,
        $aggregate_filenames_hash,
        $aggregate_contents_hash,
    );
}

/**
 * Load cache bin file info in static cache.
 *
 * @param array $files
 *   Array; array of filenames.
 *
 * @return array
 *   $cached_data. key is $cache_id; value is an array which contains
 *
 * @code
 *   'filesize' => filesize($filename),
 *   'mtime' => @filemtime($filename),
 *   'filename_hash' => $filename_hash,
 *   'content_hash' => drupal_hash_base64($file_contents),
 *   'linecount' => $linecount,
 *   'data' => $filename,
 *   'fileext' => $ext,
 * @endcode
 */
function &advagg_load_files_info_into_static_cache(array $files) {
    // Get the static cache of this data.
    $cached_data =& drupal_static('advagg_get_info_on_file');
    // Get the statically cached data for all the given files.
    $cache_ids = array();
    foreach ($files as $file) {
        $cache_id = 'advagg:file:' . advagg_drupal_hash_base64($file);
        if (!empty($cached_data) && !empty($cached_data[$cache_id])) {
            // Make sure the cache_id is included.
            $cached_data[$cache_id]['cache_id'] = $cache_id;
        }
        else {
            $cache_ids[$file] = $cache_id;
        }
    }
    // Get info from the cache back-end next.
    if (!empty($cache_ids)) {
        $values = array_values($cache_ids);
        $cache_hits = cache_get_multiple($values, 'cache_advagg_info');
        if (!empty($cache_hits)) {
            foreach ($cache_hits as $hit) {
                if (!empty($hit->data['data'])) {
                    // Make sure the cache_id is included.
                    $hit->data['cache_id'] = $hit->cid;
                    // Add to static cache.
                    $cached_data[$hit->cid] = $hit->data;
                }
            }
        }
    }
    return $cached_data;
}

/**
 * Given a filename calculate the hash for it. Uses static cache.
 *
 * @param string $file
 *   Filename.
 *
 * @return string
 *   hash of filename.
 */
function advagg_drupal_hash_base64($file) {
    // Get the static cache of this data.
    $cached_data =& drupal_static('advagg_drupal_hash_base64', array());
    if (!array_key_exists($file, $cached_data)) {
        $cached_data[$file] = drupal_hash_base64($file);
    }
    return $cached_data[$file];
}

/**
 * Given a filename calculate various hashes and gather meta data.
 *
 * @param array $files
 *   Array; array of filenames containing path information as well.
 * @param bool $bypass_cache
 *   Bool: TRUE to bypass the cache.
 *
 * @return array
 *   $return['filename'] which contains
 *
 * @code
 *   'filesize' => filesize($filename),
 *   'mtime' => @filemtime($filename),
 *   'filename_hash' => $filename_hash,
 *   'content_hash' => drupal_hash_base64($file_contents),
 *   'linecount' => $linecount,
 *   'data' => $filename,
 *   'fileext' => $ext,
 * @endcode
 */
function advagg_get_info_on_files(array $files, $bypass_cache = FALSE, $run_alter = TRUE) {
    // Get the cached data.
    $cached_data =& advagg_load_files_info_into_static_cache($files);
    // Get basic info on the files.
    $return = array();
    foreach ($files as $file) {
        $filename_hash = advagg_drupal_hash_base64($file);
        $cache_id = 'advagg:file:' . $filename_hash;
        // If we are not bypassing the cache add cached data.
        if ($bypass_cache == FALSE && is_array($cached_data) && array_key_exists($cache_id, $cached_data)) {
            $return[$file] = $cached_data[$cache_id];
            continue;
        }
        // Clear PHP's internal file status cache.
        advagg_clearstatcache($file);
        // Remove file in the cache if it does not exist.
        if (!file_exists($file) || is_dir($file)) {
            if (isset($cached_data[$cache_id])) {
                cache_clear_all($cache_id, 'cache_advagg_info', FALSE);
            }
            // Return filename_hash and data. Empty values for the other keys.
            $return[$file] = array(
                'filesize' => 0,
                'mtime' => 0,
                'filename_hash' => $filename_hash,
                'content_hash' => '',
                'linecount' => 0,
                'data' => $file,
                'cache_id' => $cache_id,
                '#no_cache' => TRUE,
            );
            continue;
        }
        // Get the file contents.
        $file_contents = (string) @advagg_file_get_contents($file);
        $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
        if ($ext !== 'css' && $ext !== 'js') {
            // Get the $ext from the database.
            $row = db_select('advagg_files', 'af')->fields('af')
                ->condition('filename', $file)
                ->execute()
                ->fetchAssoc();
            if (!empty($row['filetype'])) {
                $ext = $row['filetype'];
            }
            if ($ext === 'less') {
                $ext = 'css';
            }
        }
        if ($ext === 'css') {
            // Get the number of selectors.
            $linecount = advagg_count_css_selectors($file_contents);
        }
        else {
            // Get the number of lines.
            $linecount = substr_count($file_contents, "\n");
        }
        // Build meta data array and set cache.
        $return[$file] = array(
            'filesize' => (int) @filesize($file),
            'mtime' => @filemtime($file),
            'filename_hash' => $filename_hash,
            'content_hash' => drupal_hash_base64($file_contents),
            'linecount' => $linecount,
            'data' => $file,
            'fileext' => $ext,
            'cache_id' => $cache_id,
        );
        if (isset($cached_data[$cache_id])) {
            $return[$file] += $cached_data[$cache_id];
        }
    }
    if ($run_alter) {
        // Run hook so other modules can modify the data on these files.
        // Call hook_advagg_get_info_on_files_alter().
        drupal_alter('advagg_get_info_on_files', $return, $cached_data, $bypass_cache);
        // Set the cache and populate return array.
        foreach ($return as $info) {
            // If no cache is empty add/update the cached entry.
            // Update the cache if it is new or something changed.
            if (empty($info['#no_cache']) && !empty($info['cache_id']) && (empty($cached_data[$info['cache_id']]) || $info !== $cached_data[$info['cache_id']])) {
                // CACHE_PERMANENT isn't good here. Use 2 weeks from now + 0-45 days.
                // The random 0 to 45 day addition is to prevent a cache stampede.
                cache_set($info['cache_id'], $info, 'cache_advagg_info', round(REQUEST_TIME + 1209600 + mt_rand(0, 3888000), -3));
            }
            // Update static cache.
            $cached_data[$info['cache_id']] = $info;
        }
    }
    return $return;
}

/**
 * Given a filename calculate various hashes and gather meta data.
 *
 * @param string $filename
 *   String; filename containing path information.
 * @param bool $bypass_cache
 *   (optional) Bool: TRUE to bypass the cache.
 * @param bool $run_alter
 *   (optional) Bool: FALSE to not run drupal_alter.
 *
 * @return array
 *   Array containing key value pairs.
 *
 * @code
 *   'filesize' => filesize($filename),
 *   'mtime' => @filemtime($filename),
 *   'filename_hash' => $filename_hash,
 *   'content_hash' => drupal_hash_base64($file_contents),
 *   'linecount' => $linecount,
 *   'data' => $filename,
 *   'fileext' => $ext,
 * @endcode
 */
function advagg_get_info_on_file($filename, $bypass_cache = FALSE, $run_alter = TRUE) {
    $files_info = advagg_get_info_on_files(array(
        $filename,
    ), $bypass_cache, $run_alter);
    return $files_info[$filename];
}

/**
 * Build the filename.
 *
 * @param string $type
 *   String; css or js.
 * @param string $aggregate_filenames_hash
 *   Hash of the groupings of files.
 * @param string $aggregate_contents_hash
 *   Hash of the files contents.
 * @param string $hooks_hash
 *   Hash value from advagg_get_current_hooks_hash().
 *
 * @return string
 *   String: The filename. No path info.
 */
function advagg_build_filename($type, $aggregate_filenames_hash, $aggregate_contents_hash, $hooks_hash = '') {
    if (empty($hooks_hash)) {
        $hooks_hash = advagg_get_current_hooks_hash();
    }
    return $type . ADVAGG_SPACE . $aggregate_filenames_hash . ADVAGG_SPACE . $aggregate_contents_hash . ADVAGG_SPACE . $hooks_hash . '.' . $type;
}

/**
 * Wrapper around clearstatcache so it can use php 5.3's new features.
 *
 * @param string $filename
 *   String.
 *
 * @return null
 *   value from clearstatcache().
 */
function advagg_clearstatcache($filename = NULL) {
    static $php530;
    if (!isset($php530)) {
        $php530 = version_compare(PHP_VERSION, '5.3.0', '>=');
    }
    if ($php530) {
        return clearstatcache(TRUE, $filename);
    }
    else {
        return clearstatcache();
    }
}

/**
 * Group the CSS/JS into the biggest buckets possible.
 *
 * @param array $files_to_aggregate
 *   An array of CSS/JS groups.
 * @param string $type
 *   String; css or js.
 *
 * @return array
 *   New version of groups.
 */
function advagg_generate_groups(array $files_to_aggregate, $type) {
    $groups = array();
    $count = 0;
    $location = 0;
    $media = '';
    $defer = '';
    $async = '';
    $cache = '';
    $scope = '';
    $use_strict = 0;
    $browsers = array();
    $selector_count = 0;
    // Get CSS limit value.
    $limit_value = variable_get('advagg_ie_css_selector_limiter_value', ADVAGG_IE_CSS_SELECTOR_LIMITER_VALUE);
    if (variable_get('advagg_ie_css_selector_limiter', ADVAGG_IE_CSS_SELECTOR_LIMITER) || variable_get('advagg_resource_hints_dns_prefetch', ADVAGG_RESOURCE_HINTS_DNS_PREFETCH) || variable_get('advagg_resource_hints_preconnect', ADVAGG_RESOURCE_HINTS_PRECONNECT) || variable_get('advagg_resource_hints_preload', ADVAGG_RESOURCE_HINTS_PRELOAD)) {
        $filenames = array();
        foreach ($files_to_aggregate as $data) {
            foreach ($data as $values) {
                foreach ($values['items'] as $file_info) {
                    if (!empty($file_info['data']) && is_string($file_info['data'])) {
                        $filenames[] = $file_info['data'];
                    }
                    else {
                        watchdog('advagg', 'Bad data key. File info: <code>@finfo</code> Group info: <code>@ginfo</code>', array(
                            '@finfo' => var_export($file_info, TRUE),
                            '@ginfo' => var_export($values, TRUE),
                        ));
                    }
                }
            }
        }
        // Get filesystem data.
        $files_info = advagg_get_info_on_files($filenames, TRUE);
    }
    $strict_files = array();
    if ($type == 'js') {
        // Make drupal_get_installed_schema_version() available.
        include_once DRUPAL_ROOT . '/includes/install.inc';
        if (drupal_get_installed_schema_version('advagg') >= 7213) {
            $query = db_select('advagg_files', 'af')->fields('af', array(
                'filename',
                'use_strict',
            ))
                ->condition('use_strict', 1)
                ->execute();
            foreach ($query as $row) {
                $strict_files[$row->filename] = $row->use_strict;
            }
        }
    }
    foreach ($files_to_aggregate as $data) {
        foreach ($data as $values) {
            // Group into the biggest buckets possible.
            $last_ext = '';
            foreach ($values['items'] as $file_info) {
                $parts = array();
                // Check to see if media, browsers, defer, async, cache, or scope has
                // changed from the previous run of this loop.
                $changed = FALSE;
                $ext = isset($file_info['fileext']) ? $file_info['fileext'] : pathinfo($file_info['data'], PATHINFO_EXTENSION);
                $ext = strtolower($ext);
                if ($ext !== 'css' && $ext !== 'js') {
                    if (empty($last_ext)) {
                        // Get the $ext from the database.
                        $row = db_select('advagg_files', 'af')->fields('af')
                            ->condition('filename', $file_info['data'])
                            ->execute()
                            ->fetchAssoc();
                        $ext = $row['filetype'];
                    }
                    else {
                        $ext = $last_ext;
                    }
                }
                $last_ext = $ext;
                if ($ext === 'css') {
                    if (isset($file_info['media'])) {
                        if (variable_get('advagg_combine_css_media', ADVAGG_COMBINE_CSS_MEDIA)) {
                            $file_info['media_query'] = $file_info['media'];
                        }
                        elseif ($media != $file_info['media']) {
                            // Media changed.
                            $changed = TRUE;
                            $media = $file_info['media'];
                        }
                    }
                    if (empty($file_info['media']) && !empty($media)) {
                        // Media changed to empty.
                        $changed = TRUE;
                        $media = '';
                    }
                }
                if (isset($file_info['browsers'])) {
                    // Browsers changed.
                    $diff = array_merge(array_diff_assoc($file_info['browsers'], $browsers), array_diff_assoc($browsers, $file_info['browsers']));
                    if (!empty($diff)) {
                        $changed = TRUE;
                        $browsers = $file_info['browsers'];
                    }
                }
                if (empty($file_info['browsers']) && !empty($browsers)) {
                    // Browsers changed to empty.
                    $changed = TRUE;
                    $browsers = array();
                }
                if (!empty($strict_files[$file_info['data']]) && $use_strict != $strict_files[$file_info['data']]) {
                    // use_strict value changed to 1.
                    $changed = TRUE;
                    $use_strict = 1;
                }
                if (!empty($use_strict) && empty($strict_files[$file_info['data']])) {
                    // use_strict value changed to 0.
                    $changed = TRUE;
                    $use_strict = 0;
                }
                if (isset($file_info['defer']) && $defer != $file_info['defer']) {
                    // Defer value changed.
                    $changed = TRUE;
                    $defer = $file_info['defer'];
                }
                if (!empty($defer) && empty($file_info['defer'])) {
                    // Defer value changed to empty.
                    $changed = TRUE;
                    $defer = '';
                }
                if (isset($file_info['async']) && $async != $file_info['async']) {
                    // Async value changed.
                    $changed = TRUE;
                    $async = $file_info['async'];
                }
                if (!empty($async) && empty($file_info['async'])) {
                    // Async value changed to empty.
                    $changed = TRUE;
                    $async = '';
                }
                if (isset($file_info['cache']) && $cache != $file_info['cache']) {
                    // Cache value changed.
                    $changed = TRUE;
                    $cache = $file_info['cache'];
                }
                if (!empty($cache) && empty($file_info['cache'])) {
                    // Cache value changed to empty.
                    $changed = TRUE;
                    $cache = '';
                }
                if (isset($file_info['scope']) && $scope != $file_info['scope']) {
                    // Scope value changed.
                    $changed = TRUE;
                    $scope = $file_info['scope'];
                }
                if (!empty($scope) && empty($file_info['scope'])) {
                    // Scope value changed to empty.
                    $changed = TRUE;
                    $scope = '';
                }
                if (variable_get('advagg_ie_css_selector_limiter', ADVAGG_IE_CSS_SELECTOR_LIMITER) && array_key_exists('data', $file_info) && is_string($file_info['data']) && array_key_exists($file_info['data'], $files_info)) {
                    $file_info += $files_info[$file_info['data']];
                    // Prevent CSS rules exceeding 4095 due to limits with IE9 and below.
                    if ($ext === 'css') {
                        $selector_count += $file_info['linecount'];
                        if ($selector_count > $limit_value) {
                            $changed = TRUE;
                            $selector_count = $file_info['linecount'];
                            // Break large file into multiple smaller files.
                            if ($file_info['linecount'] > $limit_value) {
                                $parts = advagg_split_css_file($file_info);
                            }
                        }
                    }
                }
                // Merge in dns_prefetch.
                if ((variable_get('advagg_resource_hints_dns_prefetch', ADVAGG_RESOURCE_HINTS_DNS_PREFETCH) || variable_get('advagg_resource_hints_preconnect', ADVAGG_RESOURCE_HINTS_PRECONNECT)) && isset($files_info[$file_info['data']]['dns_prefetch'])) {
                    if (!isset($file_info['dns_prefetch'])) {
                        $file_info['dns_prefetch'] = array();
                    }
                    if (!empty($file_info['dns_prefetch']) && is_string($file_info['dns_prefetch'])) {
                        $temp = $file_info['dns_prefetch'];
                        unset($file_info['dns_prefetch']);
                        $file_info['dns_prefetch'] = array(
                            $temp,
                        );
                    }
                    $file_info['dns_prefetch'] = array_filter(array_unique(array_merge($file_info['dns_prefetch'], $files_info[$file_info['data']]['dns_prefetch'])));
                }
                // Merge in preload.
                if (variable_get('advagg_resource_hints_preload', ADVAGG_RESOURCE_HINTS_PRELOAD) && isset($files_info[$file_info['data']]['preload'])) {
                    if (!isset($file_info['preload'])) {
                        $file_info['preload'] = array();
                    }
                    if (!empty($file_info['preload']) && is_string($file_info['preload'])) {
                        $temp = $file_info['preload'];
                        unset($file_info['preload']);
                        $file_info['preload'] = array(
                            $temp,
                        );
                    }
                    $file_info['preload'] = array_filter(array_unique(array_merge($file_info['preload'], $files_info[$file_info['data']]['preload'])));
                }
                // If one of the above options changed, it needs to be in a different
                // aggregate.
                if (!empty($parts)) {
                    foreach ($parts as $part) {
                        ++$count;
                        $groups[$location][$count][] = $part;
                    }
                }
                else {
                    if ($changed) {
                        ++$count;
                    }
                    $groups[$location][$count][] = $file_info;
                }
            }
        }
        // Grouping if inline is mixed between files.
        ++$location;
    }
    return $groups;
}

/**
 * Given a file info array it will split the file up.
 *
 * @param array $file_info
 *   File info array from advagg_get_info_on_file().
 * @param string $file_contents
 *   CSS file contents.
 *
 * @return array
 *   Array with advagg_get_info_on_file data and split data.
 */
function advagg_split_css_file(array $file_info, $file_contents = '') {
    // Make advagg_parse_media_blocks() available.
    module_load_include('inc', 'advagg', 'advagg.missing');
    // Get the CSS file and break up by media queries.
    if (empty($file_contents)) {
        $file_contents = (string) @advagg_file_get_contents($file_info['data']);
    }
    $media_blocks = advagg_parse_media_blocks($file_contents);
    // Get the advagg_ie_css_selector_limiter_value.
    $selector_limit = (int) max(variable_get('advagg_ie_css_selector_limiter_value', ADVAGG_IE_CSS_SELECTOR_LIMITER_VALUE), 100);
    // Group media queries together.
    $part_selector_count = 0;
    $counter = 0;
    $values = array();
    foreach ($media_blocks as $media_block) {
        // Get the number of selectors.
        $selector_count = advagg_count_css_selectors($media_block);
        // This chunk is bigger than $selector_limit. It needs to be split.
        if ($selector_count > $selector_limit) {
            $inner_selector_count = 0;
            // Split css string.
            list($media_query, $split_css_strings) = advagg_split_css_string($media_block, $selector_limit);
            foreach ($split_css_strings as $split_css_strings) {
                $counter_changed = FALSE;
                if (empty($split_css_strings)) {
                    continue;
                }
                // Make sure selector count doesn't go over selector limit.
                $inner_selector_count = advagg_count_css_selectors($split_css_strings);
                $part_selector_count += $inner_selector_count;
                if ($part_selector_count > $selector_limit) {
                    if (!empty($values[$counter])) {
                        ++$counter;
                    }
                    $counter_changed = TRUE;
                    $part_selector_count = $inner_selector_count;
                }
                // Add to output array.
                if (isset($values[$counter])) {
                    if (!empty($media_query)) {
                        $values[$counter] .= "\n{$media_query} { {$split_css_strings} } ";
                    }
                    else {
                        $values[$counter] .= "{$split_css_strings}";
                    }
                }
                else {
                    if (!empty($media_query)) {
                        $values[$counter] = "{$media_query} { {$split_css_strings} } ";
                    }
                    else {
                        $values[$counter] = $split_css_strings;
                    }
                }
            }
            // Add to current selector counter and go to the next value.
            if (!$counter_changed) {
                $part_selector_count += $inner_selector_count;
            }
            continue;
        }
        $part_selector_count += $selector_count;
        if ($part_selector_count > $selector_limit) {
            if (!empty($values[$counter])) {
                ++$counter;
            }
            $values[$counter] = $media_block;
            $part_selector_count = $selector_count;
        }
        else {
            if (isset($values[$counter])) {
                $values[$counter] .= "\n{$media_block}";
            }
            else {
                $values[$counter] = $media_block;
            }
        }
    }
    // Save data.
    $parts = array();
    $overall_counter = 0;
    foreach ($values as $key => $value) {
        $last_chunk = FALSE;
        $file_info['split_last_part'] = FALSE;
        if (count($values) - 1 == $key) {
            $last_chunk = TRUE;
        }
        if ($last_chunk) {
            $file_info['split_last_part'] = TRUE;
        }
        // Get the number of selectors.
        $selector_count = advagg_count_css_selectors($value);
        $overall_counter += $selector_count;
        // Save file.
        $subfile = advagg_create_subfile($value, $overall_counter, $file_info);
        if (empty($subfile)) {
            // Something broke; do not create a subfile.
            $variables = array(
                '@info' => var_export($file_info, TRUE),
            );
            watchdog('advagg', 'Spliting up a CSS file failed. File info: <code>@info</code>', $variables);
            return array();
        }
        $parts[] = $subfile;
    }
    return $parts;
}

/**
 * Count the number of selectors inside of a CSS string.
 *
 * @param string $css_string
 *   CSS string.
 *
 * @return int
 *   The number of CSS selectors.
 */
function advagg_count_css_selectors($css_string) {
    return substr_count($css_string, ',') + substr_count($css_string, '{') - substr_count($css_string, '@media');
}

/**
 * Given a css string it will split it if it's over the selector limit.
 *
 * @param string $css_string
 *   CSS string.
 * @param int $selector_limit
 *   How many selectors can be grouped together.
 *
 * @return array
 *   Array that contains the $media_query and the $css_array.
 */
function advagg_split_css_string($css_string, $selector_limit) {
    // See if this css string is wrapped in a @media statement.
    $media_query = '';
    $media_query_pos = strpos($css_string, '@media');
    if ($media_query_pos !== FALSE) {
        // Get the opening bracket.
        $open_bracket_pos = strpos($css_string, "{", $media_query_pos);
        // Skip if there is a syntax error.
        if ($open_bracket_pos === FALSE) {
            return array();
        }
        $media_query = substr($css_string, $media_query_pos, $open_bracket_pos - $media_query_pos);
        $css_string_inside = substr($css_string, $open_bracket_pos + 1);
    }
    else {
        $css_string_inside = $css_string;
    }
    // Split CSS into selector chunks.
    $split = preg_split('/(\\{.+?\\}|,)/si', $css_string_inside, -1, PREG_SPLIT_DELIM_CAPTURE);
    $new_css_chunk = array(
        0 => '',
    );
    $selector_chunk_counter = 0;
    $counter = 0;
    // Have the key value be the running selector count and put split array semi
    // back together.
    foreach ($split as $value) {
        $new_css_chunk[$counter] .= $value;
        if (strpos($value, '}') === FALSE) {
            ++$selector_chunk_counter;
        }
        else {
            if ($counter + 1 < $selector_chunk_counter) {
                $selector_chunk_counter += ($counter - $selector_chunk_counter + 1) / 2;
            }
            $counter = $selector_chunk_counter;
            if (!isset($new_css_chunk[$counter])) {
                $new_css_chunk[$counter] = '';
            }
        }
    }
    // Generate output array in this function.
    $css_array = array();
    $keys = array_keys($new_css_chunk);
    $counter = 0;
    $chunk_counter = 0;
    foreach (array_keys($keys) as $key) {
        // Get out of loop if at the end of the array.
        if (!isset($keys[$key + 1])) {
            break;
        }
        // Get values, keys and counts.
        $this_value = $new_css_chunk[$keys[$key]];
        $this_key = $keys[$key];
        $next_key = $keys[$key + 1];
        $this_selector_count = $next_key - $this_key;
        // Single rule is bigger than the selector limit.
        if ($this_selector_count > $selector_limit) {
            // Get css rules for these selectors.
            $open_bracket_pos = strpos($this_value, "{");
            $css_rule = ' ' . substr($this_value, $open_bracket_pos);
            // Split on selectors.
            $split = preg_split('/(\\,)/si', $this_value, NULL, PREG_SPLIT_OFFSET_CAPTURE);
            $index = 0;
            $counter = 0;
            while (isset($split[$index][1])) {
                // Get starting and ending positions of the selectors given the selector
                // limit.
                $next_index = $index + $selector_limit - 1;
                $start = $split[$index][1];
                if (isset($split[$next_index][1])) {
                    $end = $split[$next_index][1];
                }
                else {
                    // Last one.
                    $temp = end($split);
                    $split_key = key($split);
                    $counter = $split_key % $selector_limit;
                    $end_open_bracket_pos = (int) strpos($temp[0], "{");
                    $end = $temp[1] + $end_open_bracket_pos;
                }
                // Extract substr.
                $sub_this_value = substr($this_value, $start, $end - $start - 1) . $css_rule;
                // Save substr.
                ++$chunk_counter;
                $key_output = $selector_limit;
                if (!empty($counter)) {
                    $key_output = $selector_limit - $counter;
                }
                $css_array["{$chunk_counter} {$key_output}"] = '';
                if (!isset($css_array[$chunk_counter])) {
                    $css_array[$chunk_counter] = $sub_this_value;
                }
                else {
                    $css_array[$chunk_counter] .= $sub_this_value;
                }
                // Move counter.
                $index = $next_index;
            }
            continue;
        }
        $counter += $this_selector_count;
        if ($counter > $selector_limit) {
            $key_output = $counter - $this_selector_count;
            $css_array["{$chunk_counter} {$key_output}"] = '';
            $counter = $next_key - $this_key;
            ++$chunk_counter;
        }
        if (!isset($css_array[$chunk_counter])) {
            $css_array[$chunk_counter] = $this_value;
        }
        else {
            $css_array[$chunk_counter] .= $this_value;
        }
    }
    // Group into sets smaller than $selector_limit.
    return array(
        $media_query,
        $css_array,
    );
}

/**
 * Write CSS parts to disk; used when CSS selectors in one file is > 4096.
 *
 * @param string $css
 *   CSS data to write to disk.
 * @param int $overall_split
 *   Running count of what selector we are from the original file.
 * @param array $file_info
 *   File info array from advagg_get_info_on_file().
 *
 * @return array
 *   Array with advagg_get_info_on_file data and split data; FALSE on failure.
 */
function advagg_create_subfile($css, $overall_split, array $file_info) {
    static $parts_uri;
    static $parts_path;
    if (!isset($parts_uri)) {
        list($css_path) = advagg_get_root_files_dir();
        $parts_uri = $css_path[0] . '/parts';
        $parts_path = $css_path[1] . '/parts';
        // Create the public://advagg_css/parts dir.
        file_prepare_directory($parts_uri, FILE_CREATE_DIRECTORY);
        // Make advagg_save_data() available.
        module_load_include('inc', 'advagg', 'advagg.missing');
    }
    // Get the path from $file_info['data'].
    $uri_path = advagg_get_relative_path($file_info['data']);
    if (!file_exists($uri_path) || is_dir($uri_path)) {
        return FALSE;
    }
    // Write the current chunk of the CSS into a file.
    $new_filename = str_ireplace('.css', '.' . $overall_split . '.css', $uri_path);
    // Fix for things that write dynamically to the public file system.
    $scheme = file_uri_scheme($new_filename);
    if ($scheme) {
        $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
        if ($wrapper && method_exists($wrapper, 'getDirectoryPath')) {
            // Use the wrappers directory path.
            $new_filename = $wrapper->getDirectoryPath() . '/' . file_uri_target($new_filename);
        }
        else {
            // If the scheme does not have a wrapper; prefix file with the scheme.
            $new_filename = $scheme . '/' . file_uri_target($new_filename);
        }
    }
    $part_uri = $parts_uri . '/' . $new_filename;
    $dirname = drupal_dirname($part_uri);
    file_prepare_directory($dirname, FILE_CREATE_DIRECTORY);
    $filename_path = advagg_s3fs_evaluate_no_rewrite_cssjs(FALSE) ? $parts_uri : $parts_path;
    // Get info on the file that was just created.
    $part = advagg_get_info_on_file($filename_path . '/' . $new_filename, TRUE) + $file_info;
    $part['split'] = TRUE;
    $part['split_location'] = $overall_split;
    $part['split_original'] = $file_info['data'];
    // Overwrite/create file if hash doesn't match.
    $hash = drupal_hash_base64($css);
    if ($part['content_hash'] !== $hash) {
        advagg_save_data($part_uri, $css, TRUE);
        $part = advagg_get_info_on_file($filename_path . '/' . $new_filename, TRUE) + $file_info;
        $part['split'] = TRUE;
        $part['split_location'] = $overall_split;
        $part['split_original'] = $file_info['data'];
    }
    return $part;
}

/**
 * Replacement for drupal_build_css_cache() and drupal_build_js_cache().
 *
 * @param array $files_to_aggregate
 *   An array of CSS/JS groups.
 * @param string $type
 *   String; css or js.
 *
 * @return array
 *   array of aggregate files.
 */
function advagg_build_aggregate_plans(array $files_to_aggregate, $type) {
    if ($type !== 'css' && $type !== 'js') {
        return array();
    }
    // Place into biggest grouping possible.
    $groups = advagg_generate_groups($files_to_aggregate, $type);
    // Get filenames.
    $files = advagg_generate_filenames($groups, $type);
    // Insert/Update Database.
    advagg_insert_update_db($files, $type, 1);
    // Update atimes for root.
    advagg_multi_update_atime($files);
    // Run hooks to modify the aggregate.
    // Call hook_advagg_build_aggregate_plans_alter().
    $modified = FALSE;
    drupal_alter('advagg_build_aggregate_plans', $files, $modified, $type);
    // If the hook above modified anything, re-insert into database.
    if ($modified) {
        // Insert/Update Database.
        advagg_insert_update_db($files, $type, 0);
        // Update atimes for non root.
        advagg_multi_update_atime($files);
    }
    // Get file paths.
    list($css_path, $js_path) = advagg_get_root_files_dir();
    // Build the plan.
    $plans = array();
    foreach ($files as $agg_filename => $values) {
        if ($type === 'css') {
            $mixed_media = FALSE;
            $media = NULL;
            foreach ($values['files'] as $value) {
                if (!isset($value['media'])) {
                    continue;
                }
                if (is_null($media)) {
                    $media = $value['media'];
                }
                if ($media != $value['media']) {
                    $mixed_media = TRUE;
                }
            }
        }
        $onload = array();
        $onerror = array();
        $attributes = array();
        $onloadcss = array();
        foreach ($values['files'] as &$items) {
            // Get onload.
            if (!empty($items['onload'])) {
                $onload[] = $items['onload'];
            }
            // Get attributes onload.
            if (!empty($items['attributes']['onload'])) {
                $onload[] = $items['attributes']['onload'];
                unset($items['attributes']['onload']);
            }
            // Get onerror.
            if (!empty($items['onerror'])) {
                $onload[] = $items['onerror'];
            }
            // Get attributes onerror.
            if (!empty($items['attributes']['onerror'])) {
                $onload[] = $items['attributes']['onerror'];
                unset($items['attributes']['onerror']);
            }
            // Get attributes onloadCSS.
            if (!empty($items['onloadCSS'])) {
                $onloadcss[] = $items['onloadCSS'];
            }
            // Get attributes onloadCSS.
            if (!empty($items['attributes']['onloadCSS'])) {
                $onloadcss[] = $items['attributes']['onloadCSS'];
                unset($items['attributes']['onloadCSS']);
            }
            // Get attributes.
            if (!empty($items['attributes'])) {
                $attributes += $items['attributes'];
            }
        }
        $onload = implode(';', array_unique(array_filter($onload)));
        $onerror = implode(';', array_unique(array_filter($onerror)));
        $onloadcss = implode(';', array_unique(array_filter($onloadcss)));
        $first = reset($values['files']);
        if (!empty($mixed_media)) {
            $first['media'] = 'all';
        }
        $url = $type === 'css' ? $css_path[0] : $js_path[0];
        $path = $type === 'css' ? $css_path[1] : $js_path[1];
        $plans[$agg_filename] = array(
            'data' => $url . '/' . $agg_filename,
            'media' => isset($first['media']) ? $first['media'] : '',
            'defer' => isset($first['defer']) ? $first['defer'] : '',
            'async' => isset($first['async']) ? $first['async'] : '',
            'onload' => $onload,
            'onerror' => $onerror,
            'browsers' => isset($first['browsers']) ? $first['browsers'] : array(),
            'cache' => isset($first['cache']) ? $first['cache'] : TRUE,
            'type' => $first['type'],
            'items' => $values,
            'filepath' => $path . '/' . $agg_filename,
            'filename' => $agg_filename,
            'attributes' => $attributes,
        );
        if (!empty($onloadcss)) {
            $plans[$agg_filename]['attributes']['onloadCSS'] = $onloadcss;
        }
    }
    $plans = array_values($plans);
    // Create the aggregate files.
    if (variable_get('advagg_pregenerate_aggregate_files', ADVAGG_PREGENERATE_AGGREGATE_FILES)) {
        advagg_create_aggregate_files($plans, $type);
    }
    // Run hooks to modify the plans.
    // Call hook_advagg_build_aggregate_plans_post_alter().
    drupal_alter('advagg_build_aggregate_plans_post', $plans, $type);
    return $plans;
}

/**
 * Create the aggregate if it does not exist; using HTTPRL if possible.
 *
 * @param array $plans
 *   An array of aggregate file names.
 * @param string $type
 *   String; css or js.
 *
 * @return array
 *   An array of what was done when generating the file.
 */
function advagg_create_aggregate_files(array $plans, $type) {
    $filenames = array();
    $return = array();
    foreach ($plans as $plan) {
        $filenames[] = $plan['filename'];
    }
    // If the httprl module exists and we want to use it.
    if (module_exists('httprl') && variable_get('advagg_use_httprl', ADVAGG_USE_HTTPRL) && (is_callable('httprl_is_background_callback_capable') && httprl_is_background_callback_capable() || !is_callable('httprl_is_background_callback_capable'))) {
        if (variable_get('advagg_fast_filesystem', ADVAGG_FAST_FILESYSTEM)) {
            list($css_path, $js_path) = advagg_get_root_files_dir();
            foreach ($filenames as $key => $filename) {
                if ($type === 'css') {
                    $uri = $css_path[0] . '/' . $filename;
                }
                elseif ($type === 'js') {
                    $uri = $js_path[0] . '/' . $filename;
                }
                if (file_exists($uri)) {
                    unset($filenames[$key]);
                }
            }
        }
        if (!empty($filenames)) {
            // Setup callback options array; call function in the background.
            $callback_options = array(
                array(
                    'function' => 'advagg_build_aggregates',
                ),
                $filenames,
                $type,
            );
            // Queue up the request.
            httprl_queue_background_callback($callback_options);
            // Execute request.
            $return = httprl_send_request();
        }
    }
    else {
        $return = advagg_build_aggregates($filenames, $type);
    }
    return $return;
}

/**
 * Loads the stylesheet and resolves all @import commands.
 *
 * Loads a stylesheet and replaces @import commands with the contents of the
 * imported file. Use this instead of file_get_contents when processing
 * stylesheets.
 *
 * The returned contents are compressed removing white space and comments only
 * when CSS aggregation is enabled. This optimization will not apply for
 * color.module enabled themes with CSS aggregation turned off.
 *
 * @param string $file
 *   Name of the stylesheet to be processed.
 * @param bool $optimize
 *   Defines if CSS contents should be compressed or not.
 * @param array $aggregate_settings
 *   Array of settings.
 *
 * @return string
 *   Contents of the stylesheet, including any resolved @import commands.
 */
function advagg_load_css_stylesheet($file, $optimize = TRUE, array $aggregate_settings = array(), $contents = '') {
    $old_base_path = $GLOBALS['base_path'];
    // Change context to that of when this aggregate was created.
    advagg_context_switch($aggregate_settings, 0);
    // Get the stylesheets contents.
    $contents = advagg_load_stylesheet($file, $optimize, TRUE, $contents);
    // Resolve public:// if needed.
    if (!advagg_is_external($file) && file_uri_scheme($file)) {
        $file = advagg_get_relative_path($file);
    }
    // Get the parent directory of this file, relative to the Drupal root.
    $css_base_url = substr($file, 0, strrpos($file, '/'));
    // Handle split css files.
    list($css_path) = advagg_get_root_files_dir();
    $parts_path = (advagg_s3fs_evaluate_no_rewrite_cssjs(FALSE) ? $css_path[0] : $css_path[1]) . '/parts/';
    $url_parts = strpos($css_base_url, $parts_path);
    // If this CSS file is actually a part of a previously split larger CSS file,
    // don't use it to construct relative paths within the CSS file for
    // 'url(...)' bits.
    if ($url_parts !== FALSE) {
        $css_base_url = substr($css_base_url, $url_parts + strlen($parts_path));
    }
    // Replace the old base path with the one that was passed in.
    if (advagg_is_external($css_base_url) || variable_get('advagg_skip_file_create_url_inside_css', ADVAGG_SKIP_FILE_CREATE_URL_INSIDE_CSS)) {
        $pos = strpos($css_base_url, $old_base_path);
        if ($pos !== FALSE) {
            $parsed_url = parse_url($css_base_url);
            if (!empty($parsed_url['path'])) {
                // Remove any double slash in path.
                $parsed_url['path'] = str_replace('//', '/', $parsed_url['path']);
                // Get newly recalculated position.
                $pos = strpos($parsed_url['path'], $old_base_path);
                // Replace.
                if (strpos($parsed_url['path'], '/') !== 0 && $old_base_path === '/') {
                    // Special case if going to a subdir.
                    $parsed_url['path'] = $GLOBALS['base_path'] . $parsed_url['path'];
                }
                else {
                    $parsed_url['path'] = substr_replace($parsed_url['path'], $GLOBALS['base_path'], $pos, strlen($old_base_path));
                }
                $css_base_url = advagg_glue_url($parsed_url);
            }
        }
    }
    _advagg_build_css_path(array(), $css_base_url . '/', $aggregate_settings);
    // Anchor all paths in the CSS with its base URL, ignoring external,
    // absolute paths, and urls that start with # or %23 (SVG).
    $contents = preg_replace_callback('%url\\(\\s*+[\'"]?+(?![a-z]++:|/|\\#|\\%23+)([^\'"\\)]++)[\'"]?+\\s*+\\)%i', '_advagg_build_css_path', $contents);
    // Change context back.
    advagg_context_switch($aggregate_settings, 1);
    // Return the stylesheets contents.
    return $contents;
}

/**
 * Changes context when generating CSS or JS files.
 *
 * @param array $aggregate_settings
 *   Array of settings.
 * @param int $mode
 *   Use 0 to change context to what is inside of $aggregate_settings.
 *   Use 1 to change context back.
 */
function advagg_context_switch(array $aggregate_settings, $mode) {
    $original =& drupal_static(__FUNCTION__);
    // Use current $aggregate_settings if none was passed in.
    if (empty($aggregate_settings)) {
        $aggregate_settings = advagg_current_hooks_hash_array();
    }
    // Call hook_advagg_context_alter().
    drupal_alter('advagg_context', $original, $aggregate_settings, $mode);
}

/**
 * Prefixes all paths within a CSS file for drupal_build_css_cache().
 *
 * @param array $matches
 *   Array of matched items from preg_replace_callback().
 * @param string $base
 *   Base path.
 * @param array $aggregate_settings
 *   Array of settings.
 *
 * @return string
 *   New version of the url() string from the css.
 *
 * @see _drupal_build_css_path()
 * @see https://drupal.org/node/1961340#comment-7735815
 * @see https://drupal.org/node/1514182#comment-7875489
 */
function _advagg_build_css_path(array $matches, $base = '', array $aggregate_settings = array()) {
    $_base =& drupal_static(__FUNCTION__, '');
    $_aggregate_settings =& drupal_static(__FUNCTION__ . '_aggregate_settings', array());
    // Store base path for preg_replace_callback.
    if (!empty($base)) {
        $_base = $base;
    }
    if (!empty($aggregate_settings)) {
        $_aggregate_settings = $aggregate_settings;
    }
    // Short circuit if no matches were passed in.
    if (empty($matches)) {
        return '';
    }
    // Prefix with base.
    $url = $_base . $matches[1];
    // If advagg_file_create_url() is not being used and the $url is local, redo
    // the $url taking the base_path into account.
    if (!advagg_is_external($url) && variable_get('advagg_skip_file_create_url_inside_css', ADVAGG_SKIP_FILE_CREATE_URL_INSIDE_CSS)) {
        $new_base_path = $GLOBALS['base_path'];
        if (isset($_aggregate_settings['variables']['base_path'])) {
            $new_base_path = $_aggregate_settings['variables']['base_path'];
        }
        // Remove first /.
        $new_base_path = ltrim($new_base_path, '/');
        $pos = FALSE;
        // See if base_path is in the passed in $_base.
        if (!empty($new_base_path)) {
            $pos = strpos($_base, $new_base_path);
        }
        if ($pos !== FALSE) {
            $url = substr($_base, $pos) . $matches[1];
        }
        else {
            $url = $new_base_path . $_base . $matches[1];
        }
    }
    // Remove '../' segments where possible.
    $last = '';
    while ($url != $last) {
        $last = $url;
        $url = preg_replace('`(^|/)(?!\\.\\./)([^/]+)/\\.\\./`', '$1', $url);
    }
    // Parse and build back the url without the query and fragment parts.
    $parsed_url = parse_url($url);
    $base_url = advagg_glue_url($parsed_url, TRUE);
    $query = isset($parsed_url['query']) ? $parsed_url['query'] : '';
    // In the case of certain URLs, we may have simply a '?' character without
    // further parameters. parse_url() misses this and leaves 'query' blank, so
    // need to this back in.
    // See http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax
    // for more information.
    if ($query != '' || strpos($url, $base_url . '?') === 0) {
        $query = '?' . $query;
    }
    $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
    $run_file_create_url = FALSE;
    if (!variable_get('advagg_skip_file_create_url_inside_css', ADVAGG_SKIP_FILE_CREATE_URL_INSIDE_CSS)) {
        $run_file_create_url = TRUE;
    }
    if (empty($parsed_url['host'])) {
        $base_url = ltrim($base_url, '/');
    }
    $base_url = advagg_file_create_url($base_url, $_aggregate_settings, $run_file_create_url, 'css');
    return 'url(' . $base_url . $query . $fragment . ')';
}

Functions

Titre Deprecated Résumé
advagg_build_aggregate_plans Replacement for drupal_build_css_cache() and drupal_build_js_cache().
advagg_build_filename Build the filename.
advagg_clearstatcache Wrapper around clearstatcache so it can use php 5.3's new features.
advagg_context_switch Changes context when generating CSS or JS files.
advagg_count_css_selectors Count the number of selectors inside of a CSS string.
advagg_create_aggregate_files Create the aggregate if it does not exist; using HTTPRL if possible.
advagg_create_subfile Write CSS parts to disk; used when CSS selectors in one file is > 4096.
advagg_does_js_start_with_use_strict Given a js string, see if "use strict"; is the first thing ran.
advagg_drupal_hash_base64 Given a filename calculate the hash for it. Uses static cache.
advagg_generate_filenames Given a group of files calculate what the aggregate filename will be.
advagg_generate_filesize_processed Given a filename calculate the processed filesize.
advagg_generate_groups Group the CSS/JS into the biggest buckets possible.
advagg_get_aggregate_info_from_files Given a group of files calculate various hashes and gather meta data.
advagg_get_info_on_file Given a filename calculate various hashes and gather meta data.
advagg_get_info_on_files Given a filename calculate various hashes and gather meta data.
advagg_get_js_header Read only the first 8192 bytes to get the file header.
advagg_insert_aggregate Insert/Update data in the advagg_aggregates table.
advagg_insert_aggregate_version Insert data in the advagg_aggregates_versions table.
advagg_insert_update_db Insert/Update data in advagg tables.
advagg_insert_update_files Insert/Update data in the advagg_files table.
advagg_load_css_stylesheet Loads the stylesheet and resolves all @import commands.
advagg_load_files_info_into_static_cache Load cache bin file info in static cache.
advagg_remove_js_comments Remove comments from JavaScript.
advagg_split_css_file Given a file info array it will split the file up.
advagg_split_css_string Given a css string it will split it if it's over the selector limit.
_advagg_build_css_path Prefixes all paths within a CSS file for drupal_build_css_cache().