title: includes/MysqliDb.php on line 823 error - url shortener ads 1.3_2
body: <p>
   * @author    Josh Campbell 
   * @author    Alexander V. Butenko 
   * @copyright Copyright (c) 2010
   * @license   <a href="http://opensource.org/licenses/gpl-3.0.html" rel="nofollow">http://opensource.org/licenses/gpl-3.0.html</a> GNU Public License
   * @version   2.0
   <strong>/
  class MysqliDb
  {
      /</strong>
       * Static instance of self
       *
       * @var MysqliDb
       */
      protected static $_instance;
      /**
       * Table prefix
       * 
       * @var string
       */
      protected static $_prefix;
      /**
       * MySQLi instance
       *
       * @var mysqli
       */
      protected $_mysqli;
      /**
       * The SQL query to be prepared and executed
       *
       * @var string
       */
      protected $_query;
      /**
       * The previously executed SQL query
       *
       * @var string
       */
      protected $_lastQuery;
      /**
       * An array that holds where joins
       *
       * @var array
       */
      protected $_join = array(); 
      /**
       * An array that holds where conditions 'fieldname' => 'value'
       *
       * @var array
       */
      protected $_where = array();
      /**
       * Dynamic type list for order by condition value
       */
      protected $_orderBy = array(); 
      /**
       * Dynamic type list for group by condition value
       */
      protected $_groupBy = array(); 
      /**
       * Dynamic array that holds a combination of where condition/table data value types and parameter referances
       *
       * @var array
       */
      protected $_bindParams = array(''); // Create the empty 0 index
      /**
       * Variable which holds an amount of returned rows during get/getOne/select queries
       *
       * @var string
       */ 
      public $count = 0;
      /**
       * Variable which holds last statement error
       *
       * @var string
       */
      protected $_stmtError;</p>
  
  <pre><code>/**
   * Database credentials
   *
   * @var string
   */
  protected $host;
  protected $username;
  protected $password;
  protected $db;
  protected $port;
  
  /**
   * Is Subquery object
   *
   */
  protected $isSubQuery = false;
  
  /**
   * @param string $host
   * @param string $username
   * @param string $password
   * @param string $db
   * @param int $port
   */
  public function __construct($host = NULL, $username = NULL, $password = NULL, $db = NULL, $port = NULL)
  {
      $this-&gt;host = $host;
      $this-&gt;username = $username;
      $this-&gt;password = $password;
      $this-&gt;db = $db;
      if($port == NULL)
          $this-&gt;port = ini_get ('mysqli.default_port');
      else
          $this-&gt;port = $port;
  
      if ($host == null &amp;&amp; $username == null &amp;&amp; $db == null) {
          $this-&gt;isSubQuery = true;
          return;
      }
  
      // for subqueries we do not need database connection and redefine root instance
      $this-&gt;connect();
      $this-&gt;setPrefix();
      self::$_instance = $this;
  }
  
  /**
   * A method to connect to the database
   *
   */
  public function connect()
  {
      if ($this-&gt;isSubQuery)
          return;
  
      $this-&gt;_mysqli = new mysqli ($this-&gt;host, $this-&gt;username, $this-&gt;password, $this-&gt;db, $this-&gt;port)
          or die('There was a problem connecting to the database');
  
      $this-&gt;_mysqli-&gt;set_charset ('utf8');
  }
  /**
   * A method of returning the static instance to allow access to the
   * instantiated object from within another class.
   * Inheriting this class would require reloading connection info.
   *
   * @uses $db = MySqliDb::getInstance();
   *
   * @return object Returns the current instance.
   */
  public static function getInstance()
  {
      return self::$_instance;
  }
  
  /**
   * Reset states after an execution
   *
   * @return object Returns the current instance.
   */
  protected function reset()
  {
      $this-&gt;_where = array();
      $this-&gt;_join = array();
      $this-&gt;_orderBy = array();
      $this-&gt;_groupBy = array(); 
      $this-&gt;_bindParams = array(''); // Create the empty 0 index
      $this-&gt;_query = null;
      $this-&gt;count = 0;
  }
  
  /**
   * Method to set a prefix
   * 
   * @param string $prefix     Contains a tableprefix
   */
  public function setPrefix($prefix = '')
  {
      self::$_prefix = $prefix;
      return $this;
  }
  
  /**
   * Pass in a raw query and an array containing the parameters to bind to the prepaird statement.
   *
   * @param string $query      Contains a user-provided query.
   * @param array  $bindParams All variables to bind to the SQL statment.
   * @param bool   $sanitize   If query should be filtered before execution
   *
   * @return array Contains the returned rows from the query.
   */
  public function rawQuery ($query, $bindParams = null, $sanitize = true)
  {
      $this-&gt;_query = $query;
      if ($sanitize)
          $this-&gt;_query = filter_var ($query, FILTER_SANITIZE_STRING,
                                  FILTER_FLAG_NO_ENCODE_QUOTES);
      $stmt = $this-&gt;_prepareQuery();
  
      if (is_array($bindParams) === true) {
          $params = array(''); // Create the empty 0 index
          foreach ($bindParams as $prop =&gt; $val) {
              $params[0] .= $this-&gt;_determineType($val);
              array_push($params, $bindParams[$prop]);
          }
  
          call_user_func_array(array($stmt, 'bind_param'), $this-&gt;refValues($params));
  
      }
  
      $stmt-&gt;execute();
      $this-&gt;_stmtError = $stmt-&gt;error;
      $this-&gt;reset();
  
      return $this-&gt;_dynamicBindResults($stmt);
  }
  
  /**
   *
   * @param string $query   Contains a user-provided select query.
   * @param int    $numRows The number of rows total to return.
   *
   * @return array Contains the returned rows from the query.
   */
  public function query($query, $numRows = null)
  {
      $this-&gt;_query = filter_var($query, FILTER_SANITIZE_STRING);
      $stmt = $this-&gt;_buildQuery($numRows);
      $stmt-&gt;execute();
      $this-&gt;_stmtError = $stmt-&gt;error;
      $this-&gt;reset();
  
      return $this-&gt;_dynamicBindResults($stmt);
  }
  
  /**
   * A convenient SELECT * function.
   *
   * @param string  $tableName The name of the database table to work with.
   * @param integer $numRows   The number of rows total to return.
   *
   * @return array Contains the returned rows from the select query.
   */
  public function get($tableName, $numRows = null, $columns = '*')
  {
      if (empty ($columns))
          $columns = '*';
  
      $column = is_array($columns) ? implode(', ', $columns) : $columns; 
      $this-&gt;_query = "SELECT $column FROM " .self::$_prefix . $tableName;
      $stmt = $this-&gt;_buildQuery($numRows);
  
      if ($this-&gt;isSubQuery)
          return $this;
  
      $stmt-&gt;execute();
      $this-&gt;_stmtError = $stmt-&gt;error;
      $this-&gt;reset();
  
      return $this-&gt;_dynamicBindResults($stmt);
  }
  
  /**
   * A convenient SELECT * function to get one record.
   *
   * @param string  $tableName The name of the database table to work with.
   *
   * @return array Contains the returned rows from the select query.
   */
  public function getOne($tableName, $columns = '*') 
  {
      $res = $this-&gt;get ($tableName, 1, $columns);
  
      if (is_object($res))
          return $res;
  
      if (isset($res[0]))
          return $res[0];
  
      return null;
  }
  
  /**
   *
   * @param &lt;string $tableName The name of the table.
   * @param array $insertData Data containing information for inserting into the DB.
   *
   * @return boolean Boolean indicating whether the insert query was completed succesfully.
   */
  public function insert($tableName, $insertData)
  {
      if ($this-&gt;isSubQuery)
          return;
  
      $this-&gt;_query = "INSERT into " .self::$_prefix . $tableName;
      $stmt = $this-&gt;_buildQuery(null, $insertData);
      $stmt-&gt;execute();
      $this-&gt;_stmtError = $stmt-&gt;error;
      $this-&gt;reset();
  
      return ($stmt-&gt;affected_rows &gt; 0 ? $stmt-&gt;insert_id : false);
  }
  
  /**
   * Update query. Be sure to first call the "where" method.
   *
   * @param string $tableName The name of the database table to work with.
   * @param array  $tableData Array of data to update the desired row.
   *
   * @return boolean
   */
  public function update($tableName, $tableData)
  {
      if ($this-&gt;isSubQuery)
          return;
  
      $this-&gt;_query = "UPDATE " . self::$_prefix . $tableName ." SET ";
  
      $stmt = $this-&gt;_buildQuery (null, $tableData);
      $status = $stmt-&gt;execute();
      $this-&gt;reset();
      $this-&gt;_stmtError = $stmt-&gt;error;
      $this-&gt;count = $stmt-&gt;affected_rows;
  
      return $status;
  }
  
  /**
   * Delete query. Call the "where" method first.
   *
   * @param string  $tableName The name of the database table to work with.
   * @param integer $numRows   The number of rows to delete.
   *
   * @return boolean Indicates success. 0 or 1.
   */
  public function delete($tableName, $numRows = null)
  {
      if ($this-&gt;isSubQuery)
          return;
  
      $this-&gt;_query = "DELETE FROM " . self::$_prefix . $tableName;
  
      $stmt = $this-&gt;_buildQuery($numRows);
      $stmt-&gt;execute();
      $this-&gt;_stmtError = $stmt-&gt;error;
      $this-&gt;reset();
  
      return ($stmt-&gt;affected_rows &gt; 0);
  }
  
  /**
   * This method allows you to specify multiple (method chaining optional) AND WHERE statements for SQL queries.
   *
   * @uses $MySqliDb-&gt;where('id', 7)-&gt;where('title', 'MyTitle');
   *
   * @param string $whereProp  The name of the database field.
   * @param mixed  $whereValue The value of the database field.
   *
   * @return MysqliDb
   */
  public function where($whereProp, $whereValue = null, $operator = null)
  {
      if ($operator)
          $whereValue = Array ($operator =&gt; $whereValue);
  
      $this-&gt;_where[] = Array ("AND", $whereValue, $whereProp);
      return $this;
  }
  
  /**
   * This method allows you to specify multiple (method chaining optional) OR WHERE statements for SQL queries.
   *
   * @uses $MySqliDb-&gt;orWhere('id', 7)-&gt;orWhere('title', 'MyTitle');
   *
   * @param string $whereProp  The name of the database field.
   * @param mixed  $whereValue The value of the database field.
   *
   * @return MysqliDb
   */
  public function orWhere($whereProp, $whereValue = null, $operator = null)
  {
      if ($operator)
          $whereValue = Array ($operator =&gt; $whereValue);
  
      $this-&gt;_where[] = Array ("OR", $whereValue, $whereProp);
      return $this;
  }
  /**
   * This method allows you to concatenate joins for the final SQL statement.
   *
   * @uses $MySqliDb-&gt;join('table1', 'field1 &lt;&gt; field2', 'LEFT')
   *
   * @param string $joinTable The name of the table.
   * @param string $joinCondition the condition.
   * @param string $joinType 'LEFT', 'INNER' etc.
   *
   * @return MysqliDb
   */
   public function join($joinTable, $joinCondition, $joinType = '')
   {
      $allowedTypes = array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER');
      $joinType = strtoupper (trim ($joinType));
      $joinTable = filter_var($joinTable, FILTER_SANITIZE_STRING);
  
      if ($joinType &amp;&amp; !in_array ($joinType, $allowedTypes))
          die ('Wrong JOIN type: '.$joinType);
  
      $this-&gt;_join[$joinType . " JOIN " . self::$_prefix . $joinTable] = $joinCondition;
  
      return $this;
  }
  /**
   * This method allows you to specify multiple (method chaining optional) ORDER BY statements for SQL queries.
   *
   * @uses $MySqliDb-&gt;orderBy('id', 'desc')-&gt;orderBy('name', 'desc');
   *
   * @param string $orderByField The name of the database field.
   * @param string $orderByDirection Order direction.
   *
   * @return MysqliDb
   */
  public function orderBy($orderByField, $orderbyDirection = "DESC")
  {
      $allowedDirection = Array ("ASC", "DESC");
      $orderbyDirection = strtoupper (trim ($orderbyDirection));
      $orderByField = preg_replace ("/[^-a-z0-9\.\(\),_]+/i",'', $orderByField);
  
      if (empty($orderbyDirection) || !in_array ($orderbyDirection, $allowedDirection))
          die ('Wrong order direction: '.$orderbyDirection);
  
      $this-&gt;_orderBy[$orderByField] = $orderbyDirection;
      return $this;
  } 
  
  /**
   * This method allows you to specify multiple (method chaining optional) GROUP BY statements for SQL queries.
   *
   * @uses $MySqliDb-&gt;groupBy('name');
   *
   * @param string $groupByField The name of the database field.
   *
   * @return MysqliDb
   */
  public function groupBy($groupByField)
  {
      $groupByField = preg_replace ("/[^-a-z0-9\.\(\),_]+/i",'', $groupByField);
  
      $this-&gt;_groupBy[] = $groupByField;
      return $this;
  } 
  
  /**
   * This methods returns the ID of the last inserted item
   *
   * @return integer The last inserted item ID.
   */
  public function getInsertId()
  {
      return $this-&gt;_mysqli-&gt;insert_id;
  }
  
  /**
   * Escape harmful characters which might affect a query.
   *
   * @param string $str The string to escape.
   *
   * @return string The escaped string.
   */
  public function escape($str)
  {
      return $this-&gt;_mysqli-&gt;real_escape_string($str);
  }
  
  /**
   * Method to call mysqli-&gt;ping() to keep unused connections open on
   * long-running scripts, or to reconnect timed out connections (if php.ini has
   * global mysqli.reconnect set to true). Can't do this directly using object
   * since _mysqli is protected.
   *
   * @return bool True if connection is up
   */
  public function ping() {
      return $this-&gt;_mysqli-&gt;ping();
  }
  
  /**
   * This method is needed for prepared statements. They require
   * the data type of the field to be bound with "i" s", etc.
   * This function takes the input, determines what type it is,
   * and then updates the param_type.
   *
   * @param mixed $item Input to determine the type.
   *
   * @return string The joined parameter types.
   */
  protected function _determineType($item)
  {
      switch (gettype($item)) {
          case 'NULL':
          case 'string':
              return 's';
              break;
  
          case 'boolean':
          case 'integer':
              return 'i';
              break;
  
          case 'blob':
              return 'b';
              break;
  
          case 'double':
              return 'd';
              break;
      }
      return '';
  }
  
  /**
   * Helper function to add variables into bind parameters array
   *
   * @param string Variable value
   */
  protected function _bindParam($value) {
      $this-&gt;_bindParams[0] .= $this-&gt;_determineType ($value);
      array_push ($this-&gt;_bindParams, $value);
  }
  
  /**
   * Helper function to add variables into bind parameters array in bulk
   *
   * @param Array Variable with values
   */
  protected function _bindParams ($values) {
      foreach ($values as $value)
          $this-&gt;_bindParam ($value);
  }
  
  /**
   * Helper function to add variables into bind parameters array and will return
   * its SQL part of the query according to operator in ' $operator ?' or
   * ' $operator ($subquery) ' formats
   *
   * @param Array Variable with values
   */
  protected function _buildPair ($operator, $value) {
      if (!is_object($value)) {
          $this-&gt;_bindParam ($value);
          return ' ' . $operator. ' ? ';
      }
  
      $subQuery = $value-&gt;getSubQuery ();
      $this-&gt;_bindParams ($subQuery['params']);
  
      return " " . $operator . " (" . $subQuery['query'] . ")";
  }
  
  /**
   * Abstraction method that will compile the WHERE statement,
   * any passed update data, and the desired rows.
   * It then builds the SQL query.
   *
   * @param int   $numRows   The number of rows total to return.
   * @param array $tableData Should contain an array of data for updating the database.
   *
   * @return mysqli_stmt Returns the $stmt object.
   */
  protected function _buildQuery($numRows = null, $tableData = null)
  {
      $this-&gt;_buildJoin();
      $this-&gt;_buildTableData ($tableData);
      $this-&gt;_buildWhere();
      $this-&gt;_buildGroupBy();
      $this-&gt;_buildOrderBy();
      $this-&gt;_buildLimit ($numRows);
  
      $this-&gt;_lastQuery = $this-&gt;replacePlaceHolders ($this-&gt;_query, $this-&gt;_bindParams);
  
      if ($this-&gt;isSubQuery)
          return;
  
      // Prepare query
      $stmt = $this-&gt;_prepareQuery();
  
      // Bind parameters to statement if any
      if (count ($this-&gt;_bindParams) &gt; 1)
          call_user_func_array(array($stmt, 'bind_param'), $this-&gt;refValues($this-&gt;_bindParams));
  
      return $stmt;
  }
  
  /**
   * This helper method takes care of prepared statements' "bind_result method
   * , when the number of variables to pass is unknown.
   *
   * @param mysqli_stmt $stmt Equal to the prepared statement object.
   *
   * @return array The results of the SQL fetch.
   */
  protected function _dynamicBindResults(mysqli_stmt $stmt)
  {
      $parameters = array();
      $results = array();
  
      $meta = $stmt-&gt;result_metadata();
  
      // if $meta is false yet sqlstate is true, there's no sql error but the query is
      // most likely an update/insert/delete which doesn't produce any results
      if(!$meta &amp;&amp; $stmt-&gt;sqlstate) { 
          return array();
      }
  
      $row = array();
      while ($field = $meta-&gt;fetch_field()) {
          $row[$field-&gt;name] = null;
          $parameters[] = &amp; $row[$field-&gt;name];
      }
  
      // avoid out of memory bug in php 5.2 and 5.3
      // https://github.com/joshcam/PHP-MySQLi-Database-Class/pull/119
      if (version_compare (phpversion(), '5.4', '&lt;'))
           $stmt-&gt;store_result();
  
      call_user_func_array(array($stmt, 'bind_result'), $parameters);
  
      while ($stmt-&gt;fetch()) {
          $x = array();
          foreach ($row as $key =&gt; $val) {
              $x[$key] = $val;
          }
          $this-&gt;count++;
          array_push($results, $x);
      }
  
      return $results;
  }
  
  
  /**
   * Abstraction method that will build an JOIN part of the query
   */
  protected function _buildJoin () {
      if (empty ($this-&gt;_join))
          return;
  
      foreach ($this-&gt;_join as $prop =&gt; $value)
          $this-&gt;_query .= " " . $prop . " on " . $value;
  }
  
  /**
   * Abstraction method that will build an INSERT or UPDATE part of the query
   */
  protected function _buildTableData ($tableData) {
      if (!is_array ($tableData))
          return;
  
      $isInsert = strpos ($this-&gt;_query, 'INSERT');
      $isUpdate = strpos ($this-&gt;_query, 'UPDATE');
  
      if ($isInsert !== false) {
          $this-&gt;_query .= '(`' . implode(array_keys($tableData), '`, `') . '`)';
          $this-&gt;_query .= ' VALUES(';
      }
  
      foreach ($tableData as $column =&gt; $value) {
          if ($isUpdate !== false)
              $this-&gt;_query .= "`" . $column . "` = ";
  
          // Subquery value
          if (is_object ($value)) {
              $this-&gt;_query .= $this-&gt;_buildPair ("", $value) . ", ";
              continue;
          }
  
          // Simple value
          if (!is_array ($value)) {
              $this-&gt;_bindParam ($value);
              $this-&gt;_query .= '?, ';
              continue;
          }
  
          // Function value
          $key = key ($value);
          $val = $value[$key];
          switch ($key) {
              case '[I]':
                  $this-&gt;_query .= $column . $val . ", ";
                  break;
              case '[F]':
                  $this-&gt;_query .= $val[0] . ", ";
                  if (!empty ($val[1]))
                      $this-&gt;_bindParams ($val[1]);
                  break;
              case '[N]':
                  if ($val == null)
                      $this-&gt;_query .= "!" . $column . ", ";
                  else
                      $this-&gt;_query .= "!" . $val . ", ";
                  break;
              default:
                  die ("Wrong operation");
          }
      }
      $this-&gt;_query = rtrim($this-&gt;_query, ', ');
      if ($isInsert !== false)
          $this-&gt;_query .= ')';
  }
  
  /**
   * Abstraction method that will build the part of the WHERE conditions
   */
  protected function _buildWhere () {
      if (empty ($this-&gt;_where))
          return;
  
      //Prepair the where portion of the query
      $this-&gt;_query .= ' WHERE ';
  
      // Remove first AND/OR concatenator
      $this-&gt;_where[0][0] = '';
      foreach ($this-&gt;_where as $cond) {
          list ($concat, $wValue, $wKey) = $cond;
  
          $this-&gt;_query .= " " . $concat ." " . $wKey;
  
          // Empty value (raw where condition in wKey)
          if ($wValue === null)
              continue;
  
          // Simple = comparison
          if (!is_array ($wValue))
              $wValue = Array ('=' =&gt; $wValue);
  
          $key = key ($wValue);
          $val = $wValue[$key];
          switch (strtolower ($key)) {
              case '0':
                  $this-&gt;_bindParams ($wValue);
                  break;
              case 'not in':
              case 'in':
                  $comparison = ' ' . $key . ' (';
                  if (is_object ($val)) {
                      $comparison .= $this-&gt;_buildPair ("", $val);
                  } else {
                      foreach ($val as $v) {
                          $comparison .= ' ?,';
                          $this-&gt;_bindParam ($v);
                      }
                  }
                  $this-&gt;_query .= rtrim($comparison, ',').' ) ';
                  break;
              case 'not between':
              case 'between':
                  $this-&gt;_query .= " $key ? AND ? ";
                  $this-&gt;_bindParams ($val);
                  break;
              default:
                  $this-&gt;_query .= $this-&gt;_buildPair ($key, $val);
          }
      }
  }
  
  /**
   * Abstraction method that will build the GROUP BY part of the WHERE statement
   *
   */
  protected function _buildGroupBy () {
      if (empty ($this-&gt;_groupBy))
          return;
  
      $this-&gt;_query .= " GROUP BY ";
      foreach ($this-&gt;_groupBy as $key =&gt; $value)
          $this-&gt;_query .= $value . ", ";
  
      $this-&gt;_query = rtrim($this-&gt;_query, ', ') . " ";
  }
  
  /**
   * Abstraction method that will build the LIMIT part of the WHERE statement
   *
   * @param int   $numRows   The number of rows total to return.
   */
  protected function _buildOrderBy () {
      if (empty ($this-&gt;_orderBy))
          return;
  
      $this-&gt;_query .= " ORDER BY ";
      foreach ($this-&gt;_orderBy as $prop =&gt; $value)
          $this-&gt;_query .= $prop . " " . $value . ", ";
  
      $this-&gt;_query = rtrim ($this-&gt;_query, ', ') . " ";
  }
  
  /**
   * Abstraction method that will build the LIMIT part of the WHERE statement
   *
   * @param int   $numRows   The number of rows total to return.
   */
  protected function _buildLimit ($numRows) {
      if (!isset ($numRows))
          return;
  
      if (is_array ($numRows))
          $this-&gt;_query .= ' LIMIT ' . (int)$numRows[0] . ', ' . (int)$numRows[1];
      else
          $this-&gt;_query .= ' LIMIT ' . (int)$numRows;
  }
  
  /**
   * Method attempts to prepare the SQL query
   * and throws an error if there was a problem.
   *
   * @return mysqli_stmt
   */
  protected function _prepareQuery()
  {
      if (!$stmt = $this-&gt;_mysqli-&gt;prepare($this-&gt;_query)) {
          trigger_error("Problem preparing query ($this-&gt;_query) " . $this-&gt;_mysqli-&gt;error, E_USER_ERROR);
      }
      return $stmt;
  }
  
  /**
   * Close connection
   */
  public function __destruct()
  {
      if (!$this-&gt;isSubQuery)
          return;
      if ($this-&gt;_mysqli)
          $this-&gt;_mysqli-&gt;close();
  }
  
  /**
   * @param array $arr
   *
   * @return array
   */
  protected function refValues($arr)
  {
      //Reference is required for PHP 5.3+
      if (strnatcmp(phpversion(), '5.3') &gt;= 0) {
          $refs = array();
          foreach ($arr as $key =&gt; $value) {
              $refs[$key] = &amp; $arr[$key];
          }
          return $refs;
      }
      return $arr;
  }
  
  /**
   * Function to replace ? with variables from bind variable
   * @param string $str
   * @param Array $vals
   *
   * @return string
   */
  protected function replacePlaceHolders ($str, $vals) {
      $i = 1;
      $newStr = "";
  
      while ($pos = strpos ($str, "?")) {
          $val = $vals[$i++];
          if (is_object ($val))
              $val = '[object]';
          $newStr .= substr ($str, 0, $pos) . $val;
          $str = substr ($str, $pos + 1);
      }
      return $newStr;
  }
  
  /**
   * Method returns last executed query
   *
   * @return string
   */
  public function getLastQuery () {
      return $this-&gt;_lastQuery;
  }
  
  /**
   * Method returns mysql error
   * 
   * @return string
   */
  public function getLastError () {
      return $this-&gt;_stmtError . " " . $this-&gt;_mysqli-&gt;error;
  }
  
  /**
   * Mostly internal method to get query and its params out of subquery object
   * after get() and getAll()
   * 
   * @return array
   */
  public function getSubQuery () {
      if (!$this-&gt;isSubQuery)
          return null;
  
      array_shift ($this-&gt;_bindParams);
      $val = Array ('query' =&gt; $this-&gt;_query,
                    'params' =&gt; $this-&gt;_bindParams
              );
      $this-&gt;reset();
      return $val;
  }
  
  /* Helper functions */
  /**
   * Method returns generated interval function as a string
   *
   * @param string interval in the formats:
   *        "1", "-1d" or "- 1 day" -- For interval - 1 day
   *        Supported intervals [s]econd, [m]inute, [h]hour, [d]day, [M]onth, [Y]ear
   *        Default null;
   * @param string Initial date
   *
   * @return string
  */
  public function interval ($diff, $func = "NOW()") {
      $types = Array ("s" =&gt; "second", "m" =&gt; "minute", "h" =&gt; "hour", "d" =&gt; "day", "M" =&gt; "month", "Y" =&gt; "year");
      $incr = '+';
      $items = '';
      $type = 'd';
  
      if ($diff &amp;&amp; preg_match('/([+-]?) ?([0-9]+) ?([a-zA-Z]?)/',$diff, $matches)) {
          if (!empty ($matches[1])) $incr = $matches[1];
          if (!empty ($matches[2])) $items = $matches[2];
          if (!empty ($matches[3])) $type = $matches[3];
          if (!in_array($type, array_keys($types)))
              trigger_error ("invalid interval type in '{$diff}'");
          $func .= " ".$incr ." interval ". $items ." ".$types[$type] . " ";
      }
      return $func;
  
  }
  /**
   * Method returns generated interval function as an insert/update function
   *
   * @param string interval in the formats:
   *        "1", "-1d" or "- 1 day" -- For interval - 1 day
   *        Supported intervals [s]econd, [m]inute, [h]hour, [d]day, 
  </code></pre>