PHP Class: MySQL Database Abstraction (Source)

This class is a database abstraction class for MySQL, based on PHPLib.


<?php

/*************************************************************************
 *                                                                       *
 * class.db.inc                                                          *
 *                                                                       *
 *************************************************************************
 *                                                                       *
 * MySQL database abstraction class adapted from PHPLib                  *
 *                                                                       *
 * Copyright (c) 2008 Jon Abernathy <jon@chuggnutt.com>                  *
 * All rights reserved.                                                  *
 *                                                                       *
 * This script 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.                                   *
 *                                                                       *
 * The GNU General Public License can be found at                        *
 * http://www.gnu.org/copyleft/gpl.html.                                 *
 *                                                                       *
 * This script 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.                          *
 *                                                                       *
 * Author(s): Jon Abernathy, jon@chuggnutt.com                           *
 *    Based on work by: Boris Erdmann, Kristian Koehntopp                *
 *                                                                       *
 * Last modified: 04/10/08                                               *
 *                                                                       *
 *************************************************************************/

/**
 *  Provides an abstraction layer for MySQL.
 *
 *  This class is based on the original PHPLib distribution, although
 *  I've eschewed much of the original functionality of the PHPLib version,
 *  mostly in order to get "as close to the metal" as possible and provide
 *  only those functions which I've found I use in all my PHP coding. The
 *  main purpose is to encapsulate and simplify the details of connecting
 *  to a MySQL database and performing queries.
 *
 *  That's right, MySQL and MySQL only. I'm not currently interested in
 *  developing an all-purpose database abstraction tool; in general, I
 *  don't like to be abstracted that far away from the DB.
 *
 *  This class is designed for a single database; to use it, extend the
 *  class in your code, with the given database/user/password you will
 *  connect to. For multiple databases, derive additional classes.
 *
 *  Example of usage:
 *    class DB_Mysql extends DB {
 *        var $host = 'host.db.com';
 *        var $database = 'dbname';
 *        var $user = 'username';
 *        var $password = 'password';
 *        // etc...
 *    }
 *    $db =& new DB_Mysql;
 *    $db->query('SELECT * FROM table');
 *    while ( $db->next_record() ) {
 *        $field = $db->f('field');
 *        // etc...
 *    }
 *
 *  @author Jon Abernathy <jon@chuggnutt.com>
 *  @version 2.0
 */
class DB
{
    /**
     *  Host name of the server to connect to.
     *
     *  @var string $host
     *  @access public
     */
    var $host = '';

    /**
     *  Name of the database to connect to.
     *
     *  @var string $database
     *  @access public
     */
    var $database = '';

    /**
     *  User name used to connect to the server.
     *
     *  @var string $user
     *  @access public
     */
    var $user = '';

    /**
     *  Password used to connect to the server.
     *
     *  @var string $password
     *  @access public
     */
    var $password = '';

    /**
     *  Whether to automatically open a new link on mysql_connect().
     *
     *  @var bool $new_link
     *  @access public
     */
    var $new_link = false;

    /**
     *  Flags whether to automatically call mysql_free_result().
     *
     *  @var bool $auto_free
     *  @access public
     */
    var $auto_free = false;

    /**
     *  Toggle debug mode on or off.
     *
     *  @var bool $debug
     *  @access public
     */
    var $debug = false;

    /**
     *  Defines what action to take when an error is encountered.
     *
     *  The following values are recognized:
     *      yes: halt with message
     *      no: ignore errors quietly
     *      report: ignore error, but spit a warning
     *      email: ignore error, but send email warning - supplemental
     *          value as a comma-separated list - "yes,email"
     *
     *  @var string $halt_on_error
     *  @see $email_on_error
     *  @access public
     */
    var $halt_on_error = 'yes';

    /**
     *  Where to send error messages to.
     *
     *  @var string $email_on_error
     *  @see $halt_on_error
     *  @access public
     */
    var $email_on_error;

    /**
     *  Flags whether or not to use persistent database connections.
     *
     *  @var bool $pconnect
     *  @access public
     */
    var $pconnect = false;

    /**
     *  Contains record result array.
     *
     *  @var array $Record
     *  @access public
     */
    var $Record = array();

    /**
     *  Contains current error code from MySQL.
     *
     *  @var int $errno
     *  @access public
     */
    var $errno = 0;

