');
/* set base Drupal path as current especially during shutdown */
if (!wsod_drupal_path()) { // change it if it's wrong
$drupal_path = wsod_detect_drupal_path(drupal_get_path('module', 'node')); // FIXME: Any other function which returns current Drupal path?
!empty($drupal_path) ? chdir($drupal_path) : NULL; // change dir to Drupal path
}
module_load_include('inc', 'wsod', 'wsod'); // include functions
// check input variables
$path = $_GET['q'];
isset($_GET['fix']) ? $fix_on_fly = TRUE : NULL; // if fix is defined, then switch to fixing mode
/* check menu handler */
ob_start();
$return = menu_execute_active_handler(); // use q argument to change default path
$content_output = ob_get_clean(); // TODO: what if there is some content?
// CHECK FOR FATAL ERRORS again
if (wsod_fatal_error_checker($output, $fix_on_fly)) {
$verbose ? print $output : print 'n/a' ;
return; // can't continue, if there is some fatal error
}
if (is_null($return)) { /* check menu handler */
$output .= t("%function: returned %handler - it's very bad!", array('%function' => 'menu_execute_active_handler()', '%handler' => 'NULL')).$nl;
$output .= t("WSOD detected!").t(' ').t("Checking for problems...").$nl;
$output .= t("ERROR: %function returned empty value!", array('%function' => 'menu_execute_active_handler()')).$nl;
/* check theme hooks */
$res = wsod_validate_theme_hooks($verbose, $output);
if (!$res) {
if ($fix_on_fly) {
wsod_clear_caches(); // clear all caches
module_rebuild_cache(); // rebuild paths in system table
$output .= t("Re-testing...").$nl;
$res = wsod_validate_theme_hooks($verbose, $output);
if (!$res) {
$output .= t("There is still problem related to theme hooks").$nl; // FIXME
} else {
$output .= t("Fixed?!").$nl;
}
} else {
$output .= t("fix_on_fly is no enabled, please enable it to fix the problem").$nl;
}
} else {
$output .= t("Validation theme hooks completed.").$nl;
}
/* check menu router hooks */
$router_item = menu_get_item($path); // get router item
if (!function_exists($router_item['page_callback'])) { // check if page callback exist
$output .= t("ERROR: Callback: %callback() doesn't exist!
",array('%callback' => $router_item['page_callback'])).$nl;
} else {
wsod_check_page_callback($router_item, $verbose, $output, $content); // get content of page callback
if (empty($content)) { // check if page callback returned some content
$output .= t("ERROR: Callback: %callback() returned empty content!",array('%callback' => $router_item['page_callback'])).$nl;
$output .= t("NOTICE: router_item = %item!",array('%item' => print_r($router_item,true))).$nl;
if ($fix_on_fly) { // if fix mode, then fix the errors
module_load_include('inc', 'system', 'system.admin'); // include functions
$output .= wsod_clear_caches(); // clear all caches
module_rebuild_cache();
$output .= t('Rebuilded module cache.').$nl; // module rebuild cache
} else {
$output .= t("fix_on_fly is no enabled, please enable it to fix the problem").$nl;
}
}
}
/* check forms */
/* TODO
$invalid_forms = wsod_check_forms();
if (!empty($invalid_forms)) {
$output .= "empty forms = ".print_r($invalid_forms,true)."
";
}
*/
// PRINT ALL ERRORS FROM SESSION WHICH WILL HELP TO IDENTIFY THE PROBLEM
if ($_SESSION['messages']['error']) {
$error_messages = $_SESSION['messages']['error'];
foreach ($error_messages as $error) {
$error = str_replace(dirname($drupal_path),'',$error); // delete Drupal full path for security reason
$output .= print_r($error,true); // show session errors
}
}
$output .= t("Done.").$nl;
} else {
/* Standard Drupal behavior section. */
switch ($return) {
case MENU_NOT_FOUND:
$page_output = drupal_not_found(); // return page not found, there can be some status error messages
if (empty($page_output)) {
$err = t('ERROR: Page is empty!').$nl;
} else {
$output .= $page_output; // if nothing to show, output standard page not found
if ($emergency) {
/* TODO: don't print it twice */
//drupal_set_message(t('%function: %handler - seems ok!', array('%function' => 'menu_execute_active_handler()', '%handler' => 'MENU_NOT_FOUND')).$nl);
}
}
break;
case MENU_ACCESS_DENIED:
$page_output = drupal_access_denied(); // return permission denied page , there can be some status error messages
if (empty($page_output)) {
$err = t('ERROR: Page is empty!').$nl;
} else {
$output .= $page_output; // if nothing to show, output standard page not found
if ($emergency) {
/* TODO: don't print it twice */
//drupal_set_message(t('%function: %handler - seems ok!', array('%function' => 'menu_execute_active_handler()', '%handler' => 'MENU_ACCESS_DENIED')).$nl);
}
}
break;
case MENU_SITE_OFFLINE:
$page_output = drupal_site_offline(); // return site offline page, there can be some status error messages
if (empty($page_output)) {
$err = t('ERROR: Page is empty!').$nl;
} else {
$output .= $page_output; // if nothing to show, output standard page not found
if ($emergency) {
/* TODO: don't print it twice */
//drupal_set_message(t('%function: %handler - seems ok!', array('%function' => 'menu_execute_active_handler()', '%handler' => 'MENU_SITE_OFFLINE')).$nl);
}
}
break;
default: // other cases
if (module_exists('content_profile')) {
/* TODO: Sometimes can't render page on shutdown, because of some errors recently
Like: Fatal error: Class 'content_profile_theme_variables' not found in content_profile\content_profile.module on line 585
*/
break;
}
//$page_output = theme('page', $return); // return page not found, there can be some status error messages
if (empty($page_output)) {
$err = t('ERROR: Page is empty!').$nl;
if ($emergency) {
/* TODO: don't print it twice */
//drupal_set_message(t('%function: %handler - seems ok!', array('%function' => 'menu_execute_active_handler()', '%handler' => "$return")).$nl);
}
} else {
$output .= $page_output; // if nothing to show, output standard page not found
}
}
}
$verbose ? print (!empty($output) ? $output : '.') : NULL ; // if you see dots, that mean nothing to show (no WSOD on front page, see README.txt)
if (!empty($output)) {
watchdog('wsod', $output); // log the error
}
return $output;
}
/**
* Clear all caches
*/
function wsod_clear_caches() {
$output = '';
$nl = t('
');
cache_clear_all(NULL, 'cache');
$output .= t("Cleared cache via %function", array('%function' => 'cache_clear_all()')).$nl;
drupal_flush_all_caches();
$output .= t("Cleared cache via %function", array('%function' => 'drupal_flush_all_caches()')).$nl;
return $output;
}
/**
* Check for fatal errors
*/
function wsod_fatal_error_checker(&$output = NULL, $fix_on_fly = TRUE) {
// CHECK FOR FATAL ERRORS (which Drupal can't handle)
$isError = FALSE;
if ($error = error_get_last()){
switch($error['type']){
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
$isError = TRUE;
break;
}
/* Show fatal error on the page.
Most people don't use this module, if they don't have any problem,
so our priority is to show what's wrong with the website
*/
if ($isError){
var_dump($error);
wsod_fatal_error_fix($error['message'], $output, $fix_on_fly); // check if we can fix handled error
$output .= "ERROR: Script execution halted with error: {$error['message']}
";
} else {
$output .= "NOTICE: Script execution completed with error: {$error['message']}
";
}
}
return $isError;
}
/**
* Try to fix fatal error
*/
function wsod_fatal_error_fix($error, &$output, $fix_on_fly = TRUE) {
if (strpos($error,"Cannot redeclare cache") !== FALSE) {
if ($fix_on_fly) { // if fix mode, then fix the errors
/* TODO: We can't use Drupal functions in case of fatal error
module_load_include('inc', 'system', 'system.admin'); // include functions
$output .= t('Trying to rebuild module cache.'); // module rebuild cache
module_rebuild_cache();
*/
}
$output .= 'Tip: Try to disable additional cache module from your settings.php.
\n';
} else if (strpos($error,"Cannot redeclare") !== FALSE) {
if ($fix_on_fly) { // if fix mode, then fix the errors
/* TODO: We can't use Drupal functions in case of fatal error
module_load_include('inc', 'system', 'system.admin'); // include functions
$output .= t('Trying to rebuild module cache.'); // module rebuild cache
module_rebuild_cache();
*/
}
} else if (strpos($error,"Call to undefined function") !== FALSE) {
if ($fix_on_fly) { // if fix mode, then fix the errors
/* TODO:
Solution 1:
We can load all module files and check where it's defined.
After that we can compare and check if the module weight is correct.
Needed module can be detected from $error via function name.
Dependend module can be detected via filename $error['file']
Fix: we can increase weight of the module depends of the other module.
Example code:
$weight = (int) db_result(db_query("SELECT weight FROM {system} WHERE name = 'module1'"));
db_query("UPDATE {system} SET weight = %d WHERE name = 'module2'", $weight + 1);
Solution 2:
If module with specified function wasn't found, maybe there is wrong dependency.
Propose to user to install missing module (get module name from $error).
*/
}
}
if (!$fix_on_fly) {
$output .= t("fix_on_fly is no enabled, please enable it to fix the problem").$nl;
}
}
function wsod_check_forms() {
global $active_forms;
$active_forms=array_unique($active_forms); // remove duplicates
$empty_forms = array();
$form_state = array ('storage' => NULL, 'submitted' => FALSE, 'post' => array ());
$node = array ('storage' => NULL, 'submitted' => FALSE, 'post' => array ());
$max_count = count($active_forms); // get count of array
foreach ($active_forms as $key => $form_id) {
$id = $form_id;
//$form = drupal_get_form($id); //$form = drupal_retrieve_form($form_id, $form_state);
$form = drupal_retrieve_form($form_id, $form_state);
module_invoke_all('form_alter', $form);
if (empty($form)) {
$empty_forms[] = $form_id;
}
//$form_content = drupal_render_form($form_id, $form);
$form_content = drupal_get_form($form_id);
if (empty($form_content)) {
$empty_forms[] = $form_content;
}
if ($key > $max_count+1) {
print "Recurrency detected in form_alter hook!
";
break;
}
}
return array_unique($empty_forms);
}
/**
* Detect Drupal path by scanning each level of current path
*
*/
function wsod_detect_drupal_path(&$output = NULL, $start_dir = __FILE__) {
$drupal_path = FALSE; // set init variable
$path_arr = wsod_pathposall(dirname($start_dir));
$bootstrap_file = '/includes/bootstrap.inc';
foreach ($path_arr as $path) {
if (file_exists($path.$bootstrap_file)) {
$drupal_path = $path;
} else {
$output .= "Couldn't find $path$bootstrap_file!
\n";
}
}
return $drupal_path;
}
/**
* pathposall
*
* Find all occurrences of a subdirs in a path
*
* @param string $dir
* @return array
*/
function wsod_pathposall($dir){
$slash = wsod_detect_filesystem_slash();
$pos = wsod_strposall($dir,$slash);
foreach ($pos as $no) {
$subdir[] = substr($dir, 0, $no);
}
if (is_array($subdir)) {
rsort($subdir); // make list in reverse
return $subdir; // return array of available subdir paths
} else {
return array();
}
}
/**
* Find all occurrences of a needle in a haystack
*
* @param string $haystack
* @param string $needle
* @return array or false
*/
function wsod_strposall($haystack,$needle){
$s=0; $i=0;
while (is_integer($i)){
$i = strpos($haystack,$needle,$s);
if (is_integer($i)) {
$aStrPos[] = $i;
$s = $i+strlen($needle);
}
}
if (isset($aStrPos)) {
return $aStrPos;
} else {
return false;
}
}
/**
* Detect slash type of filesystem
*
*/
function wsod_detect_filesystem_slash() {
if (strpos(__FILE__, '/') !== FALSE) { // detect filesystem
return '/'; // Unix style
} else {
return '\\'; // Windows style
}
}
/**
* Check if Drupal is in current path
*
*/
function wsod_drupal_path() {
$path = getcwd();
$bootstrap_file = '/includes/bootstrap.inc';
return file_exists($path.$bootstrap_file);
}
/**
* Get content type of page header
*
* @return string
*
*/
function wsod_get_header_content_type($header = 'text/html') {
$headers = explode("\n", drupal_get_headers());
$ct_key = 'Content-Type: ';
foreach ($headers as $header_line) {
if (($pos = strpos($ct_key, $header_line)) === FALSE) {
$header = substr($header_line, $pos+strlen($ct_key));
}
}
return $header;
}
/**
* Get content type of page header
*
* @return string
*
*/
function wsod_sess_close() {
wsod_check_wsod();
return TRUE;
}
/**
* WSOD version of Drupal drupal_bootstrap()
*/
function wsod_drupal_bootstrap_run($phase) {
static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION, DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE, DRUPAL_BOOTSTRAP_LANGUAGE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL), $phase_index = 0;
while ($phase >= $phase_index && isset($phases[$phase_index])) {
$current_phase = $phases[$phase_index];
unset($phases[$phase_index++]);
wsod_drupal_bootstrap($current_phase);
}
}
/**
* WSOD version of Drupal _drupal_bootstrap()
*
* Modifications:
* - replace handler sess_close() by WSOD
* Some fatal errors can't be handled by set_error_handler(), but can be handled by session_set_save_handler()
*
* @param $phase
* A constant. Allowed values are:
* DRUPAL_BOOTSTRAP_CONFIGURATION: initialize configuration.
* DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: try to call a non-database cache fetch routine.
* DRUPAL_BOOTSTRAP_DATABASE: initialize database layer.
* DRUPAL_BOOTSTRAP_ACCESS: identify and reject banned hosts.
* DRUPAL_BOOTSTRAP_SESSION: initialize session handling.
* DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: load bootstrap.inc and module.inc, start
* the variable system and try to serve a page from the cache.
* DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
* DRUPAL_BOOTSTRAP_PATH: set $_GET['q'] to Drupal path of request.
* DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input data.
*/
function wsod_drupal_bootstrap($phase) {
global $conf;
switch ($phase) {
case DRUPAL_BOOTSTRAP_CONFIGURATION:
drupal_unset_globals();
// Start a page timer:
timer_start('page');
// Initialize the configuration
conf_init();
break;
case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
// Allow specifying special cache handlers in settings.php, like
// using memcached or files for storing cache information.
require_once variable_get('cache_inc', './includes/cache.inc');
// If the page_cache_fastpath is set to TRUE in settings.php and
// page_cache_fastpath (implemented in the special implementation of
// cache.inc) printed the page and indicated this with a returned TRUE
// then we are done.
if (variable_get('page_cache_fastpath', FALSE) && page_cache_fastpath()) {
exit;
}
break;
case DRUPAL_BOOTSTRAP_DATABASE:
// Initialize the default database.
require_once './includes/database.inc';
db_set_active();
break;
case DRUPAL_BOOTSTRAP_ACCESS:
// Deny access to hosts which were banned - t() is not yet available.
if (drupal_is_denied('host', ip_address())) {
header('HTTP/1.1 403 Forbidden');
print 'Sorry, '. check_plain(ip_address()) .' has been banned.';
exit();
}
break;
case DRUPAL_BOOTSTRAP_SESSION:
require_once variable_get('session_inc', './includes/session.inc');
session_set_save_handler('sess_open', 'wsod_sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc'); // WSOD: replace wsod handle for sess_close()
if(!isset($_SESSION)){ // WSOD: additional condition if session has been already started
session_start();
}
break;
case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
// Initialize configuration variables, using values from settings.php if available.
$conf = variable_init(isset($conf) ? $conf : array());
$cache_mode = variable_get('cache', CACHE_DISABLED);
// Get the page from the cache.
$cache = $cache_mode == CACHE_DISABLED ? '' : page_get_cache();
// If the skipping of the bootstrap hooks is not enforced, call hook_boot.
if (!$cache || $cache_mode != CACHE_AGGRESSIVE) {
// Load module handling.
require_once './includes/module.inc';
bootstrap_invoke_all('boot');
}
// If there is a cached page, display it.
if ($cache) {
drupal_page_cache_header($cache);
// If the skipping of the bootstrap hooks is not enforced, call hook_exit.
if ($cache_mode != CACHE_AGGRESSIVE) {
bootstrap_invoke_all('exit');
}
// We are done.
exit;
}
// Prepare for non-cached page workflow.
drupal_page_header();
break;
case DRUPAL_BOOTSTRAP_LANGUAGE:
drupal_init_language();
break;
case DRUPAL_BOOTSTRAP_PATH:
require_once './includes/path.inc';
// Initialize $_GET['q'] prior to loading modules and invoking hook_init().
drupal_init_path();
break;
case DRUPAL_BOOTSTRAP_FULL:
require_once './includes/common.inc';
_drupal_bootstrap_full();
break;
}
}