q = &$p; // save statement $this->st = &$st; // determine what fields we have (to emulate get_result()) $md = $this->st->result_metadata(); $this->fields = array(); if($md != FALSE) { while($field = $md->fetch_field()) { $this->fields[] = $field->name; } $md->free(); } } // destructor public function __destruct() { @$this->st->free_result(); } // number of rows public function NumRows() { return $this->st->num_rows(); } // affected rows public function AffectedRows() { return $this->st->affected_rows; } // insert id public function InsertId() { return $this->st->insert_id; } // seek public function Seek($offset) { $this->st->data_seek($offset); } // fetch public function FetchRow($how = DBResult::FT_BOTH) { $n = count($this->fields); // array of values and pointers to values $a = array_fill(0, $n, NULL); $p = array(); for($i = 0; $i < $n; $i++) $p[] = &$a[$i]; // bind it call_user_func_array(array($this->st, 'bind_result'), $p); // it there is no more rows, bail out if($this->st->fetch() == FALSE) return NULL; $r = array(); if(($how & (DBResult::FT_NUM)) != 0) $r = array_merge($r, $a); if(($how & (DBResult::FT_ASSOC)) != 0) $r = array_merge($r, array_combine($this->fields, $a)); return $r; } } // Query interface class DBQuery_mysqli extends DBQuery { // mysqli private $if; // mysqli_stmt private $stmt; // parameters array private $params; // constructor public function __construct(&$if, $query, $limit, $start) { // find all parameters if(preg_match_all('#\?\(([A-Za-z0-9_]+)\)#', $query, $matches) == 0) { $this->params = array(); } else { // fill parameters array foreach($matches[1] as $m) { if(ctype_digit(substr($m, 0, 1))) throw new Exception("Query parameter names cannot start with digit!"); $this->params[] = array( 'name' => $m, 'value' => NULL, 'type' => NULL ); } // replace all with mysqli prepared statements $query = preg_replace('#\?\([A-Za-z0-9_]+\)#', '?', $query); } // add limit if(!is_null($limit)) { $query .= ' LIMIT '; if(!is_null($start)) $query .= $start . ','; $query .= $limit; } // actually construct query $this->if = &$if; $this->stmt = $if->prepare($query); } // destructor public function __destruct() { @$this->stmt->close(); } // bind parameter private function RealBindParameter($id, $value, $type) { // check type switch($type) { case DBQuery::PT_INTEGER: settype($value, 'int'); break; case DBQuery::PT_BOOLEAN: settype($value, 'bool'); break; case DBQuery::PT_FLOAT: settype($value, 'float'); break; case DBQuery::PT_STRING: settype($value, 'string'); break; // auto-determine case DBQuery::PT_AUTO: default: if(is_array($value) || is_object($value)) { throw new Exception("Parameter value cannot be an array or an object!"); } if(is_string($value)) $type = DBQuery::PT_STRING; else $type = DBQuery::PT_INTEGER; } $this->params[$id]['type'] = $type; $this->params[$id]['value'] = $value; } // bind parameter interface public function SetParameter($name, $value, $type = DBQuery::PT_AUTO) { //if id suppled if(is_int($name)) { if(!isset($this->params[$name])) throw new Exception("Parameter #" . $name . " is not defined in the query"); return $this->RealBindParameter($name, $value, $type); } //else check if its name is defined foreach($this->params as $id=>&$param) { if(strcmp($name, $param['name']) == 0) { return $this->RealBindParameter($id, $value, $type); } } throw new Exception("Parameter " . $name . " is not defined in the query"); } // execute query public function Execute() { // reset (if called more than once) $this->stmt->reset(); // bind parameters if(count($this->params) > 0) { $qs = ''; $qa = array(); foreach($this->params as &$param) { if($param['type'] === NULL) { throw new Exception("Unset parameter " . $param['name'] . " while executing the query!"); } switch($param['type']) { case DBQuery::PT_INTEGER: $qs .= 'i'; break; case DBQuery::PT_BOOLEAN: $qs .= 'b'; break; case DBQuery::PT_FLOAT: $qs .= 'd'; break; case DBQuery::PT_STRING: $qs .= 's'; break; } $qa[] = &$param['value']; } call_user_func_array(array($this->stmt, 'bind_param'), array_merge(array($qs), $qa)); } // execute it! if($this->stmt->execute() == FALSE) { throw new Exception('Database query error mysqli #' . $this->stmt->errno . ': ' . $this->stmt->error); } // store result $this->stmt->store_result(); // return result abstraction return new DBResult_mysqli($this->if, $this->stmt, $this); } } // Database interface class DB_mysqli extends DB { // interface private $mysqli; // constructor public function __construct($host, $user, $pass, $db) { $this->mysqli = mysqli_init(); if(defined('DB_CHARSET')) { // WARNING! MYSQLI_SET_CHARSET_NAME is UNDOCUMENTED in PHP API, but legal in C API. $this->mysqli->options(MYSQLI_SET_CHARSET_NAME, DB_CHARSET); } if(!$this->mysqli->real_connect($host, $user, $pass, $db)) { throw new Exception('Database connect error mysqli #' . $this->mysqli->connect_errno . ': ' . $this->mysqli->connect_error); } } // destructor public function __destruct() { @$this->mysqli->close(); } // query factory public function ConstructQuery($query, $limit, $start) { return new DBQuery_mysqli($this->mysqli, $query, $limit, $start); } } ?>