PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/system/libraries/drivers/Database/Mysql.php

http://github.com/ushahidi/Ushahidi_Web
PHP | 492 lines | 333 code | 86 blank | 73 comment | 51 complexity | 6aeb63a784ac512f3303fc0566b8447d MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php defined('SYSPATH') OR die('No direct access allowed.');
  2. /**
  3. * MySQL Database Driver
  4. *
  5. * $Id: Mysql.php 3917 2009-01-21 03:06:22Z 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_Mysql_Driver extends Database_Driver {
  13. /**
  14. * Database connection link
  15. */
  16. protected $link;
  17. /**
  18. * Database configuration
  19. */
  20. protected $db_config;
  21. /**
  22. * Sets the config for the class.
  23. *
  24. * @param array database configuration
  25. */
  26. public function __construct($config)
  27. {
  28. $this->db_config = $config;
  29. Kohana::log('debug', 'MySQL Database Driver Initialized');
  30. }
  31. /**
  32. * Closes the database connection.
  33. */
  34. public function __destruct()
  35. {
  36. is_resource($this->link) and mysql_close($this->link);
  37. }
  38. public function connect()
  39. {
  40. // Check if link already exists
  41. if (is_resource($this->link))
  42. return $this->link;
  43. // Import the connect variables
  44. extract($this->db_config['connection']);
  45. // Persistent connections enabled?
  46. $connect = ($this->db_config['persistent'] == TRUE) ? 'mysql_pconnect' : 'mysql_connect';
  47. // Build the connection info
  48. $host = isset($host) ? $host : $socket;
  49. $port = isset($port) ? ':'.$port : '';
  50. // Make the connection and select the database
  51. if (($this->link = $connect($host.$port, $user, $pass, TRUE)) AND mysql_select_db($database, $this->link))
  52. {
  53. if ($charset = $this->db_config['character_set'])
  54. {
  55. $this->set_charset($charset);
  56. }
  57. // Clear password after successful connect
  58. $this->config['connection']['pass'] = NULL;
  59. return $this->link;
  60. }
  61. return FALSE;
  62. }
  63. public function query($sql)
  64. {
  65. // Only cache if it's turned on, and only cache if it's not a write statement
  66. if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET)\b#i', $sql))
  67. {
  68. $hash = $this->query_hash($sql);
  69. if ( ! isset(self::$query_cache[$hash]))
  70. {
  71. // Set the cached object
  72. self::$query_cache[$hash] = new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql);
  73. }
  74. // Return the cached query
  75. return self::$query_cache[$hash];
  76. }
  77. return new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql);
  78. }
  79. public function set_charset($charset)
  80. {
  81. $this->query('SET NAMES '.$this->escape_str($charset));
  82. }
  83. public function escape_table($table)
  84. {
  85. if (!$this->db_config['escape'])
  86. return $table;
  87. if (stripos($table, ' AS ') !== FALSE)
  88. {
  89. // Force 'AS' to uppercase
  90. $table = str_ireplace(' AS ', ' AS ', $table);
  91. // Runs escape_table on both sides of an AS statement
  92. $table = array_map(array($this, __FUNCTION__), explode(' AS ', $table));
  93. // Re-create the AS statement
  94. return implode(' AS ', $table);
  95. }
  96. return '`'.str_replace('.', '`.`', $table).'`';
  97. }
  98. public function escape_column($column)
  99. {
  100. if (!$this->db_config['escape'])
  101. return $column;
  102. if (strtolower($column) == 'count(*)' OR $column == '*')
  103. return $column;
  104. // This matches any modifiers we support to SELECT.
  105. if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column))
  106. {
  107. if (stripos($column, ' AS ') !== FALSE)
  108. {
  109. // Force 'AS' to uppercase
  110. $column = str_ireplace(' AS ', ' AS ', $column);
  111. // Runs escape_column on both sides of an AS statement
  112. $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column));
  113. // Re-create the AS statement
  114. return implode(' AS ', $column);
  115. }
  116. return preg_replace('/[^.*]+/', '`$0`', $column);
  117. }
  118. $parts = explode(' ', $column);
  119. $column = '';
  120. for ($i = 0, $c = count($parts); $i < $c; $i++)
  121. {
  122. // The column is always last
  123. if ($i == ($c - 1))
  124. {
  125. $column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]);
  126. }
  127. else // otherwise, it's a modifier
  128. {
  129. $column .= $parts[$i].' ';
  130. }
  131. }
  132. return $column;
  133. }
  134. public function regex($field, $match = '', $type = 'AND ', $num_regexs)
  135. {
  136. $prefix = ($num_regexs == 0) ? '' : $type;
  137. return $prefix.' '.$this->escape_column($field).' REGEXP \''.$this->escape_str($match).'\'';
  138. }
  139. public function notregex($field, $match = '', $type = 'AND ', $num_regexs)
  140. {
  141. $prefix = $num_regexs == 0 ? '' : $type;
  142. return $prefix.' '.$this->escape_column($field).' NOT REGEXP \''.$this->escape_str($match) . '\'';
  143. }
  144. public function merge($table, $keys, $values)
  145. {
  146. // Escape the column names
  147. foreach ($keys as $key => $value)
  148. {
  149. $keys[$key] = $this->escape_column($value);
  150. }
  151. return 'REPLACE INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
  152. }
  153. public function limit($limit, $offset = 0)
  154. {
  155. return 'LIMIT '.$offset.', '.$limit;
  156. }
  157. public function compile_select($database)
  158. {
  159. $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT ';
  160. $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*';
  161. if (count($database['from']) > 0)
  162. {
  163. // Escape the tables
  164. $froms = array();
  165. foreach ($database['from'] as $from)
  166. {
  167. $froms[] = $this->escape_column($from);
  168. }
  169. $sql .= "\nFROM ";
  170. $sql .= implode(', ', $froms);
  171. }
  172. if (count($database['join']) > 0)
  173. {
  174. foreach($database['join'] AS $join)
  175. {
  176. $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions'];
  177. }
  178. }
  179. if (count($database['where']) > 0)
  180. {
  181. $sql .= "\nWHERE ";
  182. }
  183. $sql .= implode("\n", $database['where']);
  184. if (count($database['groupby']) > 0)
  185. {
  186. $sql .= "\nGROUP BY ";
  187. $sql .= implode(', ', $database['groupby']);
  188. }
  189. if (count($database['having']) > 0)
  190. {
  191. $sql .= "\nHAVING ";
  192. $sql .= implode("\n", $database['having']);
  193. }
  194. if (count($database['orderby']) > 0)
  195. {
  196. $sql .= "\nORDER BY ";
  197. $sql .= implode(', ', $database['orderby']);
  198. }
  199. if (is_numeric($database['limit']))
  200. {
  201. $sql .= "\n";
  202. $sql .= $this->limit($database['limit'], $database['offset']);
  203. }
  204. return $sql;
  205. }
  206. public function escape_str($str)
  207. {
  208. if (!$this->db_config['escape'])
  209. return $str;
  210. is_resource($this->link) or $this->connect();
  211. return mysql_real_escape_string($str, $this->link);
  212. }
  213. public function list_tables(Database $db)
  214. {
  215. static $tables;
  216. if (empty($tables) AND $query = $db->query('SHOW TABLES FROM '.$this->escape_table($this->db_config['connection']['database'])))
  217. {
  218. foreach ($query->result(FALSE) as $row)
  219. {
  220. $tables[] = current($row);
  221. }
  222. }
  223. return $tables;
  224. }
  225. public function show_error()
  226. {
  227. return mysql_error($this->link);
  228. }
  229. public function list_fields($table)
  230. {
  231. static $tables;
  232. if (empty($tables[$table]))
  233. {
  234. foreach ($this->field_data($table) as $row)
  235. {
  236. // Make an associative array
  237. $tables[$table][$row->Field] = $this->sql_type($row->Type);
  238. if ($row->Key === 'PRI' AND $row->Extra === 'auto_increment')
  239. {
  240. // For sequenced (AUTO_INCREMENT) tables
  241. $tables[$table][$row->Field]['sequenced'] = TRUE;
  242. }
  243. if ($row->Null === 'YES')
  244. {
  245. // Set NULL status
  246. $tables[$table][$row->Field]['null'] = TRUE;
  247. }
  248. }
  249. }
  250. if (!isset($tables[$table]))
  251. throw new Kohana_Database_Exception('database.table_not_found', $table);
  252. return $tables[$table];
  253. }
  254. public function field_data($table)
  255. {
  256. $columns = array();
  257. if ($query = mysql_query('SHOW COLUMNS FROM '.$this->escape_table($table), $this->link))
  258. {
  259. if (mysql_num_rows($query))
  260. {
  261. while ($row = mysql_fetch_object($query))
  262. {
  263. $columns[] = $row;
  264. }
  265. }
  266. }
  267. return $columns;
  268. }
  269. } // End Database_Mysql_Driver Class
  270. /**
  271. * MySQL Result
  272. */
  273. class Mysql_Result extends Database_Result {
  274. // Fetch function and return type
  275. protected $fetch_type = 'mysql_fetch_object';
  276. protected $return_type = MYSQL_ASSOC;
  277. /**
  278. * Sets up the result variables.
  279. *
  280. * @param resource query result
  281. * @param resource database link
  282. * @param boolean return objects or arrays
  283. * @param string SQL query that was run
  284. */
  285. public function __construct($result, $link, $object = TRUE, $sql)
  286. {
  287. $this->result = $result;
  288. // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query
  289. if (is_resource($result))
  290. {
  291. $this->current_row = 0;
  292. $this->total_rows = mysql_num_rows($this->result);
  293. $this->fetch_type = ($object === TRUE) ? 'mysql_fetch_object' : 'mysql_fetch_array';
  294. }
  295. elseif (is_bool($result))
  296. {
  297. if ($result == FALSE)
  298. {
  299. // SQL error
  300. throw new Kohana_Database_Exception('database.error', mysql_error($link).' - '.$sql);
  301. }
  302. else
  303. {
  304. // Its an DELETE, INSERT, REPLACE, or UPDATE query
  305. $this->insert_id = mysql_insert_id($link);
  306. $this->total_rows = mysql_affected_rows($link);
  307. }
  308. }
  309. // Set result type
  310. $this->result($object);
  311. // Store the SQL
  312. $this->sql = $sql;
  313. }
  314. /**
  315. * Destruct, the cleanup crew!
  316. */
  317. public function __destruct()
  318. {
  319. if (is_resource($this->result))
  320. {
  321. mysql_free_result($this->result);
  322. }
  323. }
  324. public function result($object = TRUE, $type = MYSQL_ASSOC)
  325. {
  326. $this->fetch_type = ((bool) $object) ? 'mysql_fetch_object' : 'mysql_fetch_array';
  327. // This check has to be outside the previous statement, because we do not
  328. // know the state of fetch_type when $object = NULL
  329. // NOTE - The class set by $type must be defined before fetching the result,
  330. // autoloading is disabled to save a lot of stupid overhead.
  331. if ($this->fetch_type == 'mysql_fetch_object' AND $object === TRUE)
  332. {
  333. $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
  334. }
  335. else
  336. {
  337. $this->return_type = $type;
  338. }
  339. return $this;
  340. }
  341. public function as_array($object = NULL, $type = MYSQL_ASSOC)
  342. {
  343. return $this->result_array($object, $type);
  344. }
  345. public function result_array($object = NULL, $type = MYSQL_ASSOC)
  346. {
  347. $rows = array();
  348. if (is_string($object))
  349. {
  350. $fetch = $object;
  351. }
  352. elseif (is_bool($object))
  353. {
  354. if ($object === TRUE)
  355. {
  356. $fetch = 'mysql_fetch_object';
  357. $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
  358. }
  359. else
  360. {
  361. $fetch = 'mysql_fetch_array';
  362. }
  363. }
  364. else
  365. {
  366. // Use the default config values
  367. $fetch = $this->fetch_type;
  368. if ($fetch == 'mysql_fetch_object')
  369. {
  370. $type = (is_string($this->return_type) AND Kohana::auto_load($this->return_type)) ? $this->return_type : 'stdClass';
  371. }
  372. }
  373. if (mysql_num_rows($this->result))
  374. {
  375. // Reset the pointer location to make sure things work properly
  376. mysql_data_seek($this->result, 0);
  377. while ($row = $fetch($this->result, $type))
  378. {
  379. $rows[] = $row;
  380. }
  381. }
  382. return isset($rows) ? $rows : array();
  383. }
  384. public function list_fields()
  385. {
  386. $field_names = array();
  387. while ($field = mysql_fetch_field($this->result))
  388. {
  389. $field_names[] = $field->name;
  390. }
  391. return $field_names;
  392. }
  393. public function seek($offset)
  394. {
  395. if ($this->offsetExists($offset) AND mysql_data_seek($this->result, $offset))
  396. {
  397. // Set the current row to the offset
  398. $this->current_row = $offset;
  399. return TRUE;
  400. }
  401. else
  402. {
  403. return FALSE;
  404. }
  405. }
  406. } // End Mysql_Result Class