    /**
     *  Contains current error message from MySQL.
     *
     *  @var string $error
     *  @access public
     */
    var $error = '';

    /**
     *  Contains count of how many queries have been performed.
     *
     *  @var int $query_count
     *  @access public
     */
    var $query_count = 0;

    /**
     *  Contains current database link identifier.
     *
     *  @var int $__link_id
     *  @access private
     */
    var $_link_id;

    /**
     *  Contains current database query identifier.
     *
     *  @var int $__query_id
     *  @access private
     */
    var $_query_id;


    /**
     *  Constructor.
     *
     *  @param string $sql Optional SQL query to execute
     */
    function DB( $sql = '' )
    {
        if ( !empty($sql) ) {
            $this->q($sql);
        }
    }

    /**
     *  Connects to the database.
     *
     *  @param string $database Optional name of the database to connect to
     *  @param string $host Optional database host address
     *  @param string $user Optional database user
     *  @param string $password Optional database password
     *  @return mixed Link ID resource on success, FALSE on fail
     */
    function connect( $database = '', $host = '', $user = '', $password = '' )
    {
        if ( !is_resource($this->_link_id) ) {
            if ( empty($database) ) {
                $database = $this->database;
            }
            if ( empty($host) ) {
                $host = $this->host;
            }
            if ( empty($user) ) {
                $user = $this->user;
            }
            if ( empty($password) ) {
                $password = $this->password;
            }
            
            if ( !$this->pconnect ) {
                $this->_link_id = mysql_connect($host, $user, $password, $this->new_link);
            } else {
                $this->_link_id = mysql_pconnect($host, $user, $password);
            }
            if ( !is_resource($this->_link_id) ) {
                $this->_halt('connect(' . $host . ', ' . $user .', [password]) failed.');
                return false;
            }
            if ( !mysql_select_db($database, $this->_link_id) ) {
                $this->_halt('cannot use database ' . $this->database);
                return false;
            }
        }
        
        return $this->_link_id;
    }

    /**
     *  Closes the current database connection.
     */
    function close()
    {
        if ( !$this->pconnect ) {
            mysql_close($this->_link_id);
        }
        $this->_link_id = null;
        return;
    }

    /**
     *  Frees resources used by recent query.
     */
    function free()
    {
        if ( is_resource($this->_query_id) ) {
            mysql_free_result($this->_query_id);
        }
        $this->_query_id = null;
        return;
    }

    /**
     *  Performs the SQL query, updates the query count.
     *
     *  @param string $sql SQL query
     *  @param bool $unbuffered Run query unbuffered?
     *  @return mixed Query ID resource or false on problem
     */
    function q( $sql, $unbuffered = false )
    {
        if ( $this->query($sql, $unbuffered) ) {
            $this->query_count++;
            return $this->_query_id;
        }
        return false;
    }

    /**
     *  Performs the SQL query.
     *
     *  @param string $sql SQL query
     *  @param bool $unbuffered Run query unbuffered?
     *  @return mixed Query ID resource or false on problem
     */
    function query( $sql, $unbuffered = false )
    {
        // No empty queries, please, since PHP4 chokes on them.
        if ( empty($sql) ) {
            return false;
        }
        
        if ( !$this->connect() ) {
            return false; // we already complained in connect() about that.
        }
        
        // New query, discard previous result.
        if ( $this->auto_free ) {
            $this->free();
        }
        
        if ( $this->debug ) {
            echo '<b>debug:</b> query = ' . $sql . '<br />';
        }
        
        if ( !$unbuffered ) {
            $this->_query_id = mysql_query($sql, $this->_link_id);
        } else {
            $this->_query_id = mysql_unbuffered_query($sql, $this->_link_id);
        }
        if ( !$this->_query_id ) {
            $this->_halt('Invalid SQL: ' . $sql);
        }
        $this->errno = mysql_errno($this->_link_id);
        $this->error = mysql_error($this->_link_id);
        
        // Will return nada if it fails. That's fine.
        return $this->_query_id;
    }

    /**
     *  Passes the SQL query as unbuffered, updates the query count.
     *
     *  @param string $sql SQL query
     *  @return mixed Query ID resource or false on problem
     */
    function uq( $sql )
    {
        return $this->q($sql, true);
    }

    /**
     *  Passes the SQL query as unbuffered.
     *
     *  @param string $sql SQL query
     *  @return mixed Query ID resource or false on problem
     */
    function uquery( $sql )
    {
        return $this->query($sql, true);
    }

