PageRenderTime 65ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 0ms

/fuel_intro/fuel/core/classes/database/mysql/connection.php

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