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"> </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 ?> |