Location: PHPKode > scripts > Database Abstraction Layer > database-abstraction-layer/dbal/dbal.php
<?php
/*
***************************************************************************
*   Copyright (C) 2007 by Cesar D. Rodas                                  *
*   hide@address.com                                               *
*                                                                         *
*   Permission is hereby granted, free of charge, to any person obtaining *
*   a copy of this software and associated documentation files (the       *
*   "Software"), to deal in the Software without restriction, including   *
*   without limitation the rights to use, copy, modify, merge, publish,   *
*   distribute, sublicense, and/or sell copies of the Software, and to    *
*   permit persons to whom the Software is furnished to do so, subject to *
*   the following conditions:                                             *
*                                                                         *
*   The above copyright notice and this permission notice shall be        *
*   included in all copies or substantial portions of the Software.       *
*                                                                         *
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
*   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
*   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
*   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
*   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
*   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
*   OTHER DEALINGS IN THE SOFTWARE.                                       *
***************************************************************************
*/
$dir = dirname(__FILE__);
require("${dir}/safeIO.php");

define('DBM_FOLDER',$dir.'/dbm/');
define('DIR_PERMISSION','PHP could not write the cache in given folder');
define('NO_SQL','No SQL statement to execute');
define('ERR_CONN_STR','Error on connection string');
define('NO_CONNECTED','You are not connected to a database');
class dbal {
    /**
     *    Database manager
     *
     *    @var object
     *    @access private
     */
    var $dbm; 
    /**
     *    Information about connection
     *
     *    @var object
     *    @access private
     */
    var $info;
    var $_sql;
    /**
     *    Is connected to database.
     *
     *    @var bool
     *    @access private
     */
    var $isConnected;
    var $cacheDir='.';
    var $buffer;
    
    /**
     *  Class constructor
     *
     *  @access private
     */
    function dbal($uri='') {
        if ($uri!='')$this->connect($uri);
    }    
    
    /**
     *  Connect to a database.
     *  @param string $uri SQL connection string (example: mysql://foo:hide@address.com/bardb)
     *  @access public  
     */
    function connect($uri) {
        $info = parse_url($uri);
        $this->info['scheme']=  isset($info['scheme']) ? $info['scheme'] : '';
        $this->info['user']  =  isset($info['user']) ? $info['user'] : '';
        $this->info['pass']  =  isset($info['pass']) ? $info['pass'] : '';
        $this->info['host']  =  isset($info['host']) ? $info['host'] : '';
        $this->info['db']    =  isset($info['path']) ? substr($info['path'],1) : '';
        return $this->loadDBM();    
    }
    /**
     *  Load a Database Handler
     *
     *  @access private
     *  @return bool
     */
    function loadDBM() {
        if ($this->info['scheme']=='') {
            trigger_error(ERR_CONN_STR,E_USER_WARNING);
            return false;
        }
        require_once(DBM_FOLDER.$this->info['scheme'].".php");
        $this->dbm = new $this->info['scheme'];
        if (!is_subclass_of($this->dbm,'dbm_model') ) {
            die('error in mysql database manager class');
        }
        return true;
    }
    
    /**
     *  Execute an SQL statement, 
     *
     *  In diference to query, this method do not retrieve 
     *  result from database, only true if success or false.
     *
     *  @access public
     */
    function execute() {
        if ( $this->_query_prepare( func_get_args(), $param,$sql) ) {
            if (!$this->_connect_db()) {
                return false;
            }
            return $this->dbm->_execute($sql);
        }
    }
    /**
     *  Execute an SQL statement, 
     *
     *  And retrieve data from database if sucess or false if there is
     *  an error
     *  
     */
    function query() {
        $arg_list = func_get_args();
        /**
         *  Prepare query.
         */
        if ( $this->_query_prepare( $arg_list, $param,$sql) ) {
            /**
             *    Get the time to cache, by default 0
             */
            $this->TTL=0;
            if ( is_numeric($arg_list[$param]) ) {
                $this->TTL = $arg_list[$param];
                $param++;
            }

            if ( $this->TTL > 0) {
                $this->getCacheFileName();
                /* Try to load the cache */
                if (!$this->loadCache($result)) {
                    /* Fails, connect database, then cache*/
                    if (!$this->_connect_db()) {
                        return false;
                    }
                    $result = $this->dbm->_query($sql);
                    if ($result===false) return false;
                    $this->saveCache($result);
                }
            }    else {
                if (!$this->_connect_db()) {
                    return false;
                }
                $result = $this->dbm->_query($sql);
                if ($result===false) return false;
            }
            $f = new dbm_result($result);
            return $f;
        }
        return false;
    }
    
    function compile($_sql) {
        $sql = & $this->_sql;
        $len=strlen($_sql);
        $sql = '';
        for($i=0; $i < $len; $i++) {
            switch ($_sql[$i]) {
                case '"':
                case "'":
                    $start = $_sql[$i];
                    $sql.=$start;
                    for(; $i < $len; $i++) {
                        switch ($_sql[$i]) {
                            case $start:
                                break;
                            case '\\':
                                $sql.=$_sql[$i];
                                $i++;
                            default:
                                $sql.=$_sql[$i];
                                break;
                        }
                    }
                    $sql.=$start;
                    break;
                case ":":
                    $sql.='$';
                    break;
                default:
                    $sql.=$_sql[$i];
                    break;
            }
        }
    }
    
    function close() {
        if ($this->isConnected) {
            $this->dbm->_close();
            $this->isConnected=false;
        }
        $this->dbm=null;
        $this->buffer=null;
    }
    
