Same filename in other branches
Advanced aggregation bundler module.
Fichier
-
advagg_bundler/
advagg_bundler.module
View source
<?php
/**
* @file
* Advanced aggregation bundler module.
*/
/**
* @addtogroup default_variables
* @{
*/
/**
* Default for maximum number of CSS bundles used in a themes region.
*/
define('ADVAGG_BUNDLER_MAX_CSS', 2);
/**
* Default for maximum number of JS bundles used in a themes region.
*/
define('ADVAGG_BUNDLER_MAX_JS', 5);
/**
* Default of the last used time before the bundle is considered outdated.
*
* Value of 1209600 is 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);
/**
* Default value to for bundler, set to file size.
*/
define('ADVAGG_BUNDLER_GROUPING_LOGIC', 1);
/**
* If 4 the admin section gets unlocked.
*/
define('ADVAGG_BUNDLER_ADMIN_MODE', 4);
/**
* @} End of "addtogroup default_variables".
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Implements hook_menu().
*/
function advagg_bundler_menu() {
$file_path = drupal_get_path('module', 'advagg_bundler');
$config_path = advagg_admin_config_root_path();
$items[$config_path . '/advagg/bundler'] = array(
'title' => 'Bundler',
'description' => 'Adjust Bundler settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'advagg_bundler_admin_settings_form',
),
'type' => MENU_LOCAL_TASK,
'access arguments' => array(
'administer site configuration',
),
'file path' => $file_path,
'file' => 'advagg_bundler.admin.inc',
'weight' => 10,
);
return $items;
}
/**
* Implements hook_advagg_hooks_implemented_alter().
*/
function advagg_bundler_advagg_hooks_implemented_alter(&$hooks, $all) {
if ($all) {
$hooks['advagg_bundler_analysis_alter'] = array();
}
}
/**
* Implements hook_init().
*/
function advagg_bundler_init() {
if (advagg_bundler_enabled()) {
$GLOBALS['conf']['advagg_core_groups'] = FALSE;
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function advagg_bundler_form_advagg_admin_settings_form_alter(&$form, $form_state) {
if (advagg_bundler_enabled()) {
$form['global']['advagg_core_groups']['#disabled'] = TRUE;
$form['global']['advagg_core_groups']['#description'] = t('The bundler submodule disables core grouping logic.');
$form['global']['advagg_core_groups']['#states'] = array();
}
}
/**
* @} End of "addtogroup hooks".
*/
/**
* Returns TRUE if the bundler will run.
*/
function advagg_bundler_enabled() {
if (variable_get('advagg_bundler_active', ADVAGG_BUNDLER_ACTIVE) && (variable_get('advagg_bundler_max_css', ADVAGG_BUNDLER_MAX_CSS) || variable_get('advagg_bundler_max_js', ADVAGG_BUNDLER_MAX_JS))) {
return TRUE;
}
}
/**
* Given a filename return a bundle key.
*
* @param string $filename
* Filename.
* @param bool $force
* Bypass the cache and get a fresh version of the analysis.
* @param bool $safesql
* Turn off SQL language that might cause errors.
* @param int $depth
* Used to prevent endless loops.
*
* @return string
* String to be used for the grouping key.
*/
function advagg_bundler_analysis($filename = '', $force = FALSE, $safesql = FALSE, $depth = 0) {
// Cache query in a static.
static $analysis = array();
if (empty($analysis)) {
// See if we have a cached version of this. Generate cache ID.
$query = db_select('advagg_aggregates_versions', 'aav')->condition('aav.root', 1)
->condition('aav.atime', REQUEST_TIME - max(172800, variable_get('advagg_bundler_outdated', ADVAGG_BUNDLER_OUTDATED), '>'), '>');
$query->addExpression('COUNT(aggregate_filenames_hash)', 'counter');
$count = $query->execute()
->fetchField();
$ideal_cid = 'advagg:bundler_analysis:' . $count;
if (!$force) {
// Generate cache IDs.
$counts = range(max(0, $count - 3), $count + 3);
foreach ($counts as $count) {
$cache_ids[] = 'advagg:bundler_analysis:' . $count;
}
// Get a range of cached bundler_analysis data.
$cache_hits = cache_get_multiple($cache_ids, 'cache_advagg_aggregates');
if (!empty($cache_hits)) {
if (isset($cache_hits[$ideal_cid])) {
$cache = $cache_hits[$ideal_cid];
}
elseif (!$force && module_exists('httprl') && httprl_is_background_callback_capable()) {
// Setup callback options array.
$callback_options = array(
array(
'function' => 'advagg_bundler_analysis',
),
$filename,
TRUE,
);
// Queue up the request.
httprl_queue_background_callback($callback_options);
// Execute request.
httprl_send_request();
// Use most recent bundler_analysis data.
$max = 0;
foreach ($cache_hits as $data) {
if ($data->created > $max) {
$max = $data->created;
$cache = $data;
}
}
}
}
}
if ($force || empty($cache->data)) {
try {
$analysis = advagg_bundler_analyisis_query($safesql);
// Save results to the cache.
cache_set($ideal_cid, $analysis, 'cache_advagg_aggregates', CACHE_TEMPORARY);
} catch (PDOException $e) {
if ($depth > 2) {
throw $e;
}
$depth++;
return advagg_bundler_analysis($filename, TRUE, TRUE, $depth);
}
}
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;
}
/**
* Run the analysis query and return the analysis array.
*
* "Magic Query"; only needs to run once. Results are cached.
* This is what the raw SQL looks like:
*
* @code
* SELECT
* af.filename AS filename,
* af.filesize AS filesize,
* af.mtime AS mtime,
* af.changes AS changes,
* af.linecount AS linecount,
* af.filename_hash AS filename_hash,
* aa.counter AS counter,
* aa.hashlist AS hashlist
* FROM advagg_files af
* INNER JOIN (
* SELECT
* aa.filename_hash AS filename_hash,
* LPAD(CAST(COUNT(aav.aggregate_filenames_hash) AS char(8)), 8, '0') AS counter,
* HEX(SHA1(GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash ORDER BY aa.aggregate_filenames_hash ASC))) AS hashlist
* FROM advagg_aggregates aa
* INNER JOIN advagg_aggregates_versions aav
* ON aav.aggregate_filenames_hash = aa.aggregate_filenames_hash
* AND aav.root = 1
* AND aav.atime > (UNIX_TIMESTAMP() - 1209600)
* GROUP BY aa.filename_hash
* ) aa ON af.filename_hash = aa.filename_hash
* @endcode
*
* @param bool $safesql
* Turn off SQL language that might cause errors.
*
* @return array
* The analysis array.
*/
function advagg_bundler_analyisis_query($safesql) {
// 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.
$db_type = Database::getConnection()->databaseType();
$schema = Database::getConnection()->schema();
if ($safesql) {
$mssql_group_concat = FALSE;
$mssql_lpad = FALSE;
$mssql_md5 = FALSE;
$pg9 = FALSE;
}
else {
$mssql_group_concat = method_exists($schema, 'functionExists') && $schema->functionExists('GROUP_CONCAT');
$mssql_lpad = method_exists($schema, 'functionExists') && $schema->functionExists('LPAD');
$mssql_md5 = method_exists($schema, 'functionExists') && $schema->functionExists('MD5');
if ($db_type === 'pgsql') {
$database_connection = Database::getConnection();
$pg9 = FALSE;
if (version_compare($database_connection->version(), '9') >= 0) {
$pg9 = TRUE;
}
}
}
// Create join query for the advagg_aggregates table.
$subquery_aggregates = db_select('advagg_aggregates', 'aa');
// Counter column.
$fields = array(
'counter',
);
if ($db_type === 'sqlsrv' && !$mssql_lpad) {
// MS SQL does not support LPAD.
$subquery_aggregates->addExpression("RIGHT(REPLICATE('0',8) + CAST(COUNT(aav.aggregate_filenames_hash) AS char(8)),8)", 'counter');
}
elseif ($db_type === 'sqlite') {
// SQLite does not support LPAD.
$subquery_aggregates->addExpression("substr('00000000' || CAST(COUNT(aav.aggregate_filenames_hash) AS char(8)), -8, 8)", 'counter');
}
else {
$subquery_aggregates->addExpression("LPAD(CAST(COUNT(aav.aggregate_filenames_hash) AS char(8)), 8, '0')", 'counter');
}
// Hashlist column.
if ($db_type === 'mysql') {
$fields[] = 'hashlist';
db_query('SET SESSION group_concat_max_len = 65535');
$subquery_aggregates->addExpression('HEX(SHA1(GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash ORDER BY aa.aggregate_filenames_hash ASC)))', 'hashlist');
}
elseif ($db_type === 'pgsql') {
if ($pg9) {
$fields[] = 'hashlist';
$subquery_aggregates->addExpression("MD5(STRING_AGG(DISTINCT(aa.aggregate_filenames_hash), ',' ORDER BY aa.aggregate_filenames_hash ASC))", 'hashlist');
}
}
elseif ($db_type === 'sqlite') {
$fields[] = 'hashlist';
$subquery_aggregates->addExpression('GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash)', 'hashlist');
$subquery_aggregates->orderBy("aa.aggregate_filenames_hash", "ASC");
}
elseif ($db_type === 'sqlsrv' && $mssql_group_concat) {
$fields[] = 'hashlist';
if ($mssql_md5) {
$subquery_aggregates->addExpression('MD5(GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash))', 'hashlist');
}
else {
$subquery_aggregates->addExpression('GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash)', 'hashlist');
}
// The ORDER BY clause is invalid in views, inline functions,
// derived tables, subqueries, and common table expressions, unless TOP or
// FOR XML is also specified. So no point in doing an order-by like in the
// other cases.
}
// Create join for the advagg_aggregates_versions table.
// 1209600 = 2 weeks.
$time = REQUEST_TIME - max(172800, variable_get('advagg_bundler_outdated', ADVAGG_BUNDLER_OUTDATED), '>');
$subquery_aggregates->join('advagg_aggregates_versions', 'aav', "aav.aggregate_filenames_hash=aa.aggregate_filenames_hash AND aav.root=1 AND aav.atime > {$time}");
$subquery_aggregates = $subquery_aggregates->fields('aa', array(
'filename_hash',
))
->groupBy('aa.filename_hash');
// Create main query for the advagg_files table.
$af_fields = array(
'filename',
'filesize',
'mtime',
'changes',
'linecount',
'filename_hash',
);
// Make drupal_get_installed_schema_version() available.
include_once DRUPAL_ROOT . '/includes/install.inc';
if (drupal_get_installed_schema_version('advagg') >= 7211) {
$af_fields[] = 'filesize_processed';
}
$query = db_select('advagg_files', 'af');
$query->join($subquery_aggregates, 'aa', 'af.filename_hash=aa.filename_hash');
$query = $query->fields('af', $af_fields)
->fields('aa', $fields);
$query->comment('Query called from ' . __FUNCTION__ . '()');
$results = $query->execute();
$analysis = array();
foreach ($results as $row) {
// Implement slower GROUP_CONCAT functionality for non mysql databases.
if (empty($row->hashlist)) {
$subquery_aggregates_versions = db_select('advagg_aggregates_versions', 'aav')->fields('aav')
->condition('aav.root', 1)
->condition('aav.atime', REQUEST_TIME - max(172800, variable_get('advagg_bundler_outdated', ADVAGG_BUNDLER_OUTDATED), '>'), '>');
$subquery_aggregates = db_select('advagg_aggregates', 'aa');
$subquery_aggregates->join($subquery_aggregates_versions, 'aav', 'aav.aggregate_filenames_hash=aa.aggregate_filenames_hash');
$subquery_aggregates = $subquery_aggregates->fields('aa', array(
'aggregate_filenames_hash',
))
->condition('aa.filename_hash', $row->filename_hash)
->groupBy('aa.aggregate_filenames_hash')
->orderBy('aa.aggregate_filenames_hash', 'ASC');
$subquery_aggregates->comment('Query called from ' . __FUNCTION__ . '()');
$aa_results = $subquery_aggregates->execute();
$aa_rows = array();
foreach ($aa_results as $aa_row) {
$aa_rows[] = $aa_row->aggregate_filenames_hash;
}
$row->hashlist = implode(',', $aa_rows);
}
$row->hashlist = drupal_hash_base64($row->hashlist);
$analysis[$row->filename] = array(
'group_hash' => $row->counter . ' ' . $row->hashlist,
'mtime' => $row->mtime,
'filesize' => $row->filesize,
'filesize_processed' => empty($row->filesize_processed) ? $row->filesize : $row->filesize_processed,
'linecount' => $row->linecount,
'changes' => $row->changes,
);
}
arsort($analysis);
// Invoke hook_advagg_bundler_analysis_alter() to give installed modules a
// chance to alter the analysis array.
drupal_alter('advagg_bundler_analysis', $analysis);
return $analysis;
}
Functions
Titre | Deprecated | Résumé |
---|---|---|
advagg_bundler_advagg_hooks_implemented_alter | Implements hook_advagg_hooks_implemented_alter(). | |
advagg_bundler_analyisis_query | Run the analysis query and return the analysis array. | |
advagg_bundler_analysis | Given a filename return a bundle key. | |
advagg_bundler_enabled | Returns TRUE if the bundler will run. | |
advagg_bundler_form_advagg_admin_settings_form_alter | Implements hook_form_FORM_ID_alter(). | |
advagg_bundler_init | Implements hook_init(). | |
advagg_bundler_menu | Implements hook_menu(). |
Constants
Titre | Deprecated | Résumé |
---|---|---|
ADVAGG_BUNDLER_ACTIVE | Default value to see if the bundler should be active or passive. | |
ADVAGG_BUNDLER_ADMIN_MODE | If 4 the admin section gets unlocked. | |
ADVAGG_BUNDLER_GROUPING_LOGIC | Default value to for bundler, set to file size. | |
ADVAGG_BUNDLER_MAX_CSS | Default for maximum number of CSS bundles used in a themes region. | |
ADVAGG_BUNDLER_MAX_JS | Default for maximum number of JS bundles used in a themes region. | |
ADVAGG_BUNDLER_OUTDATED | Default of the last used time before the bundle is considered outdated. |