From b79cc545ec020f7dd4bd83dcd06af3cf2b1fcaff Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Tue, 28 Aug 2012 05:20:20 -0400
Subject: [PATCH] Improvements/fixes for Larry skin

---
 program/include/rcube_db.php |  316 ++++++++++++++++++++++++++++------------------------
 1 files changed, 171 insertions(+), 145 deletions(-)

diff --git a/program/include/rcube_db.php b/program/include/rcube_db.php
index 64db6d5..f97d70a 100644
--- a/program/include/rcube_db.php
+++ b/program/include/rcube_db.php
@@ -1,6 +1,6 @@
 <?php
 
-/*
+/**
  +-----------------------------------------------------------------------+
  | program/include/rcube_db.php                                          |
  |                                                                       |
@@ -25,8 +25,8 @@
  *
  * This is a wrapper for the PHP PDO
  *
- * @package    Database
- * @version    1.0
+ * @package Database
+ * @version 1.0
  */
 class rcube_db
 {
@@ -36,13 +36,14 @@
     protected $db_mode;               // Connection mode
     protected $dbh;                   // Connection handle
 
-    protected $db_error = false;
-    protected $db_error_msg = '';
-    protected $conn_failure = false;
+    protected $db_error        = false;
+    protected $db_error_msg    = '';
+    protected $conn_failure    = false;
     protected $a_query_results = array('dummy');
-    protected $last_res_id = 0;
+    protected $last_res_id     = 0;
+    protected $db_index        = 0;
     protected $tables;
-    protected $db_index = 0;
+    protected $variables;
 
     protected $options = array(
         // column/table quotes
@@ -54,11 +55,11 @@
     /**
      * Factory, returns driver-specific instance of the class
      *
-     * @param string $db_dsnw  DSN for read/write operations
-     * @param string $db_dsnr  Optional DSN for read only operations
-     * @param bool   $pconn    Enables persistent connections
+     * @param string $db_dsnw DSN for read/write operations
+     * @param string $db_dsnr Optional DSN for read only operations
+     * @param bool   $pconn   Enables persistent connections
      *
-     * @return rcube_db  Object instance
+     * @return rcube_db Object instance
      */
     public static function factory($db_dsnw, $db_dsnr = '', $pconn = false)
     {
@@ -67,6 +68,7 @@
             'sqlite2' => 'sqlite',
             'sybase'  => 'mssql',
             'dblib'   => 'mssql',
+            'mysqli'  => 'mysql',
         );
 
         $driver = isset($driver_map[$driver]) ? $driver_map[$driver] : $driver;
@@ -82,13 +84,12 @@
         return new $class($db_dsnw, $db_dsnr, $pconn);
     }
 
-
     /**
      * Object constructor
      *
-     * @param  string $db_dsnw DSN for read/write operations
-     * @param  string $db_dsnr Optional DSN for read only operations
-     * @param bool   $pconn    Enables persistent connections
+     * @param string $db_dsnw DSN for read/write operations
+     * @param string $db_dsnr Optional DSN for read only operations
+     * @param bool   $pconn   Enables persistent connections
      */
     public function __construct($db_dsnw, $db_dsnr = '', $pconn = false)
     {
@@ -107,7 +108,9 @@
         $this->init();
     }
 
-
+    /**
+     * Initialization of the object with driver specific code
+     */
     protected function init()
     {
         // To be used by driver classes
@@ -116,7 +119,7 @@
     /**
      * Connect to specific database
      *
-     * @param  array $dsn  DSN for DB connections
+     * @param array $dsn DSN for DB connections
      *
      * @return PDO database handle
      */
@@ -135,6 +138,11 @@
 
         // Connect
         try {
+            // with this check we skip fatal error on PDO object creation
+            if (!class_exists('PDO', false)) {
+                throw new Exception('PDO extension not loaded. See http://php.net/manual/en/intro.pdo.php');
+            }
+
             $this->conn_prepare($dsn);
 
             $dbh = new PDO($dsn_string, $dsn['username'], $dsn['password'], $dsn_options);
@@ -142,7 +150,7 @@
             // don't throw exceptions or warnings
             $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
         }
-        catch (PDOException $e) {
+        catch (Exception $e) {
             $this->db_error     = true;
             $this->db_error_msg = $e->getMessage();
 
@@ -158,20 +166,39 @@
         return $dbh;
     }
 
-
+    /**
+     * Driver-specific preparation of database connection
+     *
+     * @param array $dsn DSN for DB connections
+     */
     protected function conn_prepare($dsn)
     {
     }
 
+    /**
+     * Driver-specific configuration of database connection
+     *
+     * @param array $dsn DSN for DB connections
+     * @param PDO   $dbh Connection handler
+     */
     protected function conn_configure($dsn, $dbh)
     {
     }
 
+    /**
+     * Driver-specific database character set setting
+     *
+     * @param string $charset Character set name
+     */
+    protected function set_charset($charset)
+    {
+        $this->query("SET NAMES 'utf8'");
+    }
 
     /**
-     * Connect to appropiate database depending on the operation
+     * Connect to appropriate database depending on the operation
      *
-     * @param  string $mode Connection mode (r|w)
+     * @param string $mode Connection mode (r|w)
      */
     public function db_connect($mode)
     {
@@ -214,7 +241,6 @@
         }
     }
 
-
     /**
      * Activate/deactivate debug mode
      *
@@ -225,50 +251,73 @@
         $this->options['debug_mode'] = $dbg;
     }
 
-
-    protected function set_charset($charset)
+    /**
+     * Writes debug information/query to 'sql' log file
+     *
+     * @param string $query SQL query
+     */
+    protected function debug($query)
     {
-        $this->query("SET NAMES 'utf8'");
+        if ($this->options['debug_mode']) {
+            rcube::write_log('sql', '[' . (++$this->db_index) . '] ' . $query . ';');
+        }
     }
-
 
     /**
      * Getter for error state
      *
-     * @param  boolean  True on error
+     * @param int $res_id Optional query result identifier
+     *
+     * @return string Error message
      */
-    public function is_error()
+    public function is_error($res_id = null)
     {
-        return $this->db_error ? $this->db_error_msg : false;
-    }
+        if ($res_id !== null) {
+            return $this->_get_result($res_id) === false ? $this->db_error_msg : null;
+        }
 
+        return $this->db_error ? $this->db_error_msg : null;
+    }
 
     /**
      * Connection state checker
      *
-     * @param  boolean  True if in connected state
+     * @return boolean True if in connected state
      */
     public function is_connected()
     {
         return !is_object($this->dbh) ? false : $this->db_connected;
     }
 
-
     /**
      * Is database replication configured?
-     * This returns true if dsnw != dsnr
+     *
+     * @return bool Returns true if dsnw != dsnr
      */
     public function is_replicated()
     {
       return !empty($this->db_dsnr) && $this->db_dsnw != $this->db_dsnr;
     }
 
+    /**
+     * Get database runtime variables
+     *
+     * @param string $varname Variable name
+     * @param mixed  $default Default value if variable is not set
+     *
+     * @return mixed Variable value or default
+     */
+    public function get_variable($varname, $default = null)
+    {
+        // to be implemented by driver class
+        return $default;
+    }
 
     /**
      * Execute a SQL query
      *
-     * @param  string  SQL query to execute
-     * @param  mixed   Values to be inserted in query
+     * @param string SQL query to execute
+     * @param mixed  Values to be inserted in query
      *
      * @return number  Query handle identifier
      */
@@ -285,16 +334,15 @@
         return $this->_query($query, 0, 0, $params);
     }
 
