1<?php defined('SYSPATH') OR die('No direct access allowed.'); 2/** 3 * MySQLi Database Driver 4 * 5 * $Id: Mysqli.php 4344 2009-05-11 16:41:39Z zombor $ 6 * 7 * @package Core 8 * @author Kohana Team 9 * @copyright (c) 2007-2008 Kohana Team 10 * @license http://kohanaphp.com/license.html 11 */ 12class Database_Mysqli_Driver extends Database_Mysql_Driver { 13 14 // Database connection link 15 protected $link; 16 protected $db_config; 17 protected $statements = array(); 18 19 /** 20 * Sets the config for the class. 21 * 22 * @param array database configuration 23 */ 24 public function __construct($config) 25 { 26 $this->db_config = $config; 27 28 Kohana::log('debug', 'MySQLi Database Driver Initialized'); 29 } 30 31 /** 32 * Closes the database connection. 33 */ 34 public function __destruct() 35 { 36 is_object($this->link) and $this->link->close(); 37 } 38 39 public function connect() 40 { 41 // Check if link already exists 42 if (is_object($this->link)) 43 return $this->link; 44 45 // Import the connect variables 46 extract($this->db_config['connection']); 47 48 // Build the connection info 49 $host = isset($host) ? $host : $socket; 50 51 // Make the connection and select the database 52 if ($this->link = new mysqli($host, $user, $pass, $database, $port)) 53 { 54 if ($charset = $this->db_config['character_set']) 55 { 56 $this->set_charset($charset); 57 } 58 59 // Clear password after successful connect 60 $this->db_config['connection']['pass'] = NULL; 61 62 return $this->link; 63 } 64 65 return FALSE; 66 } 67 68 public function query($sql) 69 { 70 // Only cache if it's turned on, and only cache if it's not a write statement 71 if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET|DELETE|TRUNCATE)\b#i', $sql)) 72 { 73 $hash = $this->query_hash($sql); 74 75 if ( ! isset($this->query_cache[$hash])) 76 { 77 // Set the cached object 78 $this->query_cache[$hash] = new Kohana_Mysqli_Result($this->link, $this->db_config['object'], $sql); 79 } 80 else 81 { 82 // Rewind cached result 83 $this->query_cache[$hash]->rewind(); 84 } 85 86 // Return the cached query 87 return $this->query_cache[$hash]; 88 } 89 90 return new Kohana_Mysqli_Result($this->link, $this->db_config['object'], $sql); 91 } 92 93 public function set_charset($charset) 94 { 95 if ($this->link->set_charset($charset) === FALSE) 96 throw new Kohana_Database_Exception('database.error', $this->show_error()); 97 } 98 99 public function escape_str($str) 100 { 101 if (!$this->db_config['escape']) 102 return $str; 103 104 is_object($this->link) or $this->connect(); 105 106 return $this->link->real_escape_string($str); 107 } 108 109 public function show_error() 110 { 111 return $this->link->error; 112 } 113 114} // End Database_Mysqli_Driver Class 115 116/** 117 * MySQLi Result 118 */ 119class Kohana_Mysqli_Result extends Database_Result { 120 121 // Database connection 122 protected $link; 123 124 // Data fetching types 125 protected $fetch_type = 'mysqli_fetch_object'; 126 protected $return_type = MYSQLI_ASSOC; 127 128 /** 129 * Sets up the result variables. 130 * 131 * @param object database link 132 * @param boolean return objects or arrays 133 * @param string SQL query that was run 134 */ 135 public function __construct($link, $object = TRUE, $sql) 136 { 137 $this->link = $link; 138 139 if ( ! $this->link->multi_query($sql)) 140 { 141 // SQL error 142 throw new Kohana_Database_Exception('database.error', $this->link->error.' - '.$sql); 143 } 144 else 145 { 146 $this->result = $this->link->store_result(); 147 148 // If the query is an object, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query 149 if (is_object($this->result)) 150 { 151 $this->current_row = 0; 152 $this->total_rows = $this->result->num_rows; 153 $this->fetch_type = ($object === TRUE) ? 'fetch_object' : 'fetch_array'; 154 } 155 elseif ($this->link->error) 156 { 157 // SQL error 158 throw new Kohana_Database_Exception('database.error', $this->link->error.' - '.$sql); 159 } 160 else 161 { 162 // Its an DELETE, INSERT, REPLACE, or UPDATE query 163 $this->insert_id = $this->link->insert_id; 164 $this->total_rows = $this->link->affected_rows; 165 } 166 } 167 168 // Set result type 169 $this->result($object); 170 171 // Store the SQL 172 $this->sql = $sql; 173 } 174 175 /** 176 * Magic __destruct function, frees the result. 177 */ 178 public function __destruct() 179 { 180 if (is_object($this->result)) 181 { 182 $this->result->free_result(); 183 184 // this is kinda useless, but needs to be done to avoid the "Commands out of sync; you 185 // can't run this command now" error. Basically, we get all results after the first one 186 // (the one we actually need) and free them. 187 if (is_resource($this->link) AND $this->link->more_results()) 188 { 189 do 190 { 191 if ($result = $this->link->store_result()) 192 { 193 $result->free_result(); 194 } 195 } while ($this->link->next_result()); 196 } 197 } 198 } 199 200 public function result($object = TRUE, $type = MYSQLI_ASSOC) 201 { 202 $this->fetch_type = ((bool) $object) ? 'fetch_object' : 'fetch_array'; 203 204 // This check has to be outside the previous statement, because we do not 205 // know the state of fetch_type when $object = NULL 206 // NOTE - The class set by $type must be defined before fetching the result, 207 // autoloading is disabled to save a lot of stupid overhead. 208 if ($this->fetch_type == 'fetch_object') 209 { 210 $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; 211 } 212 else 213 { 214 $this->return_type = $type; 215 } 216 217 return $this; 218 } 219 220 public function as_array($object = NULL, $type = MYSQLI_ASSOC) 221 { 222 return $this->result_array($object, $type); 223 } 224 225 public function result_array($object = NULL, $type = MYSQLI_ASSOC) 226 { 227 $rows = array(); 228 229 if (is_string($object)) 230 { 231 $fetch = $object; 232 } 233 elseif (is_bool($object)) 234 { 235 if ($object === TRUE) 236 { 237 $fetch = 'fetch_object'; 238 239 // NOTE - The class set by $type must be defined before fetching the result, 240 // autoloading is disabled to save a lot of stupid overhead. 241 $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; 242 } 243 else 244 { 245 $fetch = 'fetch_array'; 246 } 247 } 248 else 249 { 250 // Use the default config values 251 $fetch = $this->fetch_type; 252 253 if ($fetch == 'fetch_object') 254 { 255 $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; 256 } 257 } 258 259 if ($this->result->num_rows) 260 { 261 // Reset the pointer location to make sure things work properly 262 $this->result->data_seek(0); 263 264 while ($row = $this->result->$fetch($type)) 265 { 266 $rows[] = $row; 267 } 268 } 269 270 return isset($rows) ? $rows : array(); 271 } 272 273 public function list_fields() 274 { 275 $field_names = array(); 276 while ($field = $this->result->fetch_field()) 277 { 278 $field_names[] = $field->name; 279 } 280 281 return $field_names; 282 } 283 284 public function seek($offset) 285 { 286 if ($this->offsetExists($offset) AND $this->result->data_seek($offset)) 287 { 288 // Set the current row to the offset 289 $this->current_row = $offset; 290 291 return TRUE; 292 } 293 294 return FALSE; 295 } 296 297 public function offsetGet($offset) 298 { 299 if ( ! $this->seek($offset)) 300 return FALSE; 301 302 // Return the row 303 $fetch = $this->fetch_type; 304 return $this->result->$fetch($this->return_type); 305 } 306 307} // End Mysqli_Result Class 308 309/** 310 * MySQLi Prepared Statement (experimental) 311 */ 312class Kohana_Mysqli_Statement { 313 314 protected $link = NULL; 315 protected $stmt; 316 protected $var_names = array(); 317 protected $var_values = array(); 318 319 public function __construct($sql, $link) 320 { 321 $this->link = $link; 322 323 $this->stmt = $this->link->prepare($sql); 324 325 return $this; 326 } 327 328 public function __destruct() 329 { 330 $this->stmt->close(); 331 } 332 333 // Sets the bind parameters 334 public function bind_params($param_types, $params) 335 { 336 $this->var_names = array_keys($params); 337 $this->var_values = array_values($params); 338 call_user_func_array(array($this->stmt, 'bind_param'), array_merge($param_types, $var_names)); 339 340 return $this; 341 } 342 343 public function bind_result($params) 344 { 345 call_user_func_array(array($this->stmt, 'bind_result'), $params); 346 } 347 348 // Runs the statement 349 public function execute() 350 { 351 foreach ($this->var_names as $key => $name) 352 { 353 $$name = $this->var_values[$key]; 354 } 355 $this->stmt->execute(); 356 return $this->stmt; 357 } 358} 359