Now all modules are in core modules folder
This commit is contained in:
parent
5ba1cdfa0b
commit
05b6a91b0c
1907 changed files with 0 additions and 0 deletions
476
modules/advagg/advagg_bundler/advagg_bundler.module
Normal file
476
modules/advagg/advagg_bundler/advagg_bundler.module
Normal file
|
@ -0,0 +1,476 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Advanced aggregation bundler module.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default value of the maximum number of CSS bundles that can be generated in
|
||||
* a single request.
|
||||
*/
|
||||
define('ADVAGG_BUNDLER_MAX_CSS', 4);
|
||||
|
||||
/**
|
||||
* Default value of the maximum number of JS bundles that can be generated in
|
||||
* a single request.
|
||||
*/
|
||||
define('ADVAGG_BUNDLER_MAX_JS', 4);
|
||||
|
||||
/**
|
||||
* Default value of the last used time before the bundle is considered outdated.
|
||||
* 2 weeks in seconds.
|
||||
*/
|
||||
define('ADVAGG_BUNDLER_OUTDATED', 1209600);
|
||||
|
||||
/**
|
||||
* Default value to see if the bundler should be active or passive. If it is
|
||||
* passive, the bundler will only do analysis and not split up the aggregate.
|
||||
*/
|
||||
define('ADVAGG_BUNDLER_ACTIVE', TRUE);
|
||||
|
||||
/**
|
||||
* CSS selector limit in a single stylesheet on IE9 and below.
|
||||
*/
|
||||
define('SELECTOR_SPLIT_VALUE', 4095);
|
||||
|
||||
/**
|
||||
* Implementation of hook_menu
|
||||
*/
|
||||
function advagg_bundler_menu() {
|
||||
$items = array();
|
||||
$file_path = drupal_get_path('module', 'advagg_bundler');
|
||||
|
||||
$items['admin/settings/advagg/bundler'] = array(
|
||||
'title' => 'Bundler',
|
||||
'description' => 'Adjust Bundler settings.',
|
||||
'page callback' => 'advagg_bundler_admin_page',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'access arguments' => array('administer site configuration'),
|
||||
'file path' => $file_path,
|
||||
'file' => 'advagg_bundler.admin.inc',
|
||||
'weight' => 10,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_advagg_filenames_alter.
|
||||
*/
|
||||
function advagg_bundler_advagg_filenames_alter(&$filenames) {
|
||||
// Get max number of sub aggregates to create.
|
||||
$max_css = variable_get('advagg_bundler_max_css', ADVAGG_BUNDLER_MAX_CSS);
|
||||
$max_js = variable_get('advagg_bundler_max_js', ADVAGG_BUNDLER_MAX_JS);
|
||||
|
||||
$output = array();
|
||||
foreach ($filenames as $values) {
|
||||
// Extract values and set them.
|
||||
$filetype = $values['filetype'];
|
||||
$files = $values['files'];
|
||||
$bundle_md5 = $values['bundle_md5'];
|
||||
$cached_data_key = 'bundler_' . $bundle_md5;
|
||||
|
||||
// Try cache first; cache table is cache_advagg_bundle_reuse.
|
||||
$cached_data = advagg_cached_bundle_get($cached_data_key, 'advagg_bundler_filenames_alter');
|
||||
if (!empty($cached_data)) {
|
||||
$output = array_merge($output, $cached_data);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If cache miss then start to do processing.
|
||||
// Set the max value based off of the filetype.
|
||||
if ($filetype == 'css') {
|
||||
$max = $max_css;
|
||||
}
|
||||
if ($filetype == 'js') {
|
||||
$max = $max_js;
|
||||
}
|
||||
|
||||
// If we are only going to have one bundle then do not do any more
|
||||
// processing.
|
||||
if (empty($max) || $max == 1) {
|
||||
$output[] = $values;
|
||||
$data = array($values);
|
||||
cache_set($cached_data_key, $data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load up each file.
|
||||
$groupings = array();
|
||||
// Preserve the order while grouping.
|
||||
$last_group = '';
|
||||
foreach ($files as $key => $filename) {
|
||||
// Assign each file to their group.
|
||||
$group = advagg_bundler_analysis($filename);
|
||||
|
||||
// Set $last_group if this is the first run of this foreach loop.
|
||||
if (empty($last_group)) {
|
||||
$last_group = $group;
|
||||
}
|
||||
|
||||
if ($last_group == $group) {
|
||||
// Include this new file into the last used group.
|
||||
$groupings[$group][] = $filename;
|
||||
}
|
||||
else {
|
||||
// In order to preserve CSS/JS execution order we need to move the last
|
||||
// group to a unique name. Use the array key to make this group unique.
|
||||
$groupings[$key . ' ' . $last_group] = $groupings[$last_group];
|
||||
unset($groupings[$last_group]);
|
||||
// Place the new file into the new group and set $last_group.
|
||||
$groupings[$group][] = $filename;
|
||||
$last_group = $group;
|
||||
}
|
||||
}
|
||||
|
||||
// If only one group then don't do any more processing.
|
||||
if (count($groupings) == 1) {
|
||||
$output[] = $values;
|
||||
$data = array($values);
|
||||
cache_set($cached_data_key, $data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure we didn't go over the max; if we did merge the smallest bundles
|
||||
// together.
|
||||
advagg_bundler_merge($groupings, $max, $filetype);
|
||||
|
||||
// If only one group then don't do any more processing. The merge algorithm
|
||||
// could have reduce the groupings down to one.
|
||||
if (count($groupings) == 1) {
|
||||
$output[] = $values;
|
||||
$data = array($values);
|
||||
cache_set($cached_data_key, $data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set groups.
|
||||
$data = array();
|
||||
foreach ($groupings as $bundle) {
|
||||
$values['files'] = $bundle;
|
||||
$values['bundle_md5'] = md5(implode('', $bundle));
|
||||
$data[] = $values;
|
||||
$output[] = $values;
|
||||
}
|
||||
cache_set($cached_data_key, $data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
|
||||
}
|
||||
|
||||
// Return groupings.
|
||||
if (variable_get('advagg_bundler_active', ADVAGG_BUNDLER_ACTIVE)) {
|
||||
$filenames = $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a filename return a bundle key.
|
||||
*
|
||||
* @param $filename
|
||||
* filename
|
||||
* @param $force
|
||||
* bypass the cache and get a fresh version of the analysis.
|
||||
* @return
|
||||
* string to be used for the grouping key.
|
||||
*/
|
||||
function advagg_bundler_analysis($filename = '', $force = FALSE) {
|
||||
// Cache query in a static.
|
||||
static $analysis = array();
|
||||
if (empty($analysis)) {
|
||||
// See if we have a cached version of this.
|
||||
$count = db_result(db_query("SELECT COUNT(*) FROM {advagg_bundles} WHERE root=1"));
|
||||
$cache = cache_get('advagg_bundler_analysis:' . $count);
|
||||
if (empty($cache->data) || $force) {
|
||||
// "Magic Query"; only needs to run once.
|
||||
// Return a count of how many root bundles all files are used in. Count is
|
||||
// padded with eight zeros so the count can be key sorted as a string
|
||||
// without worrying about it getting put in the wrong order.
|
||||
// Return the bundle_md5's value; we need something more unique than count
|
||||
// when grouping together.
|
||||
// Return the filename. Used for lookup.
|
||||
// We join the advagg bundles and files together making sure to only use
|
||||
// root bundles that have been used in the last 2 weeks. This prevents an
|
||||
// old site structure from influencing new bundles.
|
||||
// Grouping by the filename gives us the count and makes it so we don't
|
||||
// return a lot of rows;
|
||||
$result = db_query("
|
||||
SELECT
|
||||
count,
|
||||
bundle_md5,
|
||||
filename
|
||||
FROM (
|
||||
SELECT
|
||||
LPAD(COUNT(*), 8, '00000000') AS count,
|
||||
bundle_md5,
|
||||
filename_md5
|
||||
FROM
|
||||
{advagg_bundles}
|
||||
WHERE
|
||||
root = 1
|
||||
AND
|
||||
timestamp > %d
|
||||
GROUP BY
|
||||
filename_md5) AS ab
|
||||
INNER JOIN {advagg_files} AS af USING ( filename_md5 )
|
||||
", time() - variable_get('advagg_bundler_outdated', ADVAGG_BUNDLER_OUTDATED));
|
||||
|
||||
// Save query into a static array with the filename as the key.
|
||||
while ($row = db_fetch_array($result)) {
|
||||
$analysis[$row['filename']] = $row['count'] . ' ' . $row['bundle_md5'];
|
||||
}
|
||||
|
||||
// Invoke hook_advagg_bundler_analysis_alter() to give installed modules a
|
||||
// chance to alter the analysis array.
|
||||
drupal_alter('advagg_bundler_analysis', $analysis);
|
||||
|
||||
// Save results to the cache.
|
||||
cache_set('advagg_bundler_analysis:' . $count, $analysis, 'cache', CACHE_TEMPORARY);
|
||||
}
|
||||
else {
|
||||
$analysis = $cache->data;
|
||||
}
|
||||
}
|
||||
|
||||
// If no filename is given pass back then entire query results.
|
||||
if (empty($filename)) {
|
||||
return $analysis;
|
||||
}
|
||||
|
||||
// Return a key to be used in groupings.
|
||||
if (!empty($analysis[$filename])) {
|
||||
return $analysis[$filename];
|
||||
}
|
||||
|
||||
// We need to return a value that can be used as an array key if the query
|
||||
// didn't give us anything.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge bundles together if too many where created.
|
||||
*
|
||||
* This preserves the order.
|
||||
*
|
||||
* @param $groupings
|
||||
* array of requested groups
|
||||
* @param $max
|
||||
* max number of grouping
|
||||
*/
|
||||
function advagg_bundler_merge(&$groupings, $max, $filetype) {
|
||||
$group_count = count($groupings);
|
||||
|
||||
if (!empty($max)) {
|
||||
// Keep going till array has been merged to the desired size.
|
||||
while ($group_count > $max) {
|
||||
// Get array sizes.
|
||||
// Counts the number of files that are placed into each bundle.
|
||||
$counts = array();
|
||||
foreach ($groupings as $key => $values) {
|
||||
$counts[$key] = count($values);
|
||||
}
|
||||
|
||||
// Create mapping.
|
||||
// Calculates the file count of potential merges. It only merges with
|
||||
// neighbors in order to preserve execution order.
|
||||
$map = array();
|
||||
$prev_key = '';
|
||||
foreach ($counts as $key => $val) {
|
||||
// First run of the foreach loop; populate prev key/values and continue.
|
||||
// We can't merge with the previous group in this case.
|
||||
if (empty($prev_key)) {
|
||||
$prev_key = $key;
|
||||
$prev_val = $val;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Array key ($prev_val + $val) is the file count of this new group if
|
||||
// these 2 groups ($prev_key, $key) where to be merged together.
|
||||
$map[] = array(
|
||||
($prev_val + $val) => array($prev_key, $key),
|
||||
);
|
||||
$prev_key = $key;
|
||||
$prev_val = $val;
|
||||
}
|
||||
|
||||
// Get best merge candidate.
|
||||
// We are looking for the smallest file count. $min is populated with a
|
||||
// large number (15 bits) so it won't be selected in this process.
|
||||
$min = 32767;
|
||||
foreach ($map as $v) {
|
||||
foreach ($v as $key => $values) {
|
||||
$min = min($min, $key);
|
||||
// If the min value (number of files in the proposed merged bundle) is
|
||||
// the same as the key, then get the 2 bundle keys that generated this
|
||||
// new min value.
|
||||
if ($min == $key) {
|
||||
list($first, $last) = $values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the new merged set.
|
||||
$a = $groupings[$first];
|
||||
$b = $groupings[$last];
|
||||
$new_set = array_merge($a, $b);
|
||||
|
||||
// Rebuild the array with the new set in the correct place.
|
||||
$new_groupings = array();
|
||||
foreach ($groupings as $key => $files) {
|
||||
if ($key == $first) {
|
||||
$new_groupings[$first . ' ' . $last] = $new_set;
|
||||
}
|
||||
elseif ($key != $last) {
|
||||
$new_groupings[$key] = $files;
|
||||
}
|
||||
}
|
||||
$groupings = $new_groupings;
|
||||
$group_count--;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure there isn't a group that has all files that don't exist or empty.
|
||||
$bad_groups = array();
|
||||
$counts = array();
|
||||
foreach ($groupings as $key => $group) {
|
||||
$missing_counter = 0;
|
||||
$counts[$key] = count($group);
|
||||
foreach ($group as $i => $file) {
|
||||
advagg_clearstatcache(TRUE, $file);
|
||||
if (!file_exists($file) || filesize($file) == 0) {
|
||||
$missing_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
// If all files are missing/empty in this group then it is a bad set.
|
||||
if ($missing_counter == count($group)) {
|
||||
$bad_groups[$key] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the bad groups to the smallest grouping in this set.
|
||||
if (!empty($bad_groups)) {
|
||||
$merge_candidate_key = '';
|
||||
$merge_candidate_count = 32767;
|
||||
$bad_group = array();
|
||||
foreach ($groupings as $key => $group) {
|
||||
if (isset($bad_groups[$key])) {
|
||||
// Merge all bad groups into one.
|
||||
$bad_group = array_merge($bad_group, $group);
|
||||
|
||||
// Delete the bad group from the master set.
|
||||
unset($groupings[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the smallest good grouping.
|
||||
$min = min($merge_candidate_count, count($group));
|
||||
if ($min < $merge_candidate_count) {
|
||||
$merge_candidate_key = $key;
|
||||
$merge_candidate_count = $min;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the bad files into the smallest good group.
|
||||
$new_set = $groupings[$merge_candidate_key];
|
||||
$new_set = array_merge($new_set, $bad_group);
|
||||
$groupings[$merge_candidate_key] = $new_set;
|
||||
}
|
||||
|
||||
// Prevent CSS selectors exceeding 4095 due to limits with IE9 and below.
|
||||
if ($filetype == 'css') {
|
||||
|
||||
// Check each group to see if it exceeds the selector limit.
|
||||
do {
|
||||
$groupings_edited = FALSE;
|
||||
foreach ($groupings as $key => $group) {
|
||||
|
||||
// Restart the selector limit check if the grouping was edited.
|
||||
if ($groupings_edited) {
|
||||
break;
|
||||
}
|
||||
|
||||
$group_selector_counter = 0;
|
||||
|
||||
$selector_counts = advagg_bundler_get_css_selector_count($group);
|
||||
|
||||
for ($i = 0; $i < count($group) && !$groupings_edited; $i++) {
|
||||
|
||||
$selector_count = isset($selector_counts[$group[$i]]) ? $selector_counts[$group[$i]] : 0;
|
||||
|
||||
if ($group_selector_counter + $selector_count > SELECTOR_SPLIT_VALUE) {
|
||||
$groupings_edited = TRUE;
|
||||
|
||||
// Divide the group.
|
||||
$first_group = array_splice($group, 0, $i);
|
||||
$second_group = array_splice($group, 0);
|
||||
|
||||
// Rebuild the array with the new set in the correct place.
|
||||
$new_groupings = array();
|
||||
foreach ($groupings as $k => $files) {
|
||||
if ($k == $key) {
|
||||
$new_groupings[$k . '_1'] = $first_group;
|
||||
$new_groupings[$k . '_2'] = $second_group;
|
||||
}
|
||||
else {
|
||||
$new_groupings[$k] = $files;
|
||||
}
|
||||
}
|
||||
$groupings = $new_groupings;
|
||||
}
|
||||
else {
|
||||
$group_selector_counter += $selector_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($groupings_edited);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selector count of the provided files.
|
||||
*
|
||||
* @param array $files
|
||||
* Array of files to use.
|
||||
*
|
||||
* @return array
|
||||
* The selector counts of each file.
|
||||
*/
|
||||
function advagg_bundler_get_css_selector_count($files) {
|
||||
$results = array();
|
||||
$placeholders = db_placeholders($files);
|
||||
$result = db_query("SELECT filename, selector_count, timestamp FROM {advagg_bundler_selector_count} WHERE filename IN ($placeholders)", $files);
|
||||
|
||||
while ($row = db_fetch_array($result)) {
|
||||
$modified = 0;
|
||||
if (is_readable($row['filename'])) {
|
||||
$modified = filemtime($row['filename']);
|
||||
}
|
||||
if ($modified > $row['timestamp']) {
|
||||
$css = advagg_build_css_bundle(array($row['filename']), TRUE);
|
||||
|
||||
// Get the number of selectors.
|
||||
// http://stackoverflow.com/questions/12567000/regex-matching-for-counting-css-selectors/12567381#12567381
|
||||
$selector_count = preg_match_all('/\{.+?\}|,/s', $css, $matched);
|
||||
|
||||
db_query("UPDATE {advagg_bundler_selector_count} SET timestamp = %d, selector_count = %d WHERE filename LIKE '%s'", $modified, $selector_count, $row['filename']);
|
||||
|
||||
$results[$row['filename']] = $selector_count;
|
||||
}
|
||||
else {
|
||||
$results[$row['filename']] = $row['selector_count'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (!isset($results[$file]) && file_exists($file)) {
|
||||
$css = advagg_build_css_bundle(array($file), TRUE);
|
||||
$selector_count = preg_match_all('/\{.+?\}|,/s', $css, $matched);
|
||||
|
||||
db_query("INSERT INTO {advagg_bundler_selector_count} VALUES('%s', '%s', %d, %d)", $file, md5($file), $selector_count, filemtime($file));
|
||||
|
||||
$results[$file] = $selector_count;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
Reference in a new issue