includes/dbal.php
changeset 229 b2f985e4cef3
parent 222 acfdccf7a2bf
child 231 9a1a32bc2050
equal deleted inserted replaced
228:7846d45bd250 229:b2f985e4cef3
    25   {
    25   {
    26     case E_ERROR: case E_USER_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: $errtype = 'Error'; break;
    26     case E_ERROR: case E_USER_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: $errtype = 'Error'; break;
    27     case E_WARNING: case E_USER_WARNING: case E_CORE_WARNING: case E_COMPILE_WARNING: $errtype = 'Warning'; break;
    27     case E_WARNING: case E_USER_WARNING: case E_CORE_WARNING: case E_COMPILE_WARNING: $errtype = 'Warning'; break;
    28   }
    28   }
    29   $debug = debug_backtrace();
    29   $debug = debug_backtrace();
    30   $debug = $debug[2]['file'] . ', line ' . $debug[2]['line'];
    30   if ( !isset($debug[0]['file']) )
       
    31     return false;
       
    32   $debug = $debug[0]['file'] . ', line ' . $debug[0]['line'];
    31   echo "<b>$errtype:</b> $errstr<br />Error source:<pre>$debug</pre>";
    33   echo "<b>$errtype:</b> $errstr<br />Error source:<pre>$debug</pre>";
    32 }
    34 }
    33  
    35  
    34 class mysql {
    36 class mysql {
    35   var $num_queries, $query_backtrace, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values;
    37   var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
    36   var $row = array();
    38   var $row = array();
    37 	var $rowset = array();
    39 	var $rowset = array();
    38   var $errhandler;
    40   var $errhandler;
    39   
    41   
    40   function enable_errorhandler()
    42   function enable_errorhandler()
    41   {
    43   {
       
    44     // echo "DBAL: enabling error handler<br />";
    42     if ( function_exists('debug_backtrace') )
    45     if ( function_exists('debug_backtrace') )
    43     {
    46     {
    44       $this->errhandler = set_error_handler('db_error_handler');
    47       $this->errhandler = set_error_handler('db_error_handler');
    45     }
    48     }
    46   }
    49   }
    47   
    50   
    48   function disable_errorhandler()
    51   function disable_errorhandler()
    49   {
    52   {
       
    53     // echo "DBAL: disabling error handler<br />";
    50     if ( $this->errhandler )
    54     if ( $this->errhandler )
    51     {
    55     {
    52       set_error_handler($this->errhandler);
    56       set_error_handler($this->errhandler);
    53     }
    57     }
    54     else
    58     else
    55     {
    59     {
    56       restore_error_handler();
    60       restore_error_handler();
    57     }
    61     }
    58   }
    62   }
    59   
    63   
    60   function sql_backtrace() {
    64   function sql_backtrace()
    61     $qb = explode("\n", $this->query_backtrace);
    65   {
    62     $bt = '';
    66     return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
    63     //for($i=sizeof($qb)-1;$i>=0;$i--) {
       
    64     for($i=0;$i<sizeof($qb);$i++) {
       
    65       $bt .= $qb[$i]."\n";
       
    66     }
       
    67     return $bt;
       
    68   }
    67   }
    69   
    68   
    70   function ensure_connection()
    69   function ensure_connection()
    71   {
    70   {
    72     if(!$this->_conn)
    71     if(!$this->_conn)
   168       dc_here('dbal: uhoh!<br />'.mysql_error());
   167       dc_here('dbal: uhoh!<br />'.mysql_error());
   169       grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to MySQL.<br />'.mysql_error().'</p>');
   168       grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to MySQL.<br />'.mysql_error().'</p>');
   170     }
   169     }
   171     
   170     
   172     // Reset some variables
   171     // Reset some variables
   173     $this->query_backtrace = '';
   172     $this->query_backtrace = array();
       
   173     $this->query_times = array();
       
   174     $this->query_sources = array();
   174     $this->num_queries = 0;
   175     $this->num_queries = 0;
       
   176     
       
   177     $this->debug = ( defined('ENANO_DEBUG') );
   175     
   178     
   176     dc_here('dbal: we\'re in, selecting database...');
   179     dc_here('dbal: we\'re in, selecting database...');
   177     $q = $this->sql_query('USE `'.$dbname.'`;');
   180     $q = $this->sql_query('USE `'.$dbname.'`;');
   178     
   181     
   179     if ( !$q )
   182     if ( !$q )
   187   }
   190   }
   188   
   191   
   189   function sql_query($q)
   192   function sql_query($q)
   190   {
   193   {
   191     $this->enable_errorhandler();
   194     $this->enable_errorhandler();
       
   195     
       
   196     if ( $this->debug && function_exists('debug_backtrace') )
       
   197     {
       
   198       $backtrace = @debug_backtrace();
       
   199       if ( is_array($backtrace) )
       
   200       {
       
   201         $bt = $backtrace[0];
       
   202         if ( isset($backtrace[1]['class']) )
       
   203         {
       
   204           if ( $backtrace[1]['class'] == 'sessionManager' )
       
   205           {
       
   206             $bt = $backtrace[1];
       
   207           }
       
   208         }
       
   209         $this->query_sources[$q] = substr($bt['file'], strlen(ENANO_ROOT) + 1) . ', line ' . $bt['line'];
       
   210       }
       
   211       unset($backtrace);
       
   212     }
       
   213     
   192     $this->num_queries++;
   214     $this->num_queries++;
   193     $this->query_backtrace .= $q . "\n";
   215     $this->query_backtrace[] = $q;
   194     $this->latest_query = $q;
   216     $this->latest_query = $q;
   195     dc_here('dbal: making SQL query:<br /><tt>'.$q.'</tt>');
   217     dc_here('dbal: making SQL query:<br /><tt>'.$q.'</tt>');
   196     // First make sure we have a connection
   218     // First make sure we have a connection
   197     if ( !$this->_conn )
   219     if ( !$this->_conn )
   198     {
   220     {
   203     {
   225     {
   204       $this->report_query($q);
   226       $this->report_query($q);
   205       grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>');
   227       grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>');
   206     }
   228     }
   207     
   229     
       
   230     $time_start = microtime_float();
   208     $r = mysql_query($q, $this->_conn);
   231     $r = mysql_query($q, $this->_conn);
       
   232     $this->query_times[$q] = microtime_float() - $time_start;
   209     $this->latest_result = $r;
   233     $this->latest_result = $r;
   210     $this->disable_errorhandler();
   234     $this->disable_errorhandler();
   211     return $r;
   235     return $r;
   212   }
   236   }
   213   
   237   
   214   function sql_unbuffered_query($q)
   238   function sql_unbuffered_query($q)
   215   {
   239   {
   216     $this->enable_errorhandler();
   240     $this->enable_errorhandler();
       
   241     
   217     $this->num_queries++;
   242     $this->num_queries++;
   218     $this->query_backtrace .= '(UNBUFFERED) ' . $q."\n";
   243     $this->query_backtrace[] = '(UNBUFFERED) ' . $q;
   219     $this->latest_query = $q;
   244     $this->latest_query = $q;
   220     dc_here('dbal: making SQL query:<br /><tt>'.$q.'</tt>');
   245     dc_here('dbal: making SQL query:<br /><tt>'.$q.'</tt>');
   221     // First make sure we have a connection
   246     // First make sure we have a connection
   222     if ( !$this->_conn )
   247     if ( !$this->_conn )
   223     {
   248     {
   228     {
   253     {
   229       $this->report_query($q);
   254       $this->report_query($q);
   230       grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>');
   255       grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>');
   231     }
   256     }
   232     
   257     
       
   258     $time_start = microtime_float();
   233     $r = mysql_unbuffered_query($q, $this->_conn);
   259     $r = mysql_unbuffered_query($q, $this->_conn);
       
   260     $this->query_times[$q] = microtime_float() - $time_start;
   234     $this->latest_result = $r;
   261     $this->latest_result = $r;
   235     $this->disable_errorhandler();
   262     $this->disable_errorhandler();
   236     return $r;
   263     return $r;
   237   }
   264   }
   238   
   265   
   679 		else
   706 		else
   680 		{
   707 		{
   681 			return false;
   708 			return false;
   682 		}
   709 		}
   683 	}
   710 	}
       
   711   /**
       
   712    * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
       
   713    */
       
   714   
       
   715   function sql_report()
       
   716   {
       
   717     global $db, $session, $paths, $template, $plugins; // Common objects
       
   718     if ( !$session->get_permissions('mod_misc') )
       
   719     {
       
   720       die_friendly('Access denied', '<p>You are not authorized to generate a SQL backtrace.</p>');
       
   721     }
       
   722     // Create copies of variables that may be changed after header is called
       
   723     $backtrace = $this->query_backtrace;
       
   724     $times = $this->query_times;
       
   725     $template->header();
       
   726     echo '<h3>SQL query log and timetable</h3>';
       
   727     echo '<div class="tblholder">
       
   728             <table border="0" cellspacing="1" cellpadding="4">';
       
   729     $i = 0;
       
   730     foreach ( $backtrace as $query )
       
   731     {
       
   732       $i++;
       
   733       $unbuffered = false;
       
   734       if ( substr($query, 0, 13) == '(UNBUFFERED) ' )
       
   735       {
       
   736         $query = substr($query, 13);
       
   737         $unbuffered = true;
       
   738       }
       
   739       if ( $i == 1 )
       
   740       {
       
   741         echo '<tr>
       
   742                 <th colspan="2">SQL backtrace for a normal page load of ' . htmlspecialchars($paths->cpage['urlname']) . '</th>
       
   743               </tr>';
       
   744       }
       
   745       else
       
   746       {
       
   747         echo '<tr>
       
   748                 <th class="subhead" colspan="2">&nbsp;</th>
       
   749               </tr>';
       
   750       }
       
   751       echo '<tr>
       
   752               <td class="row2">Query:</td>
       
   753               <td class="row1"><pre>' . htmlspecialchars($query) . '</pre></td>
       
   754             </tr>
       
   755             <tr>
       
   756               <td class="row2">Time:</td>
       
   757               <td class="row1">' . number_format($this->query_times[$query], 6) . ' seconds</td>
       
   758             </tr>
       
   759             <tr>
       
   760               <td class="row2">Unbuffered:</td>
       
   761               <td class="row1">' . ( $unbuffered ? 'Yes' : 'No' ) . '</td>
       
   762             </tr>';
       
   763       if ( isset($this->query_sources[$query]) )
       
   764       {
       
   765         echo '<tr>
       
   766                 <td class="row2">Called from:</td>
       
   767                 <td class="row1">' . $this->query_sources[$query] . '</td>
       
   768               </tr>';
       
   769       }
       
   770     }
       
   771     echo '  </table>
       
   772           </div>';
       
   773     $template->footer();
       
   774   }
   684 }
   775 }
   685 
   776 
   686 ?>
   777 ?>