-
     /**
      * Execute a SQL query with limits
      *
-     * @param  string  SQL query to execute
-     * @param  number  Offset for LIMIT statement
-     * @param  number  Number of rows for LIMIT statement
-     * @param  mixed   Values to be inserted in query
+     * @param string SQL query to execute
+     * @param int    Offset for LIMIT statement
+     * @param int    Number of rows for LIMIT statement
+     * @param mixed  Values to be inserted in query
      *
-     * @return number  Query handle identifier
+     * @return int Query handle identifier
      */
     public function limitquery()
     {
@@ -306,20 +354,20 @@
         return $this->_query($query, $offset, $numrows, $params);
     }
 
-
     /**
      * Execute a SQL query with limits
      *
-     * @param  string $query   SQL query to execute
-     * @param  number $offset  Offset for LIMIT statement
-     * @param  number $numrows Number of rows for LIMIT statement
-     * @param  array  $params  Values to be inserted in query
-     * @return number  Query handle identifier
+     * @param string $query   SQL query to execute
+     * @param int    $offset  Offset for LIMIT statement
+     * @param int    $numrows Number of rows for LIMIT statement
+     * @param array  $params  Values to be inserted in query
+     *
+     * @return int Query handle identifier
      */
     protected function _query($query, $offset, $numrows, $params)
     {
         // Read or write ?
-        $mode = preg_match('/^select/i', ltrim($query)) ? 'r' : 'w';
+        $mode = preg_match('/^(select|show)/i', ltrim($query)) ? 'r' : 'w';
 
         $this->db_connect($mode);
 
@@ -348,9 +396,7 @@
 
         $query = rtrim($query, ';');
 
-        if ($this->options['debug_mode']) {
-            rcube::write_log('sql', '[' . (++$this->db_index) . '] ' . $query . ';');
-        }
+        $this->debug($query);
 
         $query = $this->dbh->query($query);
 
@@ -368,12 +414,12 @@
         return $this->_add_result($query);
     }
 
