PageRenderTime 65ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 0ms

/system/libraries/drivers/Database.php

https://github.com/lmorchard/friendfeedarchiver
PHP | 468 lines | 183 code | 54 blank | 231 comment | 20 complexity | bee3620afffaed2a55749ddd0b6c3abf MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Database API driver
  4. *
  5. * $Id: Database.php 2130 2008-02-22 14:49:10Z Geert $
  6. *
  7. * @package Core
  8. * @author Kohana Team
  9. * @copyright (c) 2007-2008 Kohana Team
  10. * @license http://kohanaphp.com/license.html
  11. */
  12. abstract class Database_Driver {
  13. static $query_cache;
  14. /**
  15. * Connect to our database.
  16. * Returns FALSE on failure or a MySQL resource.
  17. *
  18. * @return mixed
  19. */
  20. abstract public function connect();
  21. /**
  22. * Perform a query based on a manually written query.
  23. *
  24. * @param string SQL query to execute
  25. * @return Database_Result
  26. */
  27. abstract public function query($sql);
  28. /**
  29. * Builds a DELETE query.
  30. *
  31. * @param string table name
  32. * @param array where clause
  33. * @return string
  34. */
  35. public function delete($table, $where)
  36. {
  37. return 'DELETE FROM '.$this->escape_table($table).' WHERE '.implode(' ', $where);
  38. }
  39. /**
  40. * Builds an UPDATE query.
  41. *
  42. * @param string table name
  43. * @param array key => value pairs
  44. * @param array where clause
  45. * @return string
  46. */
  47. public function update($table, $values, $where)
  48. {
  49. foreach($values as $key => $val)
  50. {
  51. $valstr[] = $this->escape_column($key).' = '.$val;
  52. }
  53. return 'UPDATE '.$this->escape_table($table).' SET '.implode(', ', $valstr).' WHERE '.implode(' ',$where);
  54. }
  55. /**
  56. * Set the charset using 'SET NAMES <charset>'.
  57. *
  58. * @param string character set to use
  59. */
  60. abstract public function set_charset($charset);
  61. /**
  62. * Wrap the tablename in backticks, has support for: table.field syntax.
  63. *
  64. * @param string table name
  65. * @return string
  66. */
  67. abstract public function escape_table($table);
  68. /**
  69. * Escape a column/field name, has support for special commands.
  70. *
  71. * @param string column name
  72. * @return string
  73. */
  74. abstract public function escape_column($column);
  75. /**
  76. * Builds a WHERE portion of a query.
  77. *
  78. * @param mixed key
  79. * @param string value
  80. * @param string type
  81. * @param int number of where clauses
  82. * @param boolean escape the value
  83. * @return string
  84. */
  85. public function where($key, $value, $type, $num_wheres, $quote)
  86. {
  87. $prefix = ($num_wheres == 0) ? '' : $type;
  88. if ($quote === -1)
  89. {
  90. $value = '';
  91. }
  92. else
  93. {
  94. if ($value === NULL)
  95. {
  96. if ( ! $this->has_operator($key))
  97. {
  98. $key .= ' IS';
  99. }
  100. $value = ' NULL';
  101. }
  102. elseif (is_bool($value))
  103. {
  104. if ( ! $this->has_operator($key))
  105. {
  106. $key .= ' =';
  107. }
  108. $value = ($value == TRUE) ? ' 1' : ' 0';
  109. }
  110. else
  111. {
  112. if ( ! $this->has_operator($key))
  113. {
  114. $key = $this->escape_column($key).' =';
  115. }
  116. else
  117. {
  118. preg_match('/^(.+?)([<>!=]+|\bIS(?:\s+NULL))\s*$/i', $key, $matches);
  119. if(isset($matches[1]) AND isset($matches[2]))
  120. {
  121. $key = $this->escape_column(trim($matches[1])).' '.trim($matches[2]);
  122. }
  123. }
  124. $value = ' '.(($quote == TRUE) ? $this->escape($value) : $value);
  125. }
  126. }
  127. return $prefix.$key.$value;
  128. }
  129. /**
  130. * Builds a LIKE portion of a query.
  131. *
  132. * @param mixed field name
  133. * @param string value to match with field
  134. * @param string clause type (AND or OR)
  135. * @param int number of likes
  136. * @return string
  137. */
  138. public function like($field, $match = '', $type = 'AND ', $num_likes)
  139. {
  140. $prefix = ($num_likes == 0) ? '' : $type;
  141. $match = (substr($match, 0, 1) == '%' OR substr($match, (strlen($match)-1), 1) == '%')
  142. ? $this->escape_str($match)
  143. : '%'.$this->escape_str($match).'%';
  144. return $prefix.' '.$this->escape_column($field).' LIKE \''.$match . '\'';
  145. }
  146. /**
  147. * Builds a NOT LIKE portion of a query.
  148. *
  149. * @param mixed field name
  150. * @param string value to match with field
  151. * @param string clause type (AND or OR)
  152. * @param int number of likes
  153. * @return string
  154. */
  155. public function notlike($field, $match = '', $type = 'AND ', $num_likes)
  156. {
  157. $prefix = ($num_likes == 0) ? '' : $type;
  158. $match = (substr($match, 0, 1) == '%' OR substr($match, (strlen($match)-1), 1) == '%')
  159. ? $this->escape_str($match)
  160. : '%'.$this->escape_str($match).'%';
  161. return $prefix.' '.$this->escape_column($field).' NOT LIKE \''.$match.'\'';
  162. }
  163. /**
  164. * Builds a REGEX portion of a query.
  165. *
  166. * @param string field name
  167. * @param string value to match with field
  168. * @param string clause type (AND or OR)
  169. * @param integer number of regexes
  170. * @return string
  171. */
  172. abstract public function regex($field, $match, $type, $num_regexs);
  173. /**
  174. * Builds a NOT REGEX portion of a query.
  175. *
  176. * @param string field name
  177. * @param string value to match with field
  178. * @param string clause type (AND or OR)
  179. * @param integer number of regexes
  180. * @return string
  181. */
  182. abstract public function notregex($field, $match, $type, $num_regexs);
  183. /**
  184. * Builds an INSERT query.
  185. *
  186. * @param string table name
  187. * @param array keys
  188. * @param array values
  189. * @return string
  190. */
  191. public function insert($table, $keys, $values)
  192. {
  193. // Escape the column names
  194. foreach ($keys as $key => $value)
  195. {
  196. $keys[$key] = $this->escape_column($value);
  197. }
  198. return 'INSERT INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
  199. }
  200. /**
  201. * Builds a MERGE portion of a query.
  202. *
  203. * @param string table name
  204. * @param array keys
  205. * @param array values
  206. * @return string
  207. */
  208. abstract public function merge($table, $keys, $values);
  209. /**
  210. * Builds a LIMIT portion of a query.
  211. *
  212. * @param integer limit
  213. * @param integer offset
  214. * @return string
  215. */
  216. abstract public function limit($limit, $offset = 0);
  217. /**
  218. * Creates a prepared statement.
  219. *
  220. * @param string SQL query
  221. * @return Database_Stmt
  222. */
  223. abstract public function stmt_prepare($sql = '');
  224. /**
  225. * Compiles the SELECT statement.
  226. * Generates a query string based on which functions were used.
  227. * Should not be called directly, the get() function calls it.
  228. *
  229. * @param array select query values
  230. * @return string
  231. */
  232. abstract public function compile_select($database);
  233. /**
  234. * Determines if the string has an arithmetic operator in it.
  235. *
  236. * @param string string to check
  237. * @return boolean
  238. */
  239. public function has_operator($str)
  240. {
  241. return (bool) preg_match('/[<>!=]|\sIS(?:\s+NOT\s+)?\b/i', trim($str));
  242. }
  243. /**
  244. * Escapes any input value.
  245. *
  246. * @param mixed value to escape
  247. * @return string
  248. */
  249. public function escape($value)
  250. {
  251. switch (gettype($value))
  252. {
  253. case 'string':
  254. $value = '\''.$this->escape_str($value).'\'';
  255. break;
  256. case 'boolean':
  257. $value = (int) $value;
  258. break;
  259. case 'double':
  260. $value = sprintf('%F', $value);
  261. break;
  262. default:
  263. $value = ($value === NULL) ? 'NULL' : $value;
  264. break;
  265. }
  266. return (string) $value;
  267. }
  268. /**
  269. * Escapes a string for a query.
  270. *
  271. * @param mixed value to escape
  272. * @return string
  273. */
  274. abstract public function escape_str($str);
  275. /**
  276. * Lists all tables in the database.
  277. *
  278. * @return array
  279. */
  280. abstract public function list_tables();
  281. /**
  282. * Lists all fields in a table.
  283. *
  284. * @param string table name
  285. * @return array
  286. */
  287. abstract function list_fields($table);
  288. /**
  289. * Returns the last database error.
  290. *
  291. * @return string
  292. */
  293. abstract public function show_error();
  294. /**
  295. * Returns field data about a table.
  296. *
  297. * @param string table name
  298. * @return array
  299. */
  300. abstract public function field_data($table);
  301. /**
  302. * Fetches SQL type information about a field, in a generic format.
  303. *
  304. * @param string field datatype
  305. * @return array
  306. */
  307. protected function sql_type($str)
  308. {
  309. static $sql_types;
  310. if ($sql_types === NULL)
  311. {
  312. // Load SQL data types
  313. $sql_types = Config::item('sql_types');
  314. }
  315. $str = strtolower(trim($str));
  316. if (($open = strpos($str, '(')) !== FALSE)
  317. {
  318. // Find closing bracket
  319. $close = strpos($str, ')', $open) - 1;
  320. // Find the type without the size
  321. $type = substr($str, 0, $open);
  322. }
  323. else
  324. {
  325. // No length
  326. $type = $str;
  327. }
  328. empty($sql_types[$type]) and exit
  329. (
  330. 'Unknown field type: '.$type.'. '.
  331. 'Please report this: http://trac.kohanaphp.com/newticket'
  332. );
  333. // Fetch the field definition
  334. $field = $sql_types[$type];
  335. switch($field['type'])
  336. {
  337. case 'string':
  338. case 'float':
  339. if (isset($close))
  340. {
  341. // Add the length to the field info
  342. $field['length'] = substr($str, $open + 1, $close - $open);
  343. }
  344. break;
  345. case 'int':
  346. // Add unsigned value
  347. $field['unsigned'] = (strpos($str, 'unsigned') !== FALSE);
  348. break;
  349. }
  350. return $field;
  351. }
  352. /**
  353. * Clears the internal query cache.
  354. *
  355. * @param string SQL query
  356. */
  357. public function clear_cache($sql = NULL)
  358. {
  359. if (empty($sql))
  360. {
  361. self::$query_cache = array();
  362. }
  363. else
  364. {
  365. unset(self::$query_cache[$this->query_hash($sql)]);
  366. }
  367. Log::add('debug', 'Database cache cleared: '.get_class($this));
  368. }
  369. /**
  370. * Creates a hash for an SQL query string. Replaces newlines with spaces,
  371. * trims, and hashes.
  372. *
  373. * @param string SQL query
  374. * @return string
  375. */
  376. protected function query_hash($sql)
  377. {
  378. return sha1(trim(str_replace("\n", ' ', $sql)));
  379. }
  380. } // End Database Driver Interface
  381. /**
  382. * Database_Result
  383. *
  384. */
  385. interface Database_Result {
  386. /**
  387. * Prepares the query result.
  388. *
  389. * @param boolean return rows as objects
  390. * @param mixed type
  391. * @return Database_Result
  392. */
  393. public function result($object = TRUE, $type = FALSE);
  394. /**
  395. * Builds an array of query results.
  396. *
  397. * @param boolean return rows as objects
  398. * @param mixed type
  399. * @return array
  400. */
  401. public function result_array($object = NULL, $type = FALSE);
  402. /**
  403. * Gets the ID of the last insert statement.
  404. *
  405. * @return integer
  406. */
  407. public function insert_id();
  408. /**
  409. * Gets the fields of an already run query.
  410. *
  411. * @return array
  412. */
  413. public function list_fields();
  414. } // End Database Result Interface