<?php
class eMySQLi extends MySQLi
{
    
/**
     * Path to the local error log file
     *
     * @var string
     */
    
public $errLog = '/path/to/php_err.log';
    
    
/**
     * Email address for error messages, multiple addresses can be
     * separated by commas
     *
     * @var string
     */
    
public $errMail = 'you@yourdomain.com';
    
    
/**
     * Placeholder for last mysqli error message
     *
     * @var string
     */
    
private $_lastErr;
    
    
/**
     * Placeholder for last mysqli error number
     *
     * @var int
     */
    
private $_lastErrNo;
    
    
/**
     * Static connection array for holding different connections
     *
     * @var array
     */
    
private static $_connections = array();
    
    
/**
     * Default Host for DB connections array
     */
    
const HOST = '127.0.0.1';
    
    
/**
     * Default Username for DB connections array
     */
    
const USER = 'db_user';
    
    
/**
     * Default Password for DB connections array
     */    
    
const PASS = 'db_pass';
    
    
    
/**
     * getConnnection:  Returns a connection to the specified
     *                  database name, which must be valid.
     *
     * @param string $dbName            valid database name
     * @param string $host[optional]    MySQL Host
     * @param string $user[optional]    MySQL User
     * @param string $pass[optional]    MySQL Password
     * @return object                   mysqli database instance for $dbName
     */
    
public static function getConnection($dbName, $host=self::HOST, $user=self::USER, $pass=self::PASS) {
        if ( !isset(
self::$_connections[$dbName]) )
            
self::$_connections[$dbName] = new eMySQLi($host, $user, $pass, $dbName);
        
        return
self::$_connections[$dbName];
    }
    
    
    
/**
     * Constructor:  creates the mysqli connection and tests for errors
     *
     * @param string $host    MySQL Host
     * @param string $user    MySQL User
     * @param string $pass    MySQL Password
     * @param string $db      MySQL Database
     */

    
private function __construct($host, $user, $pass, $db='') {
        
parent::__construct($host, $user, $pass, $db);
        
        
// MySQLi->connect_error was broken until PHP 5.2.9 and 5.3.0,
        // so use the procedural call to check for errors
        
if ( mysqli_connect_error() ) {
            
$this->_lastErr   = mysqli_connect_error();
            
$this->_lastErrNo = mysqli_connect_errno();
            
            
// If we can't connect, treat this as fatal error
            
$this->_handleError(true);
        }
    }
    
    
    
/**
     * multi_query:  Overrides mysqli::multi_query to provide default error handling
     *
     * @param string $query     Statements to be executed, multiple queries are
     *                          are separated by a semi-colon
     * @param bool $fatal       Set true to stop script execution on failure
     * @param bool $mailFlag    Set true to mail error to errMail on failure
     * @return bool             False if the first statement fails. To retrieve subsequent
     *                          errors from other statements, you must call next_result first.
     */
    
public function multi_query($query, $fatal=false, $mailFlag=false) {
        if (
parent::multi_query($query) )
            return
true;
        else {
            
$this->_lastError = $this->error . "\nSQL: $query";
            
$this->_lastErrNo = $this->errno;
            
$this->_handleError($fatal, $mailFlag);
            return
false;
        }
    }
    
    
    
/**
     * store_result:  Overrides mysqli::store_result to provide default error handling
     *
     * @return MySQLi_Result    Buffered result object on success, False on failure
     */
    
public function store_result() {
        
$res = parent::store_result();
        if (
$res === false && ($this->errno !== 0 || $this->field_count > 0) ) {
            
$this->_lastError = $this->error;
            
$this->_lastErrNo = $this->errno;
            
$this->_handleError();
        }
            
        return
$res;
    }
    
        
    
/**
     * query:  Overrides mysqli::query to provide default error handling
     *
     * @param string $query      Statement to be executed
     * @param int $resultmode    Buffered (MYSQI_STORE_RESULT, default) or unbuffered (MYSQLI_USE_RESULT)
     * @param bool $fatal        Set true to stop script execution on failure
     * @param bool $mailFlag     Set true to mail error to errMail on failure
     * @return bool/object       True on success, False on failure. For SELECT, SHOW,
     *                           DESCRIBE or EXPLAIN, a result object is returned.
     */
    
public function query($query, $resultmode=MYSQLI_STORE_RESULT, $fatal=false, $mailFlag=false) {
        
$res = parent::query($query, $resultmode);
        if (
$res === false ) {
            
$this->_lastError = $this->error . "\nSQL: $query";
            
$this->_lastErrNo = $this->errno;
            
$this->_handleError($fatal, $mailFlag);
        }
            
        return
$res;
    }
    
    
    
/**
     * select_db:  Overrides mysqli::select_db to provide default error handling
     *
     * @param string $dbname    Database name
     * @param bool $fatal       Set false to continue script execution on failure (default true)
     * @param bool $mailFlag    Set true to mail error to errMail on failure
     * @return bool             True on success, script terminates on failure
     */
    
public function select_db($dbname, $fatal=true, $mailFlag=false) {
        if (
parent::select_db($dbname) )
            return
true;
        else {
            
$this->_lastError = $this->error;
            
$this->_lastErrNo = $this->errno;
            
$this->_handleError($fatal, $mailFlag);
        }
    }
    
    
    
/**
     * _handleError:  Creates, logs, and optionally mails an error message
     *                containing a backtrace for the last error seen. If the
     *                error is marked fatal, this function stops execution.
     *
     * @param bool $fatal       Set true to stop script execution
     * @param bool $mailFlag    Set true to mail error to errMail
     * @return bool             True if error message was written to errLog
     */
    
private function _handleError($fatal=false, $mailFlag=false) {
        
$errString = '[' . date('Y-m-d H:i:s') . "]\n(errno: $this->_lastErrNo) $this->_lastError\n";
        
        
// Grab the backtrace and remove the first entry,
        // which is always the current function
        
$trace = debug_backtrace();
        
array_shift($trace);
        
        foreach (
$trace as $t ) {
            if ( isset(
$t['file']) )
                
$errString .= basename($t['file']) . ':' . $t['line'] . '  ';
            else
                
$errString .= 'PHP Compiled Code  ';
                
            if ( isset(
$t['class']) )
                
$errString .= $t['class'] . $t['type'];
                
            
$errString .= $t['function'];
            
            
$args = array();
            if (
is_array($t['args']) ) {
                foreach (
$t['args'] as $a ) {
                    switch (
gettype($a) ) {
                        case
'integer':
                        case
'double':
                            
$args[] = $a;
                            break;
                        case
'string':
                            
$replace = array("\r\n", "\n", "\r");
                            
$a = str_replace($replace, ' ', $a);
                            
$a = strlen($a) > 64 ? substr($a, 0, 64) . '...' : $a;
                            
$args[] = "\"$a\"";
                            break;
                        case
'array':
                            
$args[] = 'Array(' . count($a) . ')';
                            break;
                        case
'object':
                            
$args[] = 'Object(' . get_class($a) . ')';
                            break;
                        case
'resource':
                            
$args[] = 'Resource(' . strstr($a, '#') . ')';
                            break;
                        case
'boolean':
                            
$args[] = $a ? 'true' : 'false';
                            break;
                        case
'NULL':
                            
$args[] = 'NULL';
                            break;
                        default:
                            
$args[] = 'Unknown';
                    }
                }
            }
            
            
$errString .= '(' . (empty($args) ? '' : implode(', ', $args)) . ")\n";
        }
        
        if (
$mailFlag ) {
            
$subject = "MYSQLI ERROR: $this->_lastError";
            
mail($this->errMail, $subject, $errString);
        }

        
$r = error_log("$errString\n", 3, $this->errLog);
        
        if (
$fatal )
            exit();
        else
            return
$r;
    }


    
/**
     * escape:  Escapes and formats a value for MySQL statements, trims leading
     *          and trailing whitespace
     *
     * @param string $value     The value to be escaped
     * @param string $type      The format type (none, escape, string, text, long, int,
     *                          bigint, float, double, date, datetime)
     * @return mixed            The escaped value
     */
    
public function escape($value, $type='none') {
        
        
$value = trim($value);
        switch (
$type ) {
            case
'none':
            case
'escape':
                
// Only perform the escaping functions
                
$value = get_magic_quotes_gpc() ? stripslashes($value) : $value;
                
$value = $this->real_escape_string($value);
                break;
            case
'string':
            case
'text':
                
$value = get_magic_quotes_gpc() ? stripslashes($value) : $value;
                
$value = "'" . $this->real_escape_string($value) . "'";
                break;
            case
'int':
            case
'long':
                
$value = intval($value);
                break;
            case
'bigint':
                
$value = ctype_digit($value) ? $value : 0;
                break;
            case
'float':
            case
'double':
                
$value = floatval($value);
                break;
            case
'date':
            case
'datetime':
                if ( empty(
$value) )
                    
$value = "'0000-00-00'";
                else {
                    
$value = get_magic_quotes_gpc() ? stripslashes($value) : $value;
                    
$format = ($type == 'date' ? 'Y-m-d' : 'Y-m-d H:i:s');
                    
$ts = strtotime($value);
                    if (
$ts && $ts > -1 )
                        
$value = "'" . date($format, $ts) . "'";
                    else
                        
$value = "'0000-00-00'";
                }
                break;
        }
        
        return
$value;
    }
}
?>