| [ Index ] |
PHP Cross Reference of Drupal 6 (yi-drupal) |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @file 5 * Form settings per user 6 * 7 * @version 8 * $Id: wsod.module,v 1.1.2.18 2009/06/26 14:35:16 kenorb Exp $ 9 * 10 * @developer: 11 * Rafal Wieczorek <kenorb@gmail.com> 12 */ 13 14 /** 15 * Implementation of hook_boot(). Runs even for cached pages. 16 */ 17 function wsod_boot() { 18 global $drupal_path, $wsod_exit; 19 $drupal_path = getcwd(); 20 register_shutdown_function('wsod_check_wsod'); 21 $wsod_exit = FALSE; // init global variable 22 } 23 24 /** 25 * Implementation of hook_menu(). 26 */ 27 function wsod_menu() { 28 $items['wsod'] = array( 29 'title' => 'WSOD', 30 'page callback' => 'wsod_check_wsod', 31 'page arguments' => array(TRUE), 32 'access arguments' => array('access content'), 33 'type' => MENU_CALLBACK, 34 ); 35 return $items; // TODO: not tested yet 36 } 37 38 /** 39 * Implementation of hook_form_alter(). 40 */ 41 function wsod_form_alter($form, $form_state, $form_id) { 42 global $active_forms; 43 $active_forms[] = $form_id; 44 } 45 46 /** 47 * Implementation of hook_exit(). 48 */ 49 function wsod_exit($destination = TRUE) { 50 global $wsod_exit; 51 $wsod_exit = $destination; // set each time TRUE or destination url if hook_exit was executed 52 } 53 54 /** 55 * Detect WSOD and fix it 56 * 57 * @param bool $verbose 58 * if TRUE, print debug output to the screen 59 * @param bool $fix_on_fly 60 * if TRUE, wsod will try to fix the problem on the fly 61 * @param bool $emergency 62 * if TRUE, make additional checking for WSODs (could spent even 3x time) 63 * @return string 64 * returns diagnostics messages 65 * 66 */ 67 function wsod_check_wsod($verbose = FALSE, $fix_on_fly = TRUE, $emergency = FALSE) { 68 global $drupal_path, $wsod_rendered; 69 70 // init variables 71 if (!function_exists('t')) { 72 function t($msg, $args = NULL) { return $msg; } 73 } 74 $nl = t('<br>'); 75 76 // CHECK FOR FATAL ERRORS (which Drupal can't handle) 77 if (wsod_fatal_error_checker($output, $fix_on_fly)) { 78 $output .= t('Backtrace: ') . wsod_get_backtrace().$nl.$nl; 79 $verbose ? print $output : print 'n/a' ; 80 return; // can't continue, if there are some fatal errors 81 } 82 83 /* set base Drupal path as current especially during shutdown */ 84 $curr_path = getcwd(); 85 if ($curr_path <> $drupal_path) { // Note: Working directory of the script can change inside the shutdown function under some web servers, e.g. Apache. 86 chdir($drupal_path); 87 } 88 89 // ignore wsod checking on batch processing and maintenance work 90 if (defined('MAINTENANCE_MODE') || isset($_SESSION['batch_form_state'])) { 91 return; 92 } 93 94 // ignore wsod checking on POST forms, it breaking the forms (#416616) (TODO: later it could be supported - ) 95 if (!empty($_POST['form_build_id'])) { 96 return; 97 } 98 99 $output = ''; // init output 100 101 // ignore if we are on non-html page 102 $headers = drupal_get_headers(); // FIXME: any other way to get headers? 103 $ignore_formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values'); 104 foreach ($ignore_formats as $format) { 105 if (strstr($headers, $format)) { 106 return; 107 } 108 } 109 110 global $wsod_exit; 111 if (!$wsod_exit) { 112 $output .= t("hook_exit wasn't executed properly at %path, possible unexpected exit()/die()", array('%path' => $_GET['q'])).$nl; 113 $output .= t('Backtrace: ') . wsod_get_backtrace().$nl.$nl; 114 $verbose ? print $output : print 'n/a' ; 115 return; 116 } else if (is_string($wsod_exit)) { // check if $wsod_exit contain new destination url 117 return; // ignore any action if redirection via drupal_goto() is made 118 } 119 120 if (!function_exists('menu_get_item')) { 121 /* 122 FIXME: 123 In some version of PHP4 with specified HTTP Requests Drupal includes could be not loaded in this stage 124 causing several errors with 'Call to undefined function'. 125 There are too many dependencies, so if this will happen, it's better to stop at this stage 126 */ 127 $output = ltrim($output); 128 if (!empty($output)) { 129 watchdog('wsod', $output); // log the error 130 } 131 return; 132 } 133 134 // ignore some specified callbacks (like Rebuilding permissions) 135 $router_item = menu_get_item(); 136 if ($router_item['page_callback'] == 'system_batch_page') { 137 return; 138 } 139 140 module_load_include('inc', 'wsod', 'wsod'); // include functions 141 142 // check input variables 143 $path = $_GET['q']; 144 isset($_GET['fix']) ? $fix_on_fly = TRUE : NULL; // if fix is defined, then switch to fixing mode 145 146 /* check menu handler */ 147 ob_start(); 148 $return = menu_execute_active_handler(); // use q argument to change default path 149 $content_output = ob_get_clean(); // TODO: what if there is some content? 150 // CHECK FOR FATAL ERRORS again 151 if (wsod_fatal_error_checker($output, $fix_on_fly)) { 152 $verbose ? print $output : print 'n/a' ; 153 $output = ltrim($output); 154 if (!empty($output)) { 155 watchdog('wsod', $output); // log the error 156 } 157 return; // can't continue, if there is some fatal error 158 } 159 160 if (is_null($return)) { /* check menu handler */ 161 $output .= t("%function: returned %handler - it's very bad!", array('%function' => 'menu_execute_active_handler()', '%handler' => 'NULL')).$nl; 162 $output .= t("WSOD detected!").t(' ').t("Checking for problems...").$nl; 163 $output .= t("ERROR: %function returned empty value!", array('%function' => 'menu_execute_active_handler()')).$nl; 164 /* check theme hooks */ 165 $res = wsod_validate_theme_hooks($verbose, $output); 166 if (!$res) { 167 if ($fix_on_fly) { 168 wsod_clear_caches(); // clear all caches 169 module_rebuild_cache(); // rebuild paths in system table 170 $output .= t("Re-testing...").$nl; 171 $res = wsod_validate_theme_hooks($verbose, $output); 172 if (!$res) { 173 $output .= t("There is still problem related to theme hooks").$nl; // FIXME 174 } else { 175 $output .= t("Fixed?!").$nl; 176 } 177 } else { 178 $output .= t("fix_on_fly is no enabled, please enable it to fix the problem").$nl; 179 } 180 } else { 181 $output .= t("Validation theme hooks completed.").$nl; 182 } 183 184 /* check forms */ 185 /* TODO 186 $invalid_forms = wsod_check_forms(); 187 if (!empty($invalid_forms)) { 188 $output .= "empty forms = ".print_r($invalid_forms,true)."<br>"; 189 } 190 */ 191 192 // PRINT ALL ERRORS FROM SESSION WHICH WILL HELP TO IDENTIFY THE PROBLEM 193 if ($_SESSION['messages']['error']) { 194 $error_messages = $_SESSION['messages']['error']; 195 foreach ($error_messages as $error) { 196 $error = str_replace(dirname($drupal_path),'',$error); // delete Drupal full path for security reason 197 $output .= print_r($error,true); // show session errors 198 } 199 } 200 $output .= t("Done.").$nl; 201 } else { 202 /* Standard Drupal behavior section. */ 203 $title = drupal_set_title(); 204 switch ($return) { 205 case MENU_NOT_FOUND: 206 ob_start(); // start buffering 207 drupal_not_found(); // return page not found, there can be some status error messages 208 $page_output = ob_get_clean(); // get page output 209 if (empty($page_output)) { 210 $output .= t('MENU_NOT_FOUND: Rendered page is empty!').$nl; 211 if ($fix_on_fly) { 212 module_list(TRUE, FALSE); // Refresh the module list to include the new enabled module. [#496198] 213 $output .= t('Module list refreshed!').$nl; 214 drupal_rebuild_theme_registry(); // probably cached theme_registry is broken, rebuild it 215 $output .= t('Theme registry has been removed from cache!').$nl; 216 } 217 } else if (empty($title)) { // 218 $output .= $page_output; // if nothing to show, output standard page not found 219 if ($emergency) { 220 wsod_validate_menu_router(); 221 $output .= t('Menu router table rebuilded!').$nl; 222 //drupal_set_message(t('%function: %handler - seems ok!', array('%function' => 'menu_execute_active_handler()', '%handler' => 'MENU_NOT_FOUND')).$nl); 223 } 224 } 225 break; 226 case MENU_ACCESS_DENIED: 227 ob_start(); // start buffering 228 drupal_access_denied(); // return permission denied page , there can be some status error messages 229 $page_output = ob_get_clean(); // get page output 230 if (empty($page_output)) { 231 $output .= t('MENU_ACCESS_DENIED: Rendered page is empty!').$nl; 232 if ($fix_on_fly) { 233 module_list(TRUE, FALSE); // Refresh the module list to include the new enabled module. [#496198] 234 $output .= t('Module list refreshed!').$nl; 235 drupal_rebuild_theme_registry(); // probably cached theme_registry is broken, rebuild it 236 $output .= t('Theme registry has been removed from cache!').$nl; 237 } 238 } else { 239 $output .= $page_output; // if nothing to show, output standard page not found 240 if ($emergency) { 241 /* TODO: don't print it twice */ 242 //drupal_set_message(t('%function: %handler - seems ok!', array('%function' => 'menu_execute_active_handler()', '%handler' => 'MENU_ACCESS_DENIED')).$nl); 243 } 244 } 245 break; 246 case MENU_SITE_OFFLINE: 247 ob_start(); // start buffering 248 drupal_site_offline(); // return site offline page, there can be some status error messages 249 $page_output = ob_get_clean(); // get page output 250 if (empty($page_output)) { 251 $output .= t('MENU_SITE_OFFLINE: Rendered page is empty!').$nl; 252 if ($fix_on_fly) { 253 module_list(TRUE, FALSE); // Refresh the module list to include the new enabled module. [#496198] 254 $output .= t('Module list refreshed!').$nl; 255 drupal_rebuild_theme_registry(); // probably cached theme_registry is broken, rebuild it 256 $output .= t('Theme registry has been removed from cache!').$nl; 257 } 258 } else { 259 $output .= $page_output; // if nothing to show, output standard page not found 260 if ($emergency) { 261 /* TODO: don't print it twice */ 262 //drupal_set_message(t('%function: %handler - seems ok!', array('%function' => 'menu_execute_active_handler()', '%handler' => 'MENU_SITE_OFFLINE')).$nl); 263 } 264 } 265 break; 266 default: // other cases 267 if (module_exists('content_profile')) { 268 /* TODO: Sometimes can't render page on shutdown, because of some errors recently 269 Like: Fatal error: Class 'content_profile_theme_variables' not found in content_profile\content_profile.module on line 585 270 */ 271 break; 272 } 273 $page_output = theme('page', $return); // return page not found, there can be some status error messages 274 if (empty($page_output)) { 275 $output .= t('DEFAULT: Rendered page is empty!').$nl; 276 if ($fix_on_fly) { 277 module_list(TRUE, FALSE); // Refresh the module list to include the new enabled module. [#496198] 278 $output .= t('Module list refreshed!').$nl; 279 drupal_rebuild_theme_registry(); // probably cached theme_registry is broken, rebuild it 280 $output .= t('Theme registry has been removed from cache!').$nl; 281 } 282 if ($emergency) { 283 /* TODO: don't print it twice */ 284 //drupal_set_message(t('%function: %handler - seems ok!', array('%function' => 'menu_execute_active_handler()', '%handler' => "$return")).$nl); 285 } 286 } else { 287 $output .= $page_output; // if nothing to show, output standard page not found 288 } 289 } 290 } 291 292 if (is_null($return) || $emergency) { 293 /* check menu router hooks */ 294 /* Note: 295 Following block could slow down your website even 3x, 296 so it should be executed only when menu handler returns NULL or wsod checking it in emergency mode. 297 */ 298 $router_item = menu_get_item($path); // get router item 299 $router_callback = $router_item['page_callback']; 300 if (!empty($router_callback) && !function_exists($router_callback)) { // check if page callback exist 301 $output .= t("ERROR: Callback: %callback() doesn't exist!<br>",array('%callback' => $router_item['page_callback'])).$nl; 302 } else if (!empty($router_callback)) { 303 $res = wsod_check_page_callback($router_item, $verbose, $output, $content); // get content of page callback 304 if (empty($res)) { // check if page callback returned some content 305 $output .= t("ERROR: Callback: %callback() returned empty content!",array('%callback' => $router_callback)).$nl; 306 $output .= t("NOTICE: router_item = %item!",array('%item' => print_r($router_item,true))).$nl; 307 if ($fix_on_fly) { // if fix mode, then fix the errors 308 module_load_include('inc', 'system', 'system.admin'); // include functions 309 $output .= wsod_clear_caches(); // clear all caches 310 module_rebuild_cache(); 311 $output .= t('Rebuilded module cache.').$nl; // module rebuild cache 312 } else { 313 $output .= t("fix_on_fly is no enabled, please enable it to fix the problem").$nl; 314 } 315 } else { 316 $output .= $res; // output rendered result 317 } 318 } 319 } 320 321 $verbose ? print (!empty($output) ? $output : '.') : NULL ; // if you see dots, that mean nothing to show (no WSOD on front page, see README.txt) 322 $output = ltrim($output); // remove spaces from output 323 if (!empty($output)) { 324 $output = ltrim($output); 325 watchdog('wsod', $output); // log the error 326 } 327 return $output; 328 } 329 330 /** 331 * Check menu_router table, if it's truncated 332 * 333 * Read more: 334 * http://drupal.org/node/238760 (menu_router table truncated and site goes down) 335 * http://drupal.org/node/496198 (module_list() is broken in 6.x) 336 * 337 */ 338 function wsod_validate_menu_router() { 339 if (!db_result(db_query("SELECT * FROM menu_router WHERE path = '%s'", 'node'))) { 340 wsod_rebuild_menu_router_table(); // Rebuild menu_router table 341 } 342 } 343 344 /** 345 * Rebuild system table 346 * 347 */ 348 function wsod_rebuild_system_table() { 349 require_once './includes/file.inc'; // needed for file_scan_directory() 350 require_once './modules/system/system.module'; // needed for system_get_files_database() 351 module_rebuild_cache(); // rebuild paths in system table 352 } 353 354 /** 355 * Rebuild menu_router table 356 * 357 */ 358 function wsod_rebuild_menu_router_table() { 359 module_list(TRUE, FALSE); // Refresh the module list to include the new enabled module. [#496198] 360 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); // we need enter into FULL bootstrap mode to fully rebuild menu_router table 361 menu_router_build(TRUE); // Rebuild menu_router table 362 } 363 364 /** 365 * Return backtrace 366 */ 367 function wsod_get_backtrace() { 368 $backtrace = debug_backtrace(TRUE); 369 array_shift($backtrace); // remove this function from array 370 array_shift($backtrace); // remove wsod function 371 foreach ($backtrace as $index => $function) { 372 $function = $function['function']; 373 $line = $backtrace[$index]['line']; 374 $filename = basename($backtrace[$index]['file']); 375 $buffer .= "$function($filename:$line);"; 376 } 377 return $buffer; 378 } 379 380 /** 381 * Clear all caches 382 */ 383 function wsod_clear_caches() { 384 $output = ''; 385 $nl = t('<br>'); 386 cache_clear_all(NULL, 'cache'); 387 $output .= t("Cleared cache via %function", array('%function' => 'cache_clear_all()')).$nl; 388 drupal_flush_all_caches(); 389 $output .= t("Cleared cache via %function", array('%function' => 'drupal_flush_all_caches()')).$nl; 390 return $output; 391 } 392 393 /** 394 * Check for fatal errors 395 */ 396 function wsod_fatal_error_checker(&$output, $fix_on_fly = TRUE) { 397 // CHECK FOR FATAL ERRORS (which Drupal can't handle) 398 $isError = FALSE; 399 if ((version_compare(PHP_VERSION, '5.2.0') === 1) && $error = error_get_last()) { 400 switch($error['type']){ 401 case E_ERROR: 402 case E_CORE_ERROR: 403 case E_COMPILE_ERROR: 404 case E_USER_ERROR: 405 $isError = TRUE; 406 break; 407 } 408 /* Show fatal error on the page. 409 Most people don't use this module, if they don't have any problem, 410 so our priority is to show what's wrong with the website 411 */ 412 if ($isError){ 413 var_dump($error); 414 wsod_fatal_error_fix($error['message'], $output, $fix_on_fly); // check if we can fix handled error 415 $output .= "ERROR: Script execution halted with error: {$error['message']}<br>"; 416 } else { 417 $output .= "NOTICE: Script execution completed with error: {$error['message']}<br>"; 418 } 419 } 420 return $isError; 421 } 422 423 /** 424 * Try to fix fatal error 425 */ 426 function wsod_fatal_error_fix($error, &$output, $fix_on_fly = TRUE) { 427 if (strpos($error,"Cannot redeclare cache") !== FALSE) { 428 if ($fix_on_fly) { // if fix mode, then fix the errors 429 /* TODO: We can't use Drupal functions in case of fatal error 430 module_load_include('inc', 'system', 'system.admin'); // include functions 431 $output .= t('Trying to rebuild module cache.'); // module rebuild cache 432 module_rebuild_cache(); 433 */ 434 } 435 $output .= 'Tip: Try to disable additional cache module from your settings.php.<br>\n'; 436 } else if (strpos($error,"Cannot redeclare") !== FALSE) { 437 if ($fix_on_fly) { // if fix mode, then fix the errors 438 /* TODO: We can't use Drupal functions in case of fatal error 439 module_load_include('inc', 'system', 'system.admin'); // include functions 440 $output .= t('Trying to rebuild module cache.'); // module rebuild cache 441 module_rebuild_cache(); 442 */ 443 } 444 } else if (strpos($error,"Call to undefined function") !== FALSE) { 445 if ($fix_on_fly) { // if fix mode, then fix the errors 446 wsod_rebuild_system_table(); // rebuild system table 447 $output .= 'Rebuild system table.<br>\n'; 448 wsod_force_module_load_all(); // force module load 449 $output .= 'Force to load all enabled module files.<br>\n'; 450 preg_match('/(?P<func_prefix>[a-z0-9]+)_/', $error, $matches); 451 $module_prefix = $matches['func_prefix']; 452 //$func_name = $matches['func_name']; // TODO 453 $module_name = db_result(db_query("SELECT name FROM {system} WHERE name LIKE '$module_name%%' AND status = 0 LIMIT 1")); 454 if ($module_name && db_query("UPDATE {system} SET status = 1 WHERE name LIKE '$module_name%%' AND status = 0 LIMIT 1")) { 455 $output .= 'Enabled potential dependency module: $module_name.<br>\n'; 456 } 457 $output .= 'Please refresh the page if problem has been fixed.<br>\n'; 458 /* TODO: 459 Another Solution: 460 Eventually we can load all module files and check where it's defined. 461 After that we can compare and check if the module weight is correct. 462 Needed module can be detected from $error via function name. 463 Dependend module can be detected via filename $error['file'] 464 Fix: we can increase weight of the module depends of the other module. 465 Example code: 466 $weight = (int) db_result(db_query("SELECT weight FROM {system} WHERE name = 'module1'")); 467 db_query("UPDATE {system} SET weight = %d WHERE name = 'module2'", $weight + 1); 468 */ 469 } 470 } 471 if (!$fix_on_fly) { 472 $output .= t("fix_on_fly is no enabled, please enable it to fix the problem").$nl; 473 } 474 } 475 476 /** 477 * Force load all enabled modules 478 * 479 */ 480 function wsod_force_module_load_all() { 481 foreach (module_list(TRUE, FALSE) as $name) { 482 include_once drupal_get_filename('module', $name); 483 } 484 } 485 486 /** 487 * Check forms 488 */ 489 function wsod_check_forms() { 490 // TODO 491 global $active_forms; 492 $active_forms=array_unique($active_forms); // remove duplicates 493 $empty_forms = array(); 494 $form_state = array ('storage' => NULL, 'submitted' => FALSE, 'post' => array ()); 495 $node = array ('storage' => NULL, 'submitted' => FALSE, 'post' => array ()); 496 $max_count = count($active_forms); // get count of array 497 foreach ($active_forms as $key => $form_id) { 498 $id = $form_id; 499 //$form = drupal_get_form($id); //$form = drupal_retrieve_form($form_id, $form_state); 500 501 $form = drupal_retrieve_form($form_id, $form_state); 502 module_invoke_all('form_alter', $form); 503 if (empty($form)) { 504 $empty_forms[] = $form_id; 505 } 506 507 //$form_content = drupal_render_form($form_id, $form); 508 $form_content = drupal_get_form($form_id); 509 if (empty($form_content)) { 510 $empty_forms[] = $form_content; 511 } 512 513 if ($key > $max_count+1) { 514 print "Recurrency detected in form_alter hook!<br>"; 515 break; 516 } 517 } 518 return array_unique($empty_forms); 519 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Jul 9 18:01:44 2012 | Cross-referenced by PHPXref 0.7 |