-
     /**
      * Get number of affected rows for the last query
      *
      * @param  number $res_id Optional query handle identifier
-     * @return mixed   Number of rows or false on failure
+     *
+     * @return int Number of rows or false on failure
      */
     public function affected_rows($res_id = null)
     {
@@ -384,14 +430,12 @@
         return 0;
     }
 
-
     /**
      * Get last inserted record ID
-     * For Postgres databases, a sequence name is required
      *
-     * @param  string $table  Table name (to find the incremented sequence)
+     * @param string $table Table name (to find the incremented sequence)
      *
-     * @return mixed   ID or false on failure
+     * @return mixed ID or false on failure
      */
     public function insert_id($table = '')
     {
@@ -409,14 +453,13 @@
         return $id;
     }
 
-
     /**
      * Get an associative array for one row
      * If no query handle is specified, the last query will be taken as reference
      *
-     * @param  number $res_id Optional query handle identifier
+     * @param int $res_id Optional query handle identifier
      *
-     * @return mixed   Array with col values or false on failure
+     * @return mixed Array with col values or false on failure
      */
     public function fetch_assoc($res_id = null)
     {
@@ -424,14 +467,13 @@
         return $this->_fetch_row($result, PDO::FETCH_ASSOC);
     }
 
-
     /**
      * Get an index array for one row
      * If no query handle is specified, the last query will be taken as reference
      *
-     * @param  number $res_id  Optional query handle identifier
+     * @param int $res_id Optional query handle identifier
      *
-     * @return mixed   Array with col values or false on failure
+     * @return mixed Array with col values or false on failure
      */
     public function fetch_array($res_id = null)
     {
@@ -439,14 +481,13 @@
         return $this->_fetch_row($result, PDO::FETCH_NUM);
     }
 
-
     /**
      * Get col values for a result row
      *
-     * @param  PDOStatement $result Result handle
-     * @param  number       $mode   Fetch mode identifier
+     * @param PDOStatement $result Result handle
+     * @param int          $mode   Fetch mode identifier
      *
-     * @return mixed  Array with col values or false on failure
+     * @return mixed Array with col values or false on failure
      */
     protected function _fetch_row($result, $mode)
     {
@@ -457,10 +498,14 @@
         return $result->fetch($mode);
     }
 
-
     /**
      * Adds LIMIT,OFFSET clauses to the query
      *
+     * @param string $query  SQL query
+     * @param int    $limit  Number of rows
+     * @param int    $offset Offset
+     *
+     * @return string SQL query
      */
     protected function set_limit($query, $limit = 0, $offset = 0)
     {
@@ -474,7 +519,6 @@
 
         return $query;
     }
-
 
     /**
      * Returns list of tables in a database
@@ -498,11 +542,10 @@
         return $this->tables;
     }
 
-
     /**
      * Returns list of columns in database table
      *
-     * @param string Table name
+     * @param string $table Table name
      *
      * @return array List of table cols
      */
@@ -518,20 +561,23 @@
         return array();
     }
 
