Initial Kohana install
[speedfreak] / Server / system / libraries / drivers / Database / Mysqli.php
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  */
12 class 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  */
119 class 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  */
312 class 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 }