    function getLastError() {
        return $this->dbm->_getError();
    }

    /**
     *  Connect to a the Database handler
     *
     *  @access private
     *  @return bool
     */
    function _connect_db() {
        if (!$this->isConnected) {
            $this->isConnected=true;
            return $this->dbm->_open($this->info);
        }
        return true;
    }
    
    /**
     *  Prepare an SQL statement
     *
     *  
     */
    function _query_prepare($arg_list,&$param, &$sql) {
        if ( !$this->dbm ) {
            trigger_error(NO_CONNECTED,E_USER_WARNING);
            return false;
        }
        $result = & $this->buffer;
        $sql =  $this->_sql;
        $dbm = & $this->dbm;
        $param = 0;

        if ( is_string($arg_list[$param]) ) {
            $this->compile($arg_list[$param]);
            $sql =  $this->_sql;
            $param++;
        }
        
        if ( $sql=='') {
            trigger_error(NO_SQL,E_USER_ERROR);
            return false;
        }
        
        /**
         *    Change SQL variable by the value of the
         *    array.
         */
        if ( is_array($arg_list[$param]) ) {
            foreach($arg_list[$param] as $k => $v){  
                eval('$'.$k.'=\'"\'.$dbm->doEscape($arg_list[$param][$k]).\'"\';');
            } 
            $sql = eval('return "'.addslashes($sql).'";');
            $param++;
        } 
        $this->__sql = $sql;
        return true;
    }
    
    /**
     *  Load Query result from cache
     *
     *  @param string &$r Here the result will be referenced.
     *  @return bool
     */
    function loadCache(&$r) {
        if ($this->TTL <= 0) return false;
        
        $sio = new safeIO;
        $e=$sio->open($this->cachePath,READ);
        if ($e) {
            $f = stat($this->cachePath);
            $e =  $f['mtime']+$this->TTL > time();
            if ($e) {
                $t = unserialize( $sio->read( filesize($this->cachePath)) );
                foreach($t->content as $k => $v) {
                    $head = & $r[$k];
                    $i=0;
                    foreach($v as $k1 => $v1) {
                        $head[$i ] = $v1;
                        $head[$k1] = & $head[$i++];
                        
                    }
                }
            }
            $sio->close();
        }
        return $e;
    }
    /**
     *  Save query result on cache
     *
     *  @param string &$r Query result
     */
    function saveCache(&$r) {
        if ($this->TTL <= 0) return;
        /**
         *    PHP puts on database result
         *    and index with the column index
         *     and column name. For keep hdd space
         *    we're only store an array with colum
         *    name.
         */
        $buffer=array();
        foreach($r as $k => $v)   {
            $new = & $buffer[$k];
            foreach($v as $k1 => $v1)
                if (!is_numeric($k1))
                    $new[$k1] = $v1;
        }
        /**/
        $cache = new stdClass;
        $cache->ttl = $this->TTL;
        $cache->content = $buffer;
        $str = serialize($cache);
        /* Instante the safe IO */
        $sio = new safeIO;
        $e=$sio->open($this->cachePath,WRITE);
        if ($e) {
            $sio->write( $str,strlen($str));
            $sio->close();
        }
    }
    
    /**
     *  Set cache repository dir
     *
     *  @param string $dir Directory for save cache
     */
    function setCacheDir($dir) {
        if ( is_dir($dir) && is_writable($dir) ) {
            $this->cacheDir = $dir;
        } else {
            trigger_error(DIR_PERMISSION,E_USER_WARNING);
        }
    }
    /**
     *  Get cache repository dir
     *
     *  @return string
     */
    function getCacheDir() {
        return $this->cacheDir;
    }
    /**
     *  Get cache filename
     *
     *  @return string Cache file name
     */
    function getCacheFileName() {
        $pwd = & $this->cacheDir;
        $code  = md5(implode('',$this->info).$this->__sql);
        $path=$code[0]."/".$code[1]."/";
        if (!file_exists($pwd."/".$code[0]) ) mkdir($pwd."/".$code[0]);
        if (!file_exists($pwd."/".$path) ) mkdir($pwd."/".$path);
        $this->cachePath = $this->cacheDir."/".$code[0]."/".$code[1]."/$code.cache";
    }
}

class dbm_result {
    var $_binders;
    var $_result;
    var $_i;
    var $_length;
    
    function dbm_result($arr=array()) {
        $this->_i = 0;
        $this->_length = count($arr);
        $this->_result = $arr;
    }
    
    function fetchAll() {
        return $this->_result;
    }
    
    function & bindColumn($name) {
        $this->_binders[$name]="";
        return $this->_binders[$name];
    }
    
    function setBegin() {
        $this->_i = 0;
    }
    
    
    function getNext() {
        if ( isset($this->_result[$this->_i]) ) {
            foreach($this->_result[$this->_i] as $k => $v) {
                if ( isset($this->_binders[$k]) ) {
                    $this->_binders[$k] = $v;
                }
            }
        } else
            return false;
        return $this->_i< $this->_length ? $this->_result[$this->_i++] : false ;
    }
    
}

class dbm_model {
    /**
     *    Database handler
     *
     *    @var resource
     *    @access private
     */
    var $dbh; 
    function _open() {
        die('this method must be overwrited');
    }
    function _close() {
        die('this method must be overwrited');
    }
    function _query() {
        die('this method must be overwrited');
    }
    function _execute() {
        die('this method must be overwrited');
    }
    function _getError() {
        die('this method must be overwrited');
    }
    function doEscape() {
        die('this method must be overwrited');
    }
    

}

?>
Return current item: Database Abstraction Layer