Source for file _class_db.php
Documentation is available at _class_db.php
* This file implements the DB class.
* Based on ezSQL - Class to make it very easy to deal with MySQL database connections.
* - dynamic extension loading
* - Debug features (EXPLAIN...)
* This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
* See also {@link http://sourceforge.net/projects/evocms/}.
* @copyright (c)2003-2005 by Francois PLANQUE - {@link http://fplanque.net/}.
* Parts of this file are copyright (c)2004 by Justin Vincent - {@link http://php.justinvincent.com}
* Parts of this file are copyright (c)2004-2005 by Daniel HAHLER - {@link http://thequod.de/contact}.
* @license http://b2evolution.net/about/license.html GNU General Public License (GPL)
* b2evolution is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* b2evolution is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with b2evolution; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* This file is based on the following package (excerpt from ezSQL's readme.txt):
* =======================================================================
* Author: Justin Vincent (justin@visunet.ie)
* Web: http://php.justinvincent.com
* Desc: Class to make it very easy to deal with database connections.
* License: FREE / Donation (LGPL - You may do what you like with ezSQL - no exceptions.)
* =======================================================================
* A $10 donation has been made to Justin VINCENT on behalf of the b2evolution team.
* The package has been relicensed as GPL based on
* "You may do what you like with ezSQL - no exceptions."
* 2004-10-14 (email): Justin VINCENT grants François PLANQUE the right to relicense
* this modified class under other licenses. "Just include a link to where you got it from."
* Daniel HAHLER grants François PLANQUE the right to license
* Daniel HAHLER's contributions to this file and the b2evolution project
* under any OSI approved OSS license (http://www.opensource.org/licenses/).
* {@internal Below is a list of authors who have contributed to design/coding of this file: }}
* @author blueyed: Daniel HAHLER
* @author fplanque: François PLANQUE
* @version $Id: _class_db.php,v 1.11.2.9 2005/09/02 21:31:34 fplanque Exp $
* @todo transaction support
if( !defined('DB_USER') ) die( 'Please, do not access this page directly.' );
define( 'EZSQL_VERSION', '1.25' );
define( 'OBJECT', 'OBJECT', true );
define( 'ARRAY_A', 'ARRAY_A', true);
define( 'ARRAY_N', 'ARRAY_N', true);
{ // Function only available since PHP 4.3.0
return mysql_escape_string( $unescaped_string );
* Do we want to explain joins?
* Number of rows we want to dump in debug output:
var $error =
false; // no error yet
* Aliases that will be replaced in queries:
* Strings that will replace the aliases in queries:
* Do we want to use transactions:
* How many transactions are currently nested?
* Rememeber if we have to rollback at the end of a nested transaction construct
* connects to the server and selects a database
* blueyed> Note: Too many parameters (and without default). Should be accessed through members. $halt_on_error is relevant to the connect procedure and should be put after $dbhost.
function DB( $dbuser, $dbpassword, $dbname, $dbhost, $dbaliases, $halt_on_error =
true, $db_use_transactions =
false, $dbtableoptions =
'' )
{ // The mysql extension is not loaded, try to dynamically load it:
$this->print_error( '<p><strong>The PHP MySQL module could not be loaded.</strong></p>
<p>You must edit your php configuration (php.ini) and enable this module.</p>
<p>Do not forget to restart your webserver (if necessary) after editing the PHP conf.</p>' );
// Connect to the Database:
$this->dbh =
@mysql_connect($dbhost,$dbuser,$dbpassword);
$this->print_error( '<p><strong>Error establishing a database connection!</strong></p>
<p>('.
mysql_error().
')</p>
<li>Are you sure you have typed the correct user/password?</li>
<li>Are you sure that you have typed the correct hostname?</li>
<li>Are you sure that the database server is running?</li>
// Prepare aliases for replacements:
foreach( $dbaliases as $dbalias =>
$dbreplace )
$this->dbaliases[] =
'#\b'.
$dbalias.
'\b#'; // \b = word boundary
// echo '<br />'.'#\b'.$dbalias.'\b#';
// echo count($this->dbaliases);
* Select a DB (if another one needs to be selected)
if ( !@mysql_select_db($db,$this->dbh))
$this->print_error( '<strong>Error selecting database ['.
$db.
']!</strong>
<li>Are you sure the database exists?</li>
<li>Are you sure there is a valid database connection?</li>
* Format a string correctly for safe insert under all PHP conditions
* Returns the correct WEEK() function to get the week number for the given date.
* {@see http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html}
* @todo disable when MySQL < 4
* @param string will be used as is
* @param integer 0 for sunday, 1 for monday
function week( $date, $startofweek )
{ // Week starts on Monday:
return ' WEEK( '.
$date.
', 5 ) ';
// Week starts on Sunday:
return ' WEEK( '.
$date.
', 0 ) ';
* Returns the appropriate string to compare $val in a WHERE clause.
* @param mixed Value to create a "compare-String" for
* @return string Either 'IS NULL', 'IN ("a", "b", "c")' or " = 'a'".
return 'IN ("'.
implode('","', $val).
'")';
// All errors go to the global error array $EZSQL_ERROR..
// If no special error string then use mysql default..
$this->last_error =
empty($str) ?
( mysql_error().
'(Errno='.
mysql_errno().
')' ) :
$str;
// Log this error to the global array..
// Is error output turned on or not..
// If there is an error then take note of it
echo
'<div class="error">';
echo
'<p class="error">MySQL error!</p>';
<th>Function / Include</th>
foreach( xdebug_get_function_stack() as $lStack )
if( isset
( $lStack['include_filename'] ) )
echo
'<strong>=></strong> '.
$lStack['include_filename'];
if( isset
( $lStack['class'] ) )
echo
$lStack['class'].
'::';
echo
$lStack['function'].
'()';
<td>
<?php echo
$lStack['file'] ?></td>
<td>
<?php echo
$lStack['line'] ?></td>
* Kill cached query results
* {@internal DB::query(-) }}
* @param string SQL query
* @param string title for debugging
* @return mixed # of rows affected or false if error
function query( $query, $title =
'' )
// Log how the function was called
$this->func_call =
'$db->query("'.
$query.
'")';
// echo $this->func_call, '<br />';
if( preg_match( '#^ \s* create \s* table \s #ix', $query) )
{ // Query is a table creation, we add table options:
// Keep track of the last query for debug..
// Perform the query via std mysql_query function..
'results' =>
'unknown' );
// Resume global query timer
$Timer->resume( 'sql_queries' );
// Start a timer fot this paritcular query:
$Timer->start( 'query' );
$this->result =
@mysql_query( $query, $this->dbh );
// Get duration fpor last query:
// Pause global query timer:
$Timer->pause( 'sql_queries' );
$this->result =
@mysql_query($query,$this->dbh);
// If there is an error then take note of it..
if( preg_match( '#^ \s* (insert|delete|update|replace) \s #ix', $query) )
{ // Query was an insert, delete, update, replace:
// Take note of the insert_id
if ( preg_match("/^\\s*(insert|replace) /i",$query) )
$this->insert_id =
mysql_insert_id($this->dbh);
// Return number fo rows affected
{ // Query was a select, alter, etc...:
// Take note of column info
while( $i <
@mysql_num_fields($this->result) )
$this->col_info[$i] =
@mysql_fetch_field($this->result);
while( $row =
@mysql_fetch_object($this->result) )
// Store relults as an objects within main array
@mysql_free_result($this->result);
// Log number of rows the query returned
// Return number of rows selected
{ // Query was a select, let's try to explain joins...
$this->result =
@mysql_query( 'EXPLAIN '.
$query, $this->dbh );
// Take note of column info
while( $i <
@mysql_num_fields($this->result) )
$this->col_info[$i] =
@mysql_fetch_field($this->result);
while( $row =
@mysql_fetch_object($this->result) )
// Store results as an objects within main array
@mysql_free_result($this->result);
// Log number of rows the query returned
* Get one variable from the DB - see docs for more detail
* Note: To be sure that you received NULL from the DB and not "no rows" check
* @return mixed NULL if not found, the value otherwise (which may also be NULL).
function get_var( $query =
NULL, $x =
0, $y =
0, $title =
'' )
// Log how the function was called
$this->func_call =
"\$db->get_var(\"$query\",$x,$y)";
// If there is a query then perform it if not then use cached results..
$this->query($query, $title);
// Extract var out of cached results based x,y vals
* Get one row from the DB - see docs for more detail
function get_row( $query =
NULL, $output =
OBJECT, $y =
0, $title =
'' )
// Log how the function was called
$this->func_call =
"\$db->get_row(\"$query\",$output,$y)";
// echo $this->func_call, '<br />';
// If there is a query then perform it if not then use cached results..
$this->query($query, $title);
// If the output is an object then return object using the row offset..
// If the output is an associative array then return row as such..
// If the output is an numerical array then return row as such..
// If invalid output type was specified..
$this->print_error(" \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N");
* Function to get 1 column from the cached result set based in X index
* see docs for usage and info
function get_col( $query =
NULL, $x =
0, $title =
'' )
// If there is a query then perform it if not then use cached results..
$this->query( $query, $title );
// Extract the column values
$new_array[$i] =
$this->get_var( NULL, $x, $i );
* Get a column as comma-seperated list.
* @param string|NULLQuery to execute
* @param integer Column of the result set
function get_list( $query =
NULL, $x =
0, $title =
'' )
* Return the the query as a result set - see docs for more details
function get_results( $query =
NULL, $output =
OBJECT, $title =
'' )
// Log how the function was called
$this->func_call =
"\$db->get_results(\"$query\", $output)";
// If there is a query then perform it if not then use cached results..
$this->query($query, $title);
// Send back array of objects. Each row is an object
* Function to get column meta data info pertaining to the last query
* see docs for more info and usage
function get_col_info( $info_type =
'name', $col_offset = -
1 )
$new_array[$i] =
$col->{$info_type};
return $this->col_info[$col_offset]->{$info_type};
* Dumps the contents of any input variable to screen in a nicely
* formatted and easy to understand way - any type: Object, Var or Array
* @param mixed Variable to dump
echo
'<p><table><tr><td bgcolor="fff"><blockquote style="color:#000090">';
echo
'<pre style="font-family:Arial">';
echo
'<span style="color:#800080"><strong>ezSQL</strong> (v'.
EZSQL_VERSION.
") <strong>Variable Dump..</strong></span>\n\n";
print_r( ( $mixed ?
$mixed :
'<span style="color:#f00">No Value / False</span>') );
echo
"\n\n<strong>Type:</strong> ".
ucfirst( $var_type ).
"\n"
.
"<strong>Last Query</strong> [$this->num_queries]<strong>:</strong> "
.
'<strong>Last Function Call:</strong> '.
( $this->func_call ?
$this->func_call :
'None' ).
"\n"
.
'</pre></blockquote></td></tr></table>';
echo
"\n<hr size=1 noshade color=dddddd>";
* Alias for {@link vardump()}
* @param mixed Variable to dump
* Displays the last query string that was sent to the database & a
* table listing results (if there were any).
* (abstracted into a seperate file to save server overhead).
// =====================================================
$r .=
'<table cellspacing="0">';
for( $i =
0, $count =
count($this->col_info); $i <
$count; $i++
)
$r .=
'<th><span class="type">'.
$this->col_info[$i]->type.
' '.
$this->col_info[$i]->max_length.
'</span><br />'
// ======================================================
foreach( $one_row as $item )
$r .=
'<td class="odd">';
$item =
substr( $item, 0, 50 ).
'...';
$r .=
'<tr><td colspan="'.
(count($this->col_info)+
1).
'">No Results</td></tr>';
$r .=
'<tr><td colspan="'.
(count($this->col_info)+
1).
'">Max number of dumped rows has been reached.</td></tr>';
* Displays all queries that have been exectuted
* {@internal DB::dump_queries(-) }}
$time_queries =
$Timer->get_duration( 'sql_queries' );
foreach( $this->queries as $query )
echo
'<h4>Query: '.
$query['title'].
'</h4>';
$sql =
str_replace( 'GROUP BY', '<br />GROUP BY', $sql );
$sql =
str_replace( 'ORDER BY', '<br />ORDER BY', $sql );
$sql =
str_replace( 'AND ', '<br /> AND ', $sql );
echo
'<p class="rows">Rows: '.
$query['rows'].
' - Time: '.
$query['time'].
's';
{ // We have a total time we can use to calculate percentage:
echo
' ('.
number_format( 100/
$time_queries *
$query['time'], 2 ).
'%)';
if( isset
($query['explain']) )
if( $query['results'] !=
'unknown' )
* Note: By default, MySQL runs with autocommit mode enabled.
* This means that as soon as you execute a statement that updates (modifies)
* a table, MySQL stores the update on disk.
* Once you execute a BEGIN, the updates are "pending" until you execute a
* COMMIT {@see DB::commit()} or a ROLLBACK {@see DB:rollback()}
* Note 2: standard syntax would be START TRANSACTION but it's not supported by older
* MySQL versions whereas BEGIN is...
* Note 3: The default isolation level is REPEATABLE READ.
$this->query( 'BEGIN', 'BEGIN transaction' );
* Commit current transaction
{ // Only COMMIT if there are no remaining nested transactions:
$this->query( 'ROLLBACK', 'ROLLBACK transaction because there was a failure somewhere in the nesting of transactions' );
$this->query( 'COMMIT', 'COMMIT transaction' );
* @todo implement transactions!
{ // Only ROLLBACK if there are no remaining nested transactions:
$this->query( 'ROLLBACK', 'ROLLBACK transaction' );
{ // Remember we'll have to roll back at the end!
* $Log: _class_db.php,v $
* Revision 1.11.2.9 2005/09/02 21:31:34 fplanque
* enhanced query debugging features
* Revision 1.23 2005/08/22 18:30:29 fplanque
* Revision 1.22 2005/08/21 22:54:18 blueyed
* Updated documentation for get_var().
* Revision 1.21 2005/08/21 22:44:32 blueyed
* Removed dependencies on T_() and $Timer.
* Revision 1.20 2005/08/17 16:20:54 fplanque
* rollback! I can't see a damn good reason to break existing code just because it happens that MySQL does not have a real boolean type!
* Revision 1.19 2005/07/22 13:54:45 blueyed
* Better format for queries in print_error(); return value of get_var() is false, if nothing found (a DB cannot return false [boolean], but NULL?!)
* Revision 1.18 2005/07/15 18:11:16 fplanque
* Revision 1.17 2005/07/12 23:05:36 blueyed
* Added Timer class with categories 'main' and 'sql_queries' for now.
* Revision 1.16 2005/06/02 18:50:52 fplanque
* Revision 1.15 2005/04/21 12:10:50 blueyed
* Revision 1.14 2005/04/20 18:37:59 fplanque
* Relocation of javascripts and CSS files to their proper places...
* Revision 1.13 2005/04/19 18:04:38 fplanque
* implemented nested transactions for MySQL
* Revision 1.12 2005/03/08 20:32:07 fplanque
* small fixes; slightly enhanced WEEK() handling
* Revision 1.11 2005/02/28 09:06:32 blueyed
* removed constants for DB config (allows to override it from _config_TEST.php), introduced EVO_CONFIG_LOADED
* Revision 1.10 2005/02/23 22:32:44 blueyed
* output xdebug function stack in case of error
* Revision 1.6 2005/02/08 00:43:15 blueyed
* doc, whitespace, html, get_results() and get_row() now always return array, get_var() return false in case of error
* Revision 1.5 2004/11/09 00:25:11 blueyed
* minor translation changes (+MySQL spelling :/)
* Revision 1.3 2004/10/28 11:11:09 fplanque
* MySQL table options handling
* Revision 1.2 2004/10/14 16:28:41 fplanque
* Revision 1.1 2004/10/13 22:46:32 fplanque
* Revision 1.21 2004/10/12 10:27:18 fplanque
* Edited code documentation.