-
     /**
      * Formats input so it can be safely used in a query
      *
-     * @param  mixed  $input  Value to quote
-     * @param  string $type   Type of data
+     * @param mixed  $input Value to quote
+     * @param string $type  Type of data
      *
-     * @return string  Quoted/converted string for use in query
+     * @return string Quoted/converted string for use in query
      */
     public function quote($input, $type = null)
     {
         // handle int directly for better performance
         if ($type == 'integer' || $type == 'int') {
             return intval($input);
+        }
+
+        if (is_null($input)) {
+            return 'NULL';
         }
 
         // create DB handle if not available
@@ -551,28 +597,26 @@
         return 'NULL';
     }
 
-
     /**
      * Quotes a string so it can be safely used as a table or column name
      *
-     * @param  string $str Value to quote
+     * @param string $str Value to quote
      *
-     * @return string  Quoted string for use in query
-     * @deprecated     Replaced by rcube_db::quote_identifier
-     * @see            rcube_db::quote_identifier
+     * @return string Quoted string for use in query
+     * @deprecated    Replaced by rcube_db::quote_identifier
+     * @see           rcube_db::quote_identifier
      */
     public function quoteIdentifier($str)
     {
         return $this->quote_identifier($str);
     }
 
-
     /**
      * Quotes a string so it can be safely used as a table or column name
      *
-     * @param  string $str Value to quote
+     * @param string $str Value to quote
      *
-     * @return string  Quoted string for use in query
+     * @return string Quoted string for use in query
      */
     public function quote_identifier($str)
     {
@@ -588,7 +632,6 @@
         return  implode($name, '.');
     }
 
-
     /**
      * Return SQL function for current time and date
      *
@@ -599,12 +642,11 @@
         return "now()";
     }
 
-
     /**
      * Return list of elements for use with SQL's IN clause
      *
-     * @param  array  $arr  Input array
-     * @param  string $type Type of data
+     * @param array  $arr  Input array
+     * @param string $type Type of data
      *
      * @return string Comma-separated list of quoted values for use in query
      */
@@ -621,14 +663,13 @@
         return implode(',', $arr);
     }
 
-
     /**
      * Return SQL statement to convert a field value into a unix timestamp
      *
      * This method is deprecated and should not be used anymore due to limitations
      * of timestamp functions in Mysql (year 2038 problem)
      *
-     * @param  string $field Field name
+     * @param string $field Field name
      *
      * @return string  SQL statement to use in query
      * @deprecated
@@ -638,33 +679,30 @@
         return "UNIX_TIMESTAMP($field)";
     }
 
-
     /**
      * Return SQL statement to convert from a unix timestamp
      *
-     * @param  string $timestamp Field name
+     * @param int $timestamp Unix timestamp
      *
-     * @return string  SQL statement to use in query
+     * @return string Date string in db-specific format
      */
     public function fromunixtime($timestamp)
     {
         return date("'Y-m-d H:i:s'", $timestamp);
     }
 
-
     /**
      * Return SQL statement for case insensitive LIKE
      *
-     * @param  string $column  Field name
-     * @param  string $value   Search value
+     * @param string $column Field name
+     * @param string $value  Search value
      *
-     * @return string  SQL statement to use in query
+     * @return string SQL statement to use in query
      */
     public function ilike($column, $value)
     {
         return $this->quote_identifier($column).' LIKE '.$this->quote($value);
     }
-
 
     /**
      * Abstract SQL statement for value concatenation
@@ -681,13 +719,12 @@
         return '(' . join(' || ', $args) . ')';
     }
 
-
     /**
      * Encodes non-UTF-8 characters in string/array/object (recursive)
      *
-     * @param  mixed  $input Data to fix
+     * @param mixed $input Data to fix
      *
-     * @return mixed  Properly UTF-8 encoded data
+     * @return mixed Properly UTF-8 encoded data
      */
     public static function encode($input)
     {
@@ -707,13 +744,12 @@
         return utf8_encode($input);
     }
 
-
     /**
      * Decodes encoded UTF-8 string/object/array (recursive)
      *
-     * @param  mixed $input Input data
+     * @param mixed $input Input data
      *
-     * @return mixed  Decoded data
+     * @return mixed Decoded data
      */
     public static function decode($input)
     {
@@ -733,31 +769,28 @@
         return utf8_decode($input);
     }
 