    /**
     *  Fetches next row in result set from query as an array, storing
     *  result into the $Record array.
     *
     *  @param int $result_type One of the 3 MySQL constants indicating
     *    the type of array to fetch:
     *    MYSQL_ASSOC: Get associative array only
     *    MYSQL_NUM: Get numeric-indexed array only
     *    MYSQL_BOTH: Get combined (numeric & associative) array
     *  @return bool True if successful, False if at the end
     *    of the result set (or no results returned)
     */
    function next_record( $result_type = MYSQL_ASSOC )
    {
        if ( !$this->_query_id ) {
            $this->_halt('next_record() called with no query pending.');
            return false;
        }
        
        switch ( $result_type ) {
            case MYSQL_ASSOC:
                $this->Record = mysql_fetch_assoc($this->_query_id);
                break;
            case MYSQL_NUM:
                $this->Record = mysql_fetch_row($this->_query_id);
                break;
            default: // MYSQL_BOTH
                $this->Record = mysql_fetch_array($this->_query_id, $result_type);
        }
        $this->errno = mysql_errno($this->_link_id);
        $this->error = mysql_error($this->_link_id);
        
        $status = is_array($this->Record);
        if ( !$status && $this->auto_free ) {
            $this->free();
        }
        return $status;
    }

    /**
     *  Returns the number of affected rows from a query of type
     *  UPDATE, INSERT, REPLACE, and DELETE.
     *
     *  @return int
     */
    function affected_rows()
    {
        return mysql_affected_rows($this->_link_id);
    }

    /**
     *  Returns a count of rows from the result of a SELECT query.
     *
     *  @return int
     */
    function num_rows()
    {
        return mysql_num_rows($this->_query_id);
    }

    /**
     *  Returns a count of fields from the result of a SELECT query.
     *
     *  @return int
     */
    function num_fields()
    {
        return mysql_num_fields($this->_query_id);
    }

    /**
     *  Returns the value of the field from the current row of the result
     *  set.
     *
     *  @param string $name Name of the field
     *  @return mixed
     */
    function f( $name )
    {
        return $this->Record[$name];
    }

    /**
     *  Returns the ID generated from a previous INSERT query.
     *
     *  @return int
     */
    function insert_id()
    {
        return mysql_insert_id($this->_link_id);
    }
    
    /**
     *  Escapes special characters according to the database's current
     *  character set.
     *
     *  @param string $string Value to escape
     *  @return string
     */
    function escape( $string )
    {
        return mysql_real_escape_string($string, $this->_link_id);
    }

    /**
     *  Returns database link ID resource.
     *
     *  @return resource
     */
    function link_id()
    {
        return $this->_link_id;
    }

    /**
     *  Returns database query ID resource.
     *
     *  @return resource
     */
    function query_id()
    {
        return $this->_query_id;
    }

    /**
     *  Generates/handles an error message and, if applicable, halts
     *  the program.
     *
     *  @param string $msg Message to display
     */
    function _halt( $msg )
    {
        $this->error = mysql_error($this->_link_id);
        $this->errno = mysql_errno($this->_link_id);
        
        if ( strpos($this->halt_on_error, 'email') !== false && !empty($this->email_on_error) ) {
            $message = strip_tags($this->_haltmsg($msg));
            mail($this->email_on_error, 'MySQL Database Error', $message);
        }
        
        if ( $this->halt_on_error == 'no' ) {
            return;
        }
        
        echo $this->_haltmsg($msg);
        
        if ( $this->halt_on_error != 'report' ) {
            die('<b>Halted.</b>');
        }
        return;
    }

    /**
     *  Formats the given error message with additional system values.
     *
     *  @param string $msg Message to display
     */
    function _haltmsg( $msg )
    {
        $output = '<br /><br /><b>Database error:</b> ' . $msg . '<br />' . "\n" .
            '<b>MySQL Error</b>: ' . $this->errno . ' (' . $this->error . ')<br />' . "\n\n" .
            '<b>Host:</b> ' . $this->host . '<br />' . "\n" .
            '<b>Database:</b> ' . $this->database . '<br />' . "\n" .
            '<b>User:</b> ' . $this->user . '<br />' . "\n" .
            '<b>Date:</b> ' . date('Y-m-d H:i:s') . '<br />' . "\n";
        return $output;
    }
}

?>