Advanced aggregation module; 404 handler.
File
-
includes/
missing.inc
View source
<?php
/**
* @file
* Advanced aggregation module; 404 handler.
*
*/
/**
* Menu Callback; regenerates a missing css file.
*/
function advagg_missing_css() {
ignore_user_abort();
// Try to regenerate missing file
$msg = advagg_missing_regenerate();
// If here send out fast 404.
advagg_missing_fast404($msg);
}
/**
* Menu Callback; regenerates a missing js file.
*/
function advagg_missing_js() {
ignore_user_abort();
// Try to regenerate missing file
$msg = advagg_missing_regenerate();
// If here send out fast 404.
advagg_missing_fast404($msg);
}
/**
* regenerates a missing css file.
*
* @param $filename
* filename
* @param $type
* css or js
* @return
* false if bundle couldn't be generated.
*/
function advagg_missing_regenerate() {
global $base_path, $conf;
// Get filename from request.
$arg = arg();
$filename = array_pop($arg);
$filename = explode('?', $filename);
$filename = array_shift($filename);
$data = advagg_get_bundle_from_filename($filename);
if (is_array($data)) {
list($type, $md5, $counter) = $data;
}
else {
return $data;
}
$_GET['redirect_counter'] = isset($_GET['redirect_counter']) ? intval($_GET['redirect_counter']) : 0;
if ($_GET['redirect_counter'] > 5) {
watchdog('advagg', 'This request could not generate correctly. Loop detected. Request data: %info', array(
'%info' => $_GET['q'],
));
return t('In a Loop.');
}
// Counter in database.
$counter_in_db = db_query("SELECT counter FROM {advagg_bundles} WHERE bundle_md5 = :bundle_md5", array(
':bundle_md5' => $md5,
))->fetchField();
if ($counter_in_db === FALSE) {
return t('Not a valid bundle.');
}
// Cast counter as int
$counter_in_db = intval($counter_in_db);
$counter = intval($counter);
// Set file(s) in cache to FALSE.
$arg[] = $filename;
cache_set(implode('/', $arg), FALSE, 'cache_advagg', TRUE);
advagg_missing_remove_cache($md5);
// Build filepath.
list($css_path, $js_path) = advagg_get_root_files_dir();
if ($type == 'js') {
$file_type_path = $js_path;
}
if ($type == 'css') {
$file_type_path = $css_path;
}
$new_filename = advagg_build_filename($type, $md5, $counter);
$filepath = $file_type_path . '/' . $new_filename;
// Only process if we got an older counter.
// If we have an out of range counter see if a simlar file exists and serve
// that up.
if ($counter > $counter_in_db || $counter < 0) {
$new_filename = advagg_build_filename($type, $md5, $counter_in_db);
$filepath = $file_type_path . '/' . $new_filename;
advagg_missing_send_file($filepath, advagg_build_uri($filepath), $type);
exit;
}
// Break connection and do generation in the background.
if (!empty($_GET['generator'])) {
advagg_missing_async_opp($md5 . ' ' . $counter);
}
// Rebuild file.
$conf['advagg_async_generation'] = FALSE;
$good = advagg_rebuild_bundle($md5, $counter, TRUE);
if (!$good) {
watchdog('advagg', 'This request could not generate correctly. Aggregate not generated. Request data: %info', array(
'%info' => $_GET['q'],
));
return t('Rebuild Failed.');
}
// Serve direct or redirect to file.
$_GET['redirect_counter']++;
$uri = $base_path . $_GET['q'] . '?redirect_counter=' . $_GET['redirect_counter'];
advagg_missing_send_file($filepath, $uri, $type);
exit;
}
/**
* Send the file or send a 307 redirect.
*
* @param $filepath
* filename
* @param $uri
* css or js
*/
function advagg_missing_send_file($filepath, $uri, $type) {
if (!headers_sent()) {
// Code from file_download.
if (file_exists($filepath)) {
$headers = module_invoke_all('file_download', $filepath, $type);
if ($key = array_search(-1, $headers)) {
unset($headers[$key]);
}
// Remove all previously set Cache-Control headers, because we're going to
// override it. Since multiple Cache-Control headers might have been set,
// simply setting a new, overriding header isn't enough: that would only
// override the *last* Cache-Control header. Yay for PHP!
foreach ($headers as $key => $header) {
if (strpos($header, 'Cache-Control:') === 0) {
unset($headers[$key]);
}
elseif (strpos($header, 'ETag:') === 0) {
unset($headers[$key]);
}
}
if (function_exists('header_remove')) {
header_remove('Cache-Control');
header_remove('ETag');
}
else {
drupal_add_http_header('Cache-Control', '');
drupal_add_http_header('Cache-Control', '');
drupal_add_http_header('ETag', '');
drupal_add_http_header('ETag', '');
}
// Set a far future Cache-Control header (480 weeks), which prevents
// intermediate caches from transforming the data and allows any
// intermediate cache to cache it, since it's marked as a public resource.
$headers[] = "Cache-Control: max-age=290304000, no-transform, public";
if (count($headers)) {
advagg_missing_file_transfer($filepath, $headers);
}
}
// advagg_missing_file_transfer didn't run/send data, redirect via header.
header('Location: ' . $uri, TRUE, 307);
usleep(250000);
// Sleep for 250ms
}
}
/**
* Set cache value to FALSE.
*
* @param $bundle_md5
* Bundle's machine name.
*/
function advagg_missing_remove_cache($bundle_md5) {
$files = array();
$results = db_query("SELECT filename, filetype FROM {advagg_files} AS af INNER JOIN {advagg_bundles} AS ab USING ( filename_md5 ) WHERE bundle_md5 = :bundle_md5 ORDER BY porder ASC", array(
':bundle_md5' => $bundle_md5,
));
while ($row = db_fetch_array($results)) {
$files[] = $row['filename'];
$type = $row['filetype'];
}
list($css_path, $js_path) = advagg_get_root_files_dir();
if ($type == 'js') {
$file_type_path = $js_path;
}
if ($type == 'css') {
$file_type_path = $css_path;
}
$filenames = advagg_get_filename($files, $type, '', $bundle_md5);
if (!empty($filenames)) {
foreach ($filenames as $key => $info) {
$filename = $info['filename'];
$filepath = $file_type_path . '/' . $filename;
cache_set($filepath, FALSE, 'cache_advagg', TRUE);
}
}
}
/**
* Output text & set php in async mode.
*
* @param $output
* string - Text to output to open connection.
* @param $wait
* bool - Wait 1 second?
* @param $content_type
* string - Content type header.
* @param $length
* int - Content length.
*/
function advagg_missing_async_opp($output, $wait = TRUE, $content_type = "text/html; charset=utf-8", $length = 0) {
if (headers_sent()) {
return FALSE;
}
// Calculate Content Length
if ($length == 0) {
$output .= "\n";
$length = advagg_missing_strlen($output) - 1;
}
// Prime php for background operations
$loop = 0;
while (ob_get_level() && $loop < 25) {
ob_end_clean();
$loop++;
}
header("Connection: close");
ignore_user_abort();
// Output headers & data
ob_start();
header("Content-type: " . $content_type);
header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
header("Cache-Control: no-cache");
header("Cache-Control: must-revalidate");
header("Content-Length: " . $length);
header("Connection: close");
print $output;
ob_end_flush();
flush();
// wait for 1 second
if ($wait) {
sleep(1);
}
// text returned and connection closed.
// Do background processing. Time taken after should not effect page load times.
return TRUE;
}
/**
* Get the length of a string in bytes
*
* @param $string
* get string length
*/
function advagg_missing_strlen($string) {
if (function_exists('mb_strlen')) {
return mb_strlen($string, '8bit');
}
else {
return strlen($string);
}
}
/**
* Transfer file using http to client. Pipes a file through Drupal to the
* client.
*
* @param $source File to transfer.
* @param $headers An array of http headers to send along with file.
*/
function advagg_missing_file_transfer($source, $headers) {
$source = advagg_missing_file_create_path($source);
$fd = fopen($source, 'rb');
// Return if we can't open the file. Will try a 307 in browser to send file.
if (!$fd) {
return;
}
// Clear the buffer.
if (ob_get_level()) {
ob_end_clean();
}
// Add in headers.
foreach ($headers as $header) {
// To prevent HTTP header injection, we delete new lines that are
// not followed by a space or a tab.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
$header = preg_replace('/\\r?\\n(?!\\t| )/', '', $header);
drupal_add_http_header($header);
}
// Transfer file in 8096 byte chunks.
while (!feof($fd)) {
print fread($fd, 8096);
}
fclose($fd);
exit;
}
/**
* Make sure the destination is a complete path and resides in the file system
* directory, if it is not prepend the file system directory.
*
* @param $dest A string containing the path to verify. If this value is
* omitted, Drupal's 'files' directory will be used.
* @return A string containing the path to file, with file system directory
* appended if necessary, or FALSE if the path is invalid (i.e. outside the
* configured 'files' or temp directories).
*/
function advagg_missing_file_create_path($dest = 0) {
list($css_path, $js_path) = advagg_get_root_files_dir();
$valid_paths = array();
$valid_paths[] = file_directory_path();
$valid_paths[] = $css_path;
$valid_paths[] = $js_path;
foreach ($valid_paths as $file_path) {
if (!$dest) {
return $file_path;
}
// file_check_location() checks whether the destination is inside the Drupal files directory.
if (file_check_location($dest, $file_path)) {
return $dest;
}
else {
if (file_check_location($dest, file_directory_temp())) {
return $dest;
}
else {
if (file_check_location($file_path . '/' . $dest, $file_path)) {
return $file_path . '/' . $dest;
}
}
}
}
// File not found.
return FALSE;
}
Functions
Title | Deprecated | Summary |
---|---|---|
advagg_missing_async_opp | Output text & set php in async mode. | |
advagg_missing_css | Menu Callback; regenerates a missing css file. | |
advagg_missing_file_create_path | Make sure the destination is a complete path and resides in the file system directory, if it is not prepend the file system directory. | |
advagg_missing_file_transfer | Transfer file using http to client. Pipes a file through Drupal to the client. | |
advagg_missing_js | Menu Callback; regenerates a missing js file. | |
advagg_missing_regenerate | regenerates a missing css file. | |
advagg_missing_remove_cache | Set cache value to FALSE. | |
advagg_missing_send_file | Send the file or send a 307 redirect. | |
advagg_missing_strlen | Get the length of a string in bytes |