[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/dtools/wsod/ -> wsod.module (source)

   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('&nbsp;').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  }


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7