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) |
146 if ( !defined('scriptPath') ) |
145 if ( !defined('scriptPath') ) |
147 { |
146 { |
148 if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) ) |
147 if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) ) |
149 { |
148 { |
150 $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']); |
149 $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']); |
|
150 } |
|
151 if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) ) |
|
152 { |
|
153 // user requested http://foo/enano as opposed to http://foo/enano/index.php |
|
154 $_SERVER['REQUEST_URI'] .= '/index.php'; |
151 } |
155 } |
152 $sp = dirname($_SERVER['REQUEST_URI']); |
156 $sp = dirname($_SERVER['REQUEST_URI']); |
153 if($sp == '/' || $sp == '\\') $sp = ''; |
157 if($sp == '/' || $sp == '\\') $sp = ''; |
154 define('scriptPath', $sp); |
158 define('scriptPath', $sp); |
155 define('contentPath', "$sp/index.php?title="); |
159 define('contentPath', "$sp/index.php?title="); |
168 dc_here('dbal: uhoh!<br />'.mysql_error()); |
172 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>'); |
173 grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to MySQL.<br />'.mysql_error().'</p>'); |
170 } |
174 } |
171 |
175 |
172 // Reset some variables |
176 // Reset some variables |
173 $this->query_backtrace = ''; |
177 $this->query_backtrace = array(); |
|
178 $this->query_times = array(); |
|
179 $this->query_sources = array(); |
174 $this->num_queries = 0; |
180 $this->num_queries = 0; |
|
181 |
|
182 $this->debug = ( defined('ENANO_DEBUG') ); |
175 |
183 |
176 dc_here('dbal: we\'re in, selecting database...'); |
184 dc_here('dbal: we\'re in, selecting database...'); |
177 $q = $this->sql_query('USE `'.$dbname.'`;'); |
185 $q = $this->sql_query('USE `'.$dbname.'`;'); |
178 |
186 |
179 if ( !$q ) |
187 if ( !$q ) |
187 } |
195 } |
188 |
196 |
189 function sql_query($q) |
197 function sql_query($q) |
190 { |
198 { |
191 $this->enable_errorhandler(); |
199 $this->enable_errorhandler(); |
|
200 |
|
201 if ( $this->debug && function_exists('debug_backtrace') ) |
|
202 { |
|
203 $backtrace = @debug_backtrace(); |
|
204 if ( is_array($backtrace) ) |
|
205 { |
|
206 $bt = $backtrace[0]; |
|
207 if ( isset($backtrace[1]['class']) ) |
|
208 { |
|
209 if ( $backtrace[1]['class'] == 'sessionManager' ) |
|
210 { |
|
211 $bt = $backtrace[1]; |
|
212 } |
|
213 } |
|
214 $this->query_sources[$q] = substr($bt['file'], strlen(ENANO_ROOT) + 1) . ', line ' . $bt['line']; |
|
215 } |
|
216 unset($backtrace); |
|
217 } |
|
218 |
192 $this->num_queries++; |
219 $this->num_queries++; |
193 $this->query_backtrace .= $q . "\n"; |
220 $this->query_backtrace[] = $q; |
194 $this->latest_query = $q; |
221 $this->latest_query = $q; |
195 dc_here('dbal: making SQL query:<br /><tt>'.$q.'</tt>'); |
222 dc_here('dbal: making SQL query:<br /><tt>'.$q.'</tt>'); |
196 // First make sure we have a connection |
223 // First make sure we have a connection |
197 if ( !$this->_conn ) |
224 if ( !$this->_conn ) |
198 { |
225 { |
203 { |
230 { |
204 $this->report_query($q); |
231 $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>'); |
232 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 } |
233 } |
207 |
234 |
|
235 $time_start = microtime_float(); |
208 $r = mysql_query($q, $this->_conn); |
236 $r = mysql_query($q, $this->_conn); |
|
237 $this->query_times[$q] = microtime_float() - $time_start; |
209 $this->latest_result = $r; |
238 $this->latest_result = $r; |
210 $this->disable_errorhandler(); |
239 $this->disable_errorhandler(); |
211 return $r; |
240 return $r; |
212 } |
241 } |
213 |
242 |
214 function sql_unbuffered_query($q) |
243 function sql_unbuffered_query($q) |
215 { |
244 { |
216 $this->enable_errorhandler(); |
245 $this->enable_errorhandler(); |
|
246 |
217 $this->num_queries++; |
247 $this->num_queries++; |
218 $this->query_backtrace .= '(UNBUFFERED) ' . $q."\n"; |
248 $this->query_backtrace[] = '(UNBUFFERED) ' . $q; |
219 $this->latest_query = $q; |
249 $this->latest_query = $q; |
220 dc_here('dbal: making SQL query:<br /><tt>'.$q.'</tt>'); |
250 dc_here('dbal: making SQL query:<br /><tt>'.$q.'</tt>'); |
221 // First make sure we have a connection |
251 // First make sure we have a connection |
222 if ( !$this->_conn ) |
252 if ( !$this->_conn ) |
223 { |
253 { |
228 { |
258 { |
229 $this->report_query($q); |
259 $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>'); |
260 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 } |
261 } |
232 |
262 |
|
263 $time_start = microtime_float(); |
233 $r = mysql_unbuffered_query($q, $this->_conn); |
264 $r = mysql_unbuffered_query($q, $this->_conn); |
|
265 $this->query_times[$q] = microtime_float() - $time_start; |
234 $this->latest_result = $r; |
266 $this->latest_result = $r; |
235 $this->disable_errorhandler(); |
267 $this->disable_errorhandler(); |
236 return $r; |
268 return $r; |
237 } |
269 } |
238 |
270 |
679 else |
711 else |
680 { |
712 { |
681 return false; |
713 return false; |
682 } |
714 } |
683 } |
715 } |
|
716 /** |
|
717 * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with. |
|
718 */ |
|
719 |
|
720 function sql_report() |
|
721 { |
|
722 global $db, $session, $paths, $template, $plugins; // Common objects |
|
723 if ( !$session->get_permissions('mod_misc') ) |
|
724 { |
|
725 die_friendly('Access denied', '<p>You are not authorized to generate a SQL backtrace.</p>'); |
|
726 } |
|
727 // Create copies of variables that may be changed after header is called |
|
728 $backtrace = $this->query_backtrace; |
|
729 $times = $this->query_times; |
|
730 $template->header(); |
|
731 echo '<h3>SQL query log and timetable</h3>'; |
|
732 echo '<div class="tblholder"> |
|
733 <table border="0" cellspacing="1" cellpadding="4">'; |
|
734 $i = 0; |
|
735 foreach ( $backtrace as $query ) |
|
736 { |
|
737 $i++; |
|
738 $unbuffered = false; |
|
739 if ( substr($query, 0, 13) == '(UNBUFFERED) ' ) |
|
740 { |
|
741 $query = substr($query, 13); |
|
742 $unbuffered = true; |
|
743 } |
|
744 if ( $i == 1 ) |
|
745 { |
|
746 echo '<tr> |
|
747 <th colspan="2">SQL backtrace for a normal page load of ' . htmlspecialchars($paths->cpage['urlname']) . '</th> |
|
748 </tr>'; |
|
749 } |
|
750 else |
|
751 { |
|
752 echo '<tr> |
|
753 <th class="subhead" colspan="2"> </th> |
|
754 </tr>'; |
|
755 } |
|
756 echo '<tr> |
|
757 <td class="row2">Query:</td> |
|
758 <td class="row1"><pre>' . htmlspecialchars($query) . '</pre></td> |
|
759 </tr> |
|
760 <tr> |
|
761 <td class="row2">Time:</td> |
|
762 <td class="row1">' . number_format($this->query_times[$query], 6) . ' seconds</td> |
|
763 </tr> |
|
764 <tr> |
|
765 <td class="row2">Unbuffered:</td> |
|
766 <td class="row1">' . ( $unbuffered ? 'Yes' : 'No' ) . '</td> |
|
767 </tr>'; |
|
768 if ( isset($this->query_sources[$query]) ) |
|
769 { |
|
770 echo '<tr> |
|
771 <td class="row2">Called from:</td> |
|
772 <td class="row1">' . $this->query_sources[$query] . '</td> |
|
773 </tr>'; |
|
774 } |
|
775 } |
|
776 if ( function_exists('array_sum') ) |
|
777 { |
|
778 $query_time_total = array_sum($this->query_times); |
|
779 echo '<tr> |
|
780 <th class="subhead" colspan="2"> |
|
781 Total time taken for SQL queries: ' . round( $query_time_total, 6 ) . ' seconds |
|
782 </th> |
|
783 </tr>'; |
|
784 } |
|
785 echo ' </table> |
|
786 </div>'; |
|
787 $template->footer(); |
|
788 } |
684 } |
789 } |
685 |
790 |
686 ?> |
791 ?> |