-
     /**
      * Adds a query result and returns a handle ID
      *
-     * @param  object $res Query handle
+     * @param object $res Query handle
      *
-     * @return mixed   Handle ID
+     * @return int Handle ID
      */
     protected function _add_result($res)
     {
-        $res_id = sizeof($this->a_query_results);
-        $this->last_res_id = $res_id;
-        $this->a_query_results[$res_id] = $res;
+        $this->last_res_id = sizeof($this->a_query_results);
+        $this->a_query_results[$this->last_res_id] = $res;
 
-        return $res_id;
+        return $this->last_res_id;
     }
-
 
     /**
      * Resolves a given handle ID and returns the according query handle
      * If no ID is specified, the last resource handle will be returned
      *
-     * @param  number $res_id Handle ID
+     * @param int $res_id Handle ID
      *
-     * @return mixed   Resource handle or false on failure
+     * @return mixed Resource handle or false on failure
      */
     protected function _get_result($res_id = null)
     {
@@ -771,7 +804,6 @@
 
         return false;
     }
-
 
     /**
      * Return correct name for a specific database table
@@ -794,32 +826,12 @@
         return $table;
     }
 
-
     /**
-     * Return correct name for a specific database sequence
-     * (used for Postgres only)
+     * MDB2 DSN string parser
      *
      * @param string $sequence Secuence name
      *
-     * @return string Translated sequence name
-     */
-    public function sequence_name($sequence)
-    {
-        $rcube = rcube::get_instance();
-
-        // return sequence name if configured
-        $config_key = 'db_sequence_'.$sequence;
-
-        if ($name = $rcube->config->get($config_key)) {
-            return $name;
-        }
-
-        return $sequence;
-    }
-
-
-    /**
-     * MDB2 DSN string parser
+     * @return array DSN parameters
      */
     public static function parse_dsn($dsn)
     {
@@ -831,7 +843,8 @@
         if (($pos = strpos($dsn, '://')) !== false) {
             $str = substr($dsn, 0, $pos);
             $dsn = substr($dsn, $pos + 3);
-        } else {
+        }
+        else {
             $str = $dsn;
             $dsn = null;
         }
@@ -841,7 +854,8 @@
         if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
             $parsed['phptype']  = $arr[1];
             $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
-        } else {
+        }
+        else {
             $parsed['phptype']  = $str;
             $parsed['dbsyntax'] = $str;
         }
@@ -858,7 +872,8 @@
             if (($pos = strpos($str, ':')) !== false) {
                 $parsed['username'] = rawurldecode(substr($str, 0, $pos));
                 $parsed['password'] = rawurldecode(substr($str, $pos + 1));
-            } else {
+            }
+            else {
                 $parsed['username'] = rawurldecode($str);
             }
         }
@@ -887,9 +902,11 @@
                 $pos = strrpos($proto_opts, '/');
                 $dsn = substr($proto_opts, $pos + 1);
                 $proto_opts = substr($proto_opts, 0, $pos);
-            } elseif (strpos($dsn, '/') !== false) {
+            }
+            else if (strpos($dsn, '/') !== false) {
                 list($proto_opts, $dsn) = explode('/', $dsn, 2);
-            } else {
+            }
+            else {
                 $proto_opts = $dsn;
                 $dsn = null;
             }
@@ -921,7 +938,8 @@
                 $dsn = substr($dsn, $pos + 1);
                 if (strpos($dsn, '&') !== false) {
                     $opts = explode('&', $dsn);
-                } else { // database?param1=value1
+                }
+                else { // database?param1=value1
                     $opts = array($dsn);
                 }
                 foreach ($opts as $opt) {
@@ -938,7 +956,11 @@
     }
 
     /**
-     * Returns PDO DSN string from DSN array (parse_dsn() result)
+     * Returns PDO DSN string from DSN array
+     *
+     * @param array $dsn DSN parameters
+     *
+     * @return string DSN string
      */
     protected function dsn_string($dsn)
     {
@@ -965,7 +987,11 @@
     }
 
     /**
-     * Returns PDO driver options array from DSN array (parse_dsn() result)
+     * Returns driver-specific connection options
+     *
+     * @param array $dsn DSN parameters
+     *
+     * @return array Connection options
      */
     protected function dsn_options($dsn)
     {

--
Gitblit v1.9.1