Update version.php to 3.3.0
[isso.git] / Db.php
diff --git a/Db.php b/Db.php
index 2877d9a4b3519757465e047e7bec040f25031980..169893d0d88f8a7b885d7635a638583c8d80ed4f 100644 (file)
--- a/Db.php
+++ b/Db.php
@@ -2,11 +2,11 @@
 /*=====================================================================*\
 || ###################################################################
 || # Blue Static ISSO Framework
-|| # Copyright ©2002-[#]year[#] Blue Static
+|| # Copyright (c)2005-2009 Blue Static
 || #
 || # This program 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; version [#]gpl[#] of the License.
+|| # the Free Software Foundation; version 2 of the License.
 || #
 || # This program is distributed in the hope that it will be useful, but
 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 \*=====================================================================*/
 
 /**
-* Abstract Database Layer (Db.php)
-*
-* @package     ISSO
-*/
+ * Abstract Database Layer (Db.php)
+ *
+ * @package    ISSO
+ */
 
-require_once('ISSO/Functions.php');
+require_once(ISSO . '/Functions.php');
 
 /**
-* Abstract Database Layer
-*
-* This class provides an abstract template for all RDBMS layers. All
-* ISSO abstraction layers should inherit this class. It provides error
-* reporting, SQL analysis, and general connection functionality.
-*
-* Constants:
-*              ISSO_SHOW_QUERIES_LIVE - Show queries in page output as they are sent
-*
-* @author              Blue Static
-* @copyright   Copyright ©2002 - [#]year[#], Blue Static
-* @version             $Revision$
-* @package             ISSO
-* 
-*/
+ * Abstract Database Layer
+ *
+ * This class provides an abstract template for all RDBMS layers. All
+ * ISSO abstraction layers should inherit this class. It provides error
+ * reporting, SQL analysis, and general connection functionality.
+ *
+ * Constants:
+ *             ISSO_SHOW_QUERIES_LIVE - Show queries in page output as they are sent
+ *
+ * @author             Blue Static
+ * @copyright  Copyright (c)2005 - 2009, Blue Static
+ * @package            ISSO
+ * 
+ */
 abstract class BSDb
 {
        /**
-       * Determines whether or not errors should be shown
-       * @var  bool
-       */
+        * Determines whether or not errors should be shown
+        * @var bool
+        */
        public $showerrors = true;
        
        /**
-       * Current error number
-       * @var  integer
-       */
+        * Current error number
+        * @var integer
+        */
        protected $errnum = 0;
        
        /**
-       * Description of current error
-       * @var  string
-       */
+        * Description of current error
+        * @var string
+        */
        protected $errstr = '';
        
        /**
-       * Currend open database connection
-       * @var  integer
-       */
+        * Currend open database connection
+        * @var integer
+        */
        protected $dblink = null;
        
        /**
-       * Current query ID
-       * @var  integer
-       */
+        * Current query ID
+        * @var integer
+        */
        protected $result = null;
        
        /**
-       * Current query string
-       * @var  string
-       */
+        * Current query string
+        * @var string
+        */
        protected $querystr = '';
        
        /**
-       * History of all executed queryies
-       * @var  array
-       */
+        * History of all executed queryies
+        * @var array
+        */
        protected $history = array();
        
-       // ###################################################################
        /**
-       * Connect to a the specified database
-       *
-       * @param        string  Server name
-       * @param        string  User name
-       * @param        string  Password
-       * @param        string  Database name
-       * @param        bool    Use p-connect?
-       *
-       * @return       bool    Result of connect
-       */
-       public function connect($server, $user, $password, $database, $pconnect)
+        * Returns the history information array
+        *
+        * @return      array   History record
+        */
+       public function getHistory()
+       {
+               return $this->history;
+       }
+       
+       /**
+        * Connect to a the specified database
+        *
+        * @param       string  Server name
+        * @param       string  User name
+        * @param       string  Password
+        * @param       string  Database name
+        *
+        * @return      bool    Result of connect
+        */
+       public function connect($server, $user, $password, $database)
        {
                if ($this->dblink == false)
                {
-                       $this->dblink = $this->{($pconnect ? '_connect' : '_pConnect')}($server, $user, $password, $database);
+                       $this->dblink = $this->_connect($server, $user, $password, $database);
                        
                        if ($this->dblink == false)
                        {
-                               $this->_error('DB-Link == false, cannot connect');
+                               throw new BSDbException('Connect Failed', -1, 'DB-Link == false; connect failed');
                                return false;
                        }
                        
@@ -116,52 +123,38 @@ abstract class BSDb
        }
        
        /**
-       * Abstract function that returns a database link after establishing a connection. This just
-       * calls the function and does not do any checking
-       *
-       * @param        string  Server name
-       * @param        string  User name
-       * @param        string  Password
-       * @param        string  Database name
-       *
-       * @return       integer Database link
-       */
+        * Abstract function that returns a database link after establishing a connection. This just
+        * calls the function and does not do any checking
+        *
+        * @param       string  Server name
+        * @param       string  User name
+        * @param       string  Password
+        * @param       string  Database name
+        *
+        * @return      integer Database link
+        */
        protected abstract function _connect($server, $user, $password, $database);
        
        /**
-       * Abstract function that returns a database link after establishing a connection. This just
-       * calls the function and does not do any checking
-       *
-       * @param        string  Server name
-       * @param        string  User name
-       * @param        string  Password
-       * @param        string  Database name
-       *
-       * @return       resource        Database link
-       */
-       protected abstract function _pConnect($server, $user, $password, $database);
-       
-       // ###################################################################
-       /**
-       * Send a query to the open database link
-       *
-       * @param        string  Query string
-       *
-       * @return       integer Result
-       */
+        * Send a query to the open database link
+        *
+        * @param       string  Query string
+        *
+        * @return      BSDbResult      Result object, or NULL
+        */
        public function query($string)
        {
                $time = microtime();
                
                $this->querystr = $string;
-               $this->result = $this->_query($this->dblink, $string);
+               $this->result = $this->_query($string);
                                
                if (!$this->result)
                {
-                       $this->_error('Invalid SQL query');
+                       throw new BSDbException($this->_errorString(), $this->_errorNumber(), $string);
                }
                
-               $this->history[] = $history = array('query' => $string, 'time' => BSFunctions::FetchMicrotimeDiff($time), 'trace' => $this->registry->format_debug_trace(debug_backtrace()));
+               $this->history[] = $history = array('query' => $string, 'time' => BSFunctions::fetch_microtime_diff($time), 'trace' => BSFunctions::format_backtrace(debug_backtrace()));
                
                if (defined('ISSO_SHOW_QUERIES_LIVE'))
                {
@@ -171,333 +164,417 @@ abstract class BSDb
                        }
                }
                
-               return $this->result;
+               if (preg_match('/^\s*(select|describe|show|explain)/i', $string))
+               {
+                       $class = get_class($this) . 'Result';
+                       return new $class($this->result);
+               }
        }
        
        /**
-       * Abstract function that executes the query command on the database
-       *
-       * @param        string  Query string
-       *
-       * @return       integer Result ID
-       */
+        * Abstract function that executes the query command on the database
+        *
+        * @param       string  Query string
+        *
+        * @return      integer Result ID
+        */
        protected abstract function _query($query);
        
-       // ###################################################################
        /**
-       * Escape a string (depending on character set, if supported)
-       *
-       * @param        string  String to be escaped
-       *
-       * @return       string  Escaped string
-       */
+        * Escape a string (depending on character set, if supported)
+        *
+        * @param       string  String to be escaped
+        *
+        * @return      string  Escaped string
+        */
        public function escapeString($string)
        {
                return $this->_escapeString($string);
        }
        
        /**
-       * Abstract function that calls the escape_string() method
-       *
-       * @param        string  String to escape
-       *
-       * @return       string  Escaped string
-       */
+        * Abstract function that calls the escape_string() method
+        *
+        * @param       string  String to escape
+        *
+        * @return      string  Escaped string
+        */
        protected abstract function _escapeString($string);
        
-       // ###################################################################
        /**
-       * Escapes a binary string for insertion into the database
-       *
-       * @param        string  Unescaped data
-       *
-       * @return       string  Escaped binary data
-       */
+        * Escapes a binary string for insertion into the database
+        *
+        * @param       string  Unescaped data
+        *
+        * @return      string  Escaped binary data
+        */
        public function escapeBinary($binary)
        {
                return $this->_escapeBinary($binary);
        }
        
        /**
-       * Abstract function that calls escape_binary()
-       *
-       * @param        string  Binary to escape
-       *
-       * @return       string  Escaped binary
-       */
+        * Abstract function that calls escape_binary()
+        *
+        * @param       string  Binary to escape
+        *
+        * @return      string  Escaped binary
+        */
        protected abstract function _escapeBinary($string);
        
-       // ###################################################################
        /**
-       * Unescapes a binary string that was fetched from the database
-       *
-       * @param        string  Escaped data
-       *
-       * @return       string  Unescaped binary data
-       */
+        * Unescapes a binary string that was fetched from the database
+        *
+        * @param       string  Escaped data
+        *
+        * @return      string  Unescaped binary data
+        */
        public function unescapeBinary($binary)
        {
                return $this->_unescapeBinary($binary);
        }
        
        /**
-       * Abstract function that calls unescape_binary()
-       *
-       * @param        string  Escaped data
-       *
-       * @return       string  Data that has been unescaped
-       */
+        * Abstract function that calls unescape_binary()
+        *
+        * @param       string  Escaped data
+        *
+        * @return      string  Data that has been unescaped
+        */
        protected abstract function _unescapeBinary($string);
        
-       // ###################################################################
        /**
-       * Fetch the query result as an array
-       *
-       * @param        integer Result
-       * @param        bool    Return an associative array?
-       *
-       * @return       array   A row of the query result
-       */
-       public function fetchArray($result, $assoc = true)
+        * Send a query and return the first row of the results
+        *
+        * @param       string  Query string
+        * @param       string  Result return function (in the database layer)
+        *
+        * @return      mixed   Results in variable formats
+        */
+       public function queryFirst($string, $callback = 'fetchArray')
        {
-               return $this->{($assoc ? '_fetchAssocArray' : '_fetchRowArray')}($result);
+               $resource = $this->query($string);
+               if ($resource)
+               {
+                       $return = $resource->$callback();
+                       $resource->free();
+                       return $return;
+               }
+               else
+               {
+                       return false;
+               }
        }
        
        /**
-       * Abstract function that returns an associative array of given result
-       *
-       * @param        integer Result
-       *
-       * @return       array   Result array
-       */
-       protected abstract function _fetchAssocArray($result);
+        * Returns the errror number
+        */
+       public abstract function _errorNumber();
        
        /**
-       * Abstract function that returns a row array of given result
-       *
-       * @param        integer Result
-       *
-       * @return       array   Result array
-       */
-       protected abstract function _fetchRowArray($result);
+        * Returns the error string
+        */
+       public abstract function _errorString();
        
-       // ###################################################################
        /**
-       * Fetch the query result as an object
-       *
-       * @param        integer Result
-       *
-       * @return       object  An object with the query result
-       */
-       public function fetchObject($result)
+        * Fetch the unique ID of the record just inserted
+        *
+        * @return      integer Insert-ID
+        */
+       public function insertId()
        {
-               return $this->_fetchObject($result);
+               return $this->_insertID();
        }
        
        /**
-       * Abstract function that returns an object for a given result
-       *
-       * @param        integer Result
-       *
-       * @return       object  Row object
-       */
-       public abstract function _fetchObject($result);
+        * Abstract function that returns the ID of the most recently-inserted
+        * record
+        *
+        * @return      integer Insertion ID
+        */
+       protected abstract function _insertId();
        
-       // ###################################################################
        /**
-       * Send a query and return the first row of the results
-       *
-       * @param        string  Query string
-       * @param        string  Result return function (in the database layer)
-       *
-       * @return       mixed   Results in variable formats
-       */
-       public function queryFirst($string, $callback = 'fetchArray')
+        * Fetch the number of rows affected by the query
+        *
+        * @param       integer Result
+        *
+        * @return      integer Number of affected rows
+        */
+       public function affectedRows()
        {
-               $resource = $this->query($string);
-               if ($resource)
-               {
-                       $return = $this->$callback($resource);
-                       $this->_freeResult($resource);
-                       return $return;
-               }
-               else
+               return $this->_affectedRows($this->result);
+       }
+       
+       /**
+        * Abstract function that returns the number of affected rows in the result
+        *
+        * @param       integer Result ID
+        *
+        * @return      integer Number of rows
+        */
+       protected abstract function _affectedRows($result);
+       
+       /**
+        * Sends the command to start a transaction. This command should never
+        * be reached as it's always overridden
+        */
+       public abstract function begin();
+       
+       /**
+        * Sends the command to rollback to a given savepoint. This command
+        * should never be reached as it's always overridden
+        *
+        * @param       string  Named savepoint
+        */
+       public abstract function rollback();
+       
+       /**
+        * Sends the command to commit the entire transaction. This command
+        * should never be reached as it's always overridden
+        */
+       public abstract function commit();
+}
+
+/**
+ * Database Result
+ *
+ * This class holds result information for a database result
+ *
+ * @author             rsesek
+ * @copyright  Copyright (c)2005 - 2009, Blue Static
+ * @package            ISSO
+ *
+ */
+abstract class BSDbResult implements Iterator
+{
+       /**
+        * The result resource
+        * @var resource
+        */
+       protected $result;
+       
+       /**
+        * All the results (as used by the iterator implementation)
+        * @var string
+        */
+       protected $iterator;
+       
+       /**
+        * Sets the resource and returns a result object
+        *
+        * @param       resource        The result of the query
+        */
+       public function __construct($result)
+       {
+               $this->result = $result;
+       }
+       
+       /**
+        * Method to call on the result
+        * 
+        * @param       string  Method name
+        *
+        * @return      array   Array of results
+        */
+       public function fetchAll($method = 'fetchArray')
+       {
+               $results = array();
+               while ($result = $this->$method())
                {
-                       return false;
+                       $results[] = $result;
                }
+               return $results;
        }
        
-       // ###################################################################
        /**
-       * Free the current query result
-       *
-       * @param        integer Result
-       */
-       public function freeResult($result)
+        * Fetch the query result as an array
+        *
+        * @param       integer Result
+        * @param       bool    Return an associative array?
+        *
+        * @return      array   A row of the query result
+        */
+       public function fetchArray($assoc = true)
        {
-               $this->_freeResult($result);
-               $this->result = null;
-               $this->querystr = '';
+               return $this->{($assoc ? '_fetchAssocArray' : '_fetchRowArray')}($this->result);
        }
        
        /**
-       * Abstract function that frees a given result
-       *
-       * @param        integer Result ID
-       */
-       protected abstract function _freeResult($result);
+        * Abstract function that returns an associative array of given result
+        *
+        * @param       integer Result
+        *
+        * @return      array   Result array
+        */
+       protected abstract function _fetchAssocArray($result);
        
-       // ###################################################################
        /**
-       * Fetch the unique ID of the record just inserted
-       *
-       * @return       integer Insert-ID
-       */
-       public function insertId()
+        * Abstract function that returns a row array of given result
+        *
+        * @param       integer Result
+        *
+        * @return      array   Result array
+        */
+       protected abstract function _fetchRowArray($result);
+       
+       /**
+        * Fetch the query result as an object
+        *
+        * @param       integer Result
+        *
+        * @return      object  An object with the query result
+        */
+       public function fetchObject()
        {
-               return $this->_insertID();
+               return $this->_fetchObject($this->result);
        }
        
        /**
-       * Abstract function that returns the ID of the most recently-inserted
-       * record
-       *
-       * @return       integer Insertion ID
-       */
-       protected abstract function _insertId();
-               
-       // ###################################################################
+        * Abstract function that returns an object for a given result
+        *
+        * @param       integer Result
+        *
+        * @return      object  Row object
+        */
+       public abstract function _fetchObject($result);
+       
        /**
-       * Fetch the number of rows in the result
-       *
-       * @param        integer Result
-       *
-       * @return       integer Number of rows
-       */
-       public function numRows($result)
+        * Free the current query result
+        *
+        * @param       integer Result
+        */
+       public function free()
        {
-               return $this->_numRows($result);
+               $this->_freeResult($this->result);
        }
        
        /**
-       * Abstract function that returns the number of rows in the result
-       *
-       * @param        integer Result ID
-       *
-       * @return       integer Number of rows
-       */
-       protected abstract function _numRows($result);
+        * Abstract function that frees a given result
+        *
+        * @param       integer Result ID
+        */
+       protected abstract function _freeResult($result);
        
-       // ###################################################################
        /**
-       * Fetch the number of rows affected by the query
-       *
-       * @param        integer Result
-       *
-       * @return       integer Number of affected rows
-       */
-       public function affectedRows($result)
+        * Fetch the number of rows in the result
+        *
+        * @param       integer Result
+        *
+        * @return      integer Number of rows
+        */
+       public function size()
        {
-               return $this->_affectedRows($result);
+               return $this->_numRows($this->result);
        }
        
        /**
-       * Abstract function that returns the number of affected rows in the result
-       *
-       * @param        integer Result ID
-       *
-       * @return       integer Number of rows
-       */
-       protected abstract function _affectedRows($result);
+        * Abstract function that returns the number of rows in the result
+        *
+        * @param       integer Result ID
+        *
+        * @return      integer Number of rows
+        */
+       protected abstract function _numRows($result);
        
+       // ###################################################################
+       // # Iterator implementation
        // ###################################################################
        /**
-       * Sends the command to start a transaction. This command should never
-       * be reached as it's always overridden
-       */
-       public abstract function transactionStart();
+        * Rewinds the iterator by resetting the result pointer
+        *
+        * @return void
+        */
+       public function rewind()
+       {
+               if ($this->iterator == null)
+               {
+                       $this->iterator = $this->fetchAll();
+               }
+               reset($this->iterator);
+       }
+       
+       /**
+        * Returns the current row
+        *
+        * @return array
+        */
+       public function current()
+       {
+               return current($this->iterator);
+       }
        
-       // ###################################################################
        /**
-       * Sends the command to set this as a savepoint. This command should never
-       * be reached as it's always overridden
-       *
-       * @param        string  Named savepoint
-       */
-       public abstract function transactionSavepoint($name);
+        * Returns the key (result pointer index)
+        *
+        * @return integer
+        */
+       public function key()
+       {
+               return key($this->iterator);
+       }
        
-       // ###################################################################
        /**
-       * Sends the command to rollback to a given savepoint. This command
-       * should never be reached as it's always overridden
-       *
-       * @param        string  Named savepoint
-       */
-       public abstract function transactionRollback($name = null);
+        * Advances the iterator and returns the row
+        *
+        * @return array
+        */
+       public function next()
+       {
+               return next($this->iterator);
+       }
        
-       // ###################################################################
        /**
-       * Sends the command to commit the entire transaction. This command
-       * should never be reached as it's always overridden
-       */
-       public abstract function transactionCommit();
+        * Checks whether the iterator is valid
+        *
+        * @return boolean
+        */
+       public function valid()
+       {
+               return ($this->current() !== false);
+       }
+}
+
+/**
+ * Database Exception
+ *
+ * Exception handler class for the database classes
+ *
+ * @author             Blue Static
+ * @copyright  Copyright (c)2005 - 2009, Blue Static
+ * @package            ISSO
+ *
+ */
+class BSDbException extends Exception
+{
+       /**
+        * The query string that caused the error
+        * @var string
+        */
+       protected $query;
        
-       // ###################################################################
        /**
-       * Constructs a table of query information output that is used in some
-       * other modules to display a list of queries. This merely formats
-       * a DB->history array entry nicely
-       *
-       * @param        array   An entry from DB->history
-       *
-       * @return       string  A formatted table block
-       */
-       protected function _constructDebugQuery($query)
+        * Initializes a new database exception
+        * 
+        * @param       string  The error message
+        * @param       ineger  MySQL error code
+        * @param       sring   Query string that caused the error
+        */
+       public function __construct($error, $errorNum, $query)
        {
-               $block = "<strong>Query:</strong>\n\n<div>" . htmlspecialchars($query['query']) . "</div>\n";
-               $block .= "<tr style=\"background-color: #FFFFFF; text-align: left\">\n\t<td>\n\t\t";
-               $block .= "<strong>Time:</strong> $query[time]<br />\n\t\t<br />\n\t\t";
-               $block .= "<strong>Backtrace:</strong>\n\t\t<div>" . implode("<br />\n", $query['trace']) . "</div>\n\t</td>\n</tr>";
-               
-               return BSRegister::Message('Query Debug', $block, 1, true, false, 0);
+               $this->query = $query;
+               parent::__construct($error, $errorNum);
        }
        
-       // ###################################################################
        /**
-       * Error wrapper for ISSO->message()
-       *
-       * @param        string  User defined error message
-       */
-       protected function _error($message)
+        * Returns the query that failed
+        *
+        * @return      string
+        */
+       public function getQuery()
        {
-               if ($this->showerrors)
-               {
-                       if ($this->dblink)
-                       {
-                               $this->errnum = call_user_func($this->commands['error_num'], $this->dblink);
-                               $this->errstr = call_user_func($this->commands['error_str'], $this->dblink);
-                       }
-                       
-                       $style['code'] = 'font-family: \'Courier New\', Courier, mono; font-size: 11px;';
-                       
-                       $message_prepped = "<blockquote>\n<p>";
-                       $message_prepped .= "\n\t&raquo; <strong>Query:</strong>\n<br /> <pre style=\"$style[code]\">" . htmlspecialchars($this->querystr) ."</pre>\n<br />";
-                       $message_prepped .= "\n\t&raquo; <strong>Error Number:</strong> <span style=\"$style[code]\">" . $this->errnum . "</span>\n<br />";
-                       $message_prepped .= "\n\t&raquo; <strong>Error Message:</strong> <span style=\"$style[code]\">" . $this->errstr . "</span>\n<br />";
-                       $message_prepped .= "\n\t&raquo; <strong>Additional Notes:</strong> <span style=\"$style[code]\">" . $message . "</span>\n<br />";
-                       $message_prepped .= "\n\t&raquo; <strong>File:</strong> <span style=\"$style[code]\">" . $_SERVER['PHP_SELF'] . "</span>\n";
-                       $message_prepped .= "\n</p>\n</blockquote>";
-                       
-                       BSRegister::Message('Database Error in `<em>' . BSRegister::GetApplication() . '</em>`', $message_prepped, 3);
-                       exit;
-               }
+               return $this->query;
        }
 }
 
-/*=====================================================================*\
-|| ###################################################################
-|| # $HeadURL$
-|| # $Id$
-|| ###################################################################
-\*=====================================================================*/
 ?>
\ No newline at end of file