PageRenderTime 50ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/database/mysqli/connection.php

https://github.com/jrdoane/core
PHP | 434 lines | 316 code | 62 blank | 56 comment | 31 complexity | 47c5bc343e97981e794f41dd34b255ab MD5 | raw file
  1. <?php
  2. /**
  3. * MySQLi database connection.
  4. *
  5. * @package Fuel/Database
  6. * @category Drivers
  7. * @author Kohana Team
  8. * @copyright (c) 2008-2009 Kohana Team
  9. * @license http://kohanaphp.com/license
  10. */
  11. namespace Fuel\Core;
  12. class Database_MySQLi_Connection extends \Database_Connection {
  13. // Database in use by each connection
  14. protected static $_current_databases = array();
  15. // Use SET NAMES to set the character set
  16. protected static $_set_names;
  17. // Identifier for this connection within the PHP driver
  18. protected $_connection_id;
  19. // MySQL uses a backtick for identifiers
  20. protected $_identifier = '`';
  21. // Allows transactions
  22. protected $_trans_enabled = FALSE;
  23. // transaction errors
  24. public $trans_errors = FALSE;
  25. // Know which kind of DB is used
  26. public $_db_type = 'mysql';
  27. public function connect()
  28. {
  29. if ($this->_connection)
  30. return;
  31. if (static::$_set_names === NULL)
  32. {
  33. // Determine if we can use mysqli_set_charset(), which is only
  34. // available on PHP 5.2.3+ when compiled against MySQL 5.0+
  35. static::$_set_names = ! function_exists('mysqli_set_charset');
  36. }
  37. // Extract the connection parameters, adding required variables
  38. extract($this->_config['connection'] + array(
  39. 'database' => '',
  40. 'hostname' => '',
  41. 'port' => '',
  42. 'socket' => '',
  43. 'username' => '',
  44. 'password' => '',
  45. 'persistent' => FALSE,
  46. ));
  47. // Prevent this information from showing up in traces
  48. unset($this->_config['connection']['username'], $this->_config['connection']['password']);
  49. try
  50. {
  51. if ($socket != '')
  52. {
  53. $port = null;
  54. }
  55. elseif ($port != '')
  56. {
  57. $socket = null;
  58. }
  59. else
  60. {
  61. $socket = null;
  62. $port = null;
  63. }
  64. if ($persistent)
  65. {
  66. // Create a persistent connection
  67. $this->_connection = new \mysqli('p:'.$hostname, $username, $password, $database, $port, $socket);
  68. }
  69. else
  70. {
  71. // Create a connection and force it to be a new link
  72. $this->_connection = new \mysqli($hostname, $username, $password, $database, $port, $socket);
  73. }
  74. if ($this->_connection->error)
  75. {
  76. // Unable to connect, select database, etc
  77. throw new \Database_Exception($this->_connection->error, $this->_connection->errno);
  78. }
  79. }
  80. catch (ErrorException $e)
  81. {
  82. // No connection exists
  83. $this->_connection = NULL;
  84. throw new \Database_Exception('No MySQLi Connection', 0);
  85. }
  86. // \xFF is a better delimiter, but the PHP driver uses underscore
  87. $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password);
  88. if ( ! empty($this->_config['charset']))
  89. {
  90. // Set the character set
  91. $this->set_charset($this->_config['charset']);
  92. }
  93. static::$_current_databases[$this->_connection_id] = $database;
  94. }
  95. /**
  96. * Select the database
  97. *
  98. * @param string Database
  99. * @return void
  100. */
  101. protected function _select_db($database)
  102. {
  103. if ($this->_config['connection']['database'] !== static::$_current_databases[$this->_connection_id])
  104. {
  105. if ($this->_connection->select_db($database) !== true)
  106. {
  107. // Unable to select database
  108. throw new \Database_Exception($this->_connection->error, $this->_connection->errno);
  109. }
  110. }
  111. static::$_current_databases[$this->_connection_id] = $database;
  112. }
  113. public function disconnect()
  114. {
  115. try
  116. {
  117. // Database is assumed disconnected
  118. $status = TRUE;
  119. if ($this->_connection instanceof MySQLi)
  120. {
  121. $status = $this->_connection->close();
  122. }
  123. }
  124. catch (Exception $e)
  125. {
  126. // Database is probably not disconnected
  127. $status = !($this->_connection instanceof MySQLi);
  128. }
  129. return $status;
  130. }
  131. public function set_charset($charset)
  132. {
  133. // Make sure the database is connected
  134. $this->_connection or $this->connect();
  135. $status = $this->_connection->set_charset($charset);
  136. if ($status === FALSE)
  137. {
  138. throw new \Database_Exception($this->_connection->error, $this->_connection->errno);
  139. }
  140. }
  141. public function query($type, $sql, $as_object)
  142. {
  143. // Make sure the database is connected
  144. $this->_connection or $this->connect();
  145. if ( ! empty($this->_config['profiling']))
  146. {
  147. // Benchmark this query for the current instance
  148. $benchmark = Profiler::start("Database ({$this->_instance})", $sql);
  149. }
  150. if ( ! empty($this->_config['connection']['persistent']) AND $this->_config['connection']['database'] !== static::$_current_databases[$this->_connection_id])
  151. {
  152. // Select database on persistent connections
  153. $this->_select_db($this->_config['connection']['database']);
  154. }
  155. // Execute the query
  156. if (($result = $this->_connection->query($sql)) === FALSE)
  157. {
  158. if (isset($benchmark))
  159. {
  160. // This benchmark is worthless
  161. Profiler::delete($benchmark);
  162. }
  163. if ($type !== \DB::SELECT && $this->_trans_enabled)
  164. {
  165. // If we are using transactions, throwing an exception would defeat the purpose
  166. // We need to log the failures for transaction status
  167. if ( ! is_array($this->trans_errors))
  168. {
  169. $this->trans_errors = array();
  170. }
  171. $this->trans_errors[] = $this->_connection->errno.': '.$this->_connection->error.' [ '.$sql.' ]';
  172. }
  173. else
  174. {
  175. throw new \Database_Exception($this->_connection->error.' [ '.$sql.' ]', $this->_connection->errno);
  176. }
  177. }
  178. if (isset($benchmark))
  179. {
  180. Profiler::stop($benchmark);
  181. }
  182. // Set the last query
  183. $this->last_query = $sql;
  184. if ($type === \DB::SELECT)
  185. {
  186. // Return an iterator of results
  187. return new \Database_MySQLi_Result($result, $sql, $as_object);
  188. }
  189. elseif ($type === \DB::INSERT)
  190. {
  191. // Return a list of insert id and rows created
  192. return array(
  193. $this->_connection->insert_id,
  194. $this->_connection->affected_rows,
  195. );
  196. }
  197. else
  198. {
  199. // Return the number of rows affected
  200. return $this->_connection->affected_rows;
  201. }
  202. }
  203. public function datatype($type)
  204. {
  205. static $types = array
  206. (
  207. 'blob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '65535'),
  208. 'bool' => array('type' => 'bool'),
  209. 'bigint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '18446744073709551615'),
  210. 'datetime' => array('type' => 'string'),
  211. 'decimal unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
  212. 'double' => array('type' => 'float'),
  213. 'double precision unsigned' => array('type' => 'float', 'min' => '0'),
  214. 'double unsigned' => array('type' => 'float', 'min' => '0'),
  215. 'enum' => array('type' => 'string'),
  216. 'fixed' => array('type' => 'float', 'exact' => TRUE),
  217. 'fixed unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
  218. 'float unsigned' => array('type' => 'float', 'min' => '0'),
  219. 'int unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
  220. 'integer unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
  221. 'longblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '4294967295'),
  222. 'longtext' => array('type' => 'string', 'character_maximum_length' => '4294967295'),
  223. 'mediumblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '16777215'),
  224. 'mediumint' => array('type' => 'int', 'min' => '-8388608', 'max' => '8388607'),
  225. 'mediumint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '16777215'),
  226. 'mediumtext' => array('type' => 'string', 'character_maximum_length' => '16777215'),
  227. 'national varchar' => array('type' => 'string'),
  228. 'numeric unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
  229. 'nvarchar' => array('type' => 'string'),
  230. 'point' => array('type' => 'string', 'binary' => TRUE),
  231. 'real unsigned' => array('type' => 'float', 'min' => '0'),
  232. 'set' => array('type' => 'string'),
  233. 'smallint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '65535'),
  234. 'text' => array('type' => 'string', 'character_maximum_length' => '65535'),
  235. 'tinyblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '255'),
  236. 'tinyint' => array('type' => 'int', 'min' => '-128', 'max' => '127'),
  237. 'tinyint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '255'),
  238. 'tinytext' => array('type' => 'string', 'character_maximum_length' => '255'),
  239. 'year' => array('type' => 'string'),
  240. );
  241. $type = str_replace(' zerofill', '', $type);
  242. if (isset($types[$type]))
  243. return $types[$type];
  244. return parent::datatype($type);
  245. }
  246. public function list_tables($like = NULL)
  247. {
  248. if (is_string($like))
  249. {
  250. // Search for table names
  251. $result = $this->query(\DB::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE);
  252. }
  253. else
  254. {
  255. // Find all table names
  256. $result = $this->query(\DB::SELECT, 'SHOW TABLES', FALSE);
  257. }
  258. $tables = array();
  259. foreach ($result as $row)
  260. {
  261. $tables[] = reset($row);
  262. }
  263. return $tables;
  264. }
  265. public function list_columns($table, $like = NULL)
  266. {
  267. // Quote the table name
  268. $table = $this->quote_table($table);
  269. if (is_string($like))
  270. {
  271. // Search for column names
  272. $result = $this->query(\DB::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE);
  273. }
  274. else
  275. {
  276. // Find all column names
  277. $result = $this->query(\DB::SELECT, 'SHOW FULL COLUMNS FROM '.$table, FALSE);
  278. }
  279. $count = 0;
  280. $columns = array();
  281. foreach ($result as $row)
  282. {
  283. list($type, $length) = $this->_parse_type($row['Type']);
  284. $column = $this->datatype($type);
  285. $column['name'] = $row['Field'];
  286. $column['default'] = $row['Default'];
  287. $column['data_type'] = $type;
  288. $column['null'] = ($row['Null'] == 'YES');
  289. $column['ordinal_position'] = ++$count;
  290. switch ($column['type'])
  291. {
  292. case 'float':
  293. if (isset($length))
  294. {
  295. list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
  296. }
  297. break;
  298. case 'int':
  299. if (isset($length))
  300. {
  301. // MySQL attribute
  302. $column['display'] = $length;
  303. }
  304. break;
  305. case 'string':
  306. switch ($column['data_type'])
  307. {
  308. case 'binary':
  309. case 'varbinary':
  310. $column['character_maximum_length'] = $length;
  311. break;
  312. case 'char':
  313. case 'varchar':
  314. $column['character_maximum_length'] = $length;
  315. case 'text':
  316. case 'tinytext':
  317. case 'mediumtext':
  318. case 'longtext':
  319. $column['collation_name'] = $row['Collation'];
  320. break;
  321. case 'enum':
  322. case 'set':
  323. $column['collation_name'] = $row['Collation'];
  324. $column['options'] = explode('\',\'', substr($length, 1, -1));
  325. break;
  326. }
  327. break;
  328. }
  329. // MySQL attributes
  330. $column['comment'] = $row['Comment'];
  331. $column['extra'] = $row['Extra'];
  332. $column['key'] = $row['Key'];
  333. $column['privileges'] = $row['Privileges'];
  334. $columns[$row['Field']] = $column;
  335. }
  336. return $columns;
  337. }
  338. public function escape($value)
  339. {
  340. // Make sure the database is connected
  341. $this->_connection or $this->connect();
  342. if (($value = $this->_connection->real_escape_string((string) $value)) === FALSE)
  343. {
  344. throw new \Database_Exception($this->_connection->error, $this->_connection->errno);
  345. }
  346. // SQL standard is to use single-quotes for all values
  347. return "'$value'";
  348. }
  349. public function transactional($use_trans = true)
  350. {
  351. if (is_bool($use_trans))
  352. {
  353. $this->_trans_enabled = $use_trans;
  354. }
  355. }
  356. public function start_transaction()
  357. {
  358. $this->transactional();
  359. $this->query(0, 'SET AUTOCOMMIT=0', false);
  360. $this->query(0, 'START TRANSACTION', false);
  361. }
  362. public function commit_transaction()
  363. {
  364. $this->query(0, 'COMMIT', false);
  365. $this->query(0, 'SET AUTOCOMMIT=1', false);
  366. }
  367. public function rollback_transaction()
  368. {
  369. $this->query(0, 'ROLLBACK', false);
  370. $this->query(0, 'SET AUTOCOMMIT=1', false);
  371. }
  372. } // End Database_MySQLi