PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/application/libraries/drivers/Database.php

https://github.com/drawrof/hullowrld
PHP | 689 lines | 327 code | 82 blank | 280 comment | 23 complexity | f27444fdf64e301f661636264c342449 MD5 | raw file
  1. <?php defined('ROOT') OR die('No direct access allowed.');
  2. /**
  3. * Database API driver
  4. *
  5. * $Id: Database.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. abstract class Database_Driver {
  13. static $query_cache;
  14. static $sql_types_merged = true;
  15. static $sql_types = array(
  16. // INTEGER
  17. 'tinyint' => array('type' => 'int', 'max' => 127),
  18. 'smallint' => array('type' => 'int', 'max' => 32767),
  19. 'mediumint' => array('type' => 'int', 'max' => 8388607),
  20. 'int' => array('type' => 'int', 'max' => 2147483647),
  21. 'integer' => array('type' => 'int', 'max' => 2147483647),
  22. 'bigint' => array('type' => 'int', 'max' => 9223372036854775807),
  23. // FLOAT & DOUBLE
  24. 'float' => array('type' => 'float'),
  25. 'double' => array('type' => 'float'),
  26. 'double unsigned' => array('type' => 'float'),
  27. 'decimal' => array('type' => 'float'),
  28. 'real' => array('type' => 'float'),
  29. 'numeric' => array('type' => 'float'),
  30. 'float' => array('type' => 'float'),
  31. 'float unsigned' => array('type' => 'float', 'min' => 0),
  32. // BIT
  33. 'boolean' => array('type' => 'boolean'),
  34. 'bit' => array('type' => 'boolean'),
  35. // TIME
  36. 'time' => array('type' => 'date', 'process_as' => 'date', 'format' => 'time'),
  37. 'date' => array('type' => 'date', 'process_as' => 'date', 'format' => 'date'),
  38. 'year' => array('type' => 'date', 'process_as' => 'date', 'format' => 'year'),
  39. 'datetime' => array('type' => 'date', 'process_as' => 'date', 'format' => 'datetime'),
  40. 'timestamp' => array('type' => 'date', 'process_as' => 'date', 'format' => 'datetime'),
  41. // CHAR
  42. 'char' => array('type' => 'string', 'exact' => TRUE),
  43. 'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE),
  44. 'varchar' => array('type' => 'string'),
  45. // ENUM
  46. 'enum' => array('type' => 'string', 'process_as' => 'enum'),
  47. 'set' => array('type' => 'string', 'process_as' => 'set'),
  48. // BINARY
  49. 'varbinary' => array('type' => 'string', 'binary' => TRUE),
  50. 'blob' => array('type' => 'string', 'binary' => TRUE),
  51. 'tinyblob' => array('type' => 'string', 'binary' => TRUE),
  52. 'mediumblob' => array('type' => 'string', 'binary' => TRUE),
  53. 'longblob' => array('type' => 'string', 'binary' => TRUE),
  54. 'clob' => array('type' => 'string', 'binary' => TRUE),
  55. // TEXT
  56. 'text' => array('type' => 'string'),
  57. 'tinytext' => array('type' => 'string'),
  58. 'mediumtext' => array('type' => 'string'),
  59. 'longtext' => array('type' => 'string'),
  60. );
  61. /**
  62. * Connect to our database.
  63. * Returns FALSE on failure or a MySQL resource.
  64. *
  65. * @return mixed
  66. */
  67. abstract public function connect();
  68. /**
  69. * Perform a query based on a manually written query.
  70. *
  71. * @param string SQL query to execute
  72. * @return Database_Result
  73. */
  74. abstract public function query($sql);
  75. /**
  76. * Builds a DELETE query.
  77. *
  78. * @param string table name
  79. * @param array where clause
  80. * @return string
  81. */
  82. public function delete($table, $where)
  83. {
  84. return 'DELETE FROM '.$this->escape_table($table).' WHERE '.implode(' ', $where);
  85. }
  86. /**
  87. * Builds an UPDATE query.
  88. *
  89. * @param string table name
  90. * @param array key => value pairs
  91. * @param array where clause
  92. * @return string
  93. */
  94. public function update($table, $values, $where)
  95. {
  96. foreach ($values as $key => $val)
  97. {
  98. $valstr[] = $this->escape_column($key).' = '.$val;
  99. }
  100. return 'UPDATE '.$this->escape_table($table).' SET '.implode(', ', $valstr).' WHERE '.implode(' ',$where);
  101. }
  102. /**
  103. * Wrap the tablename in backticks, has support for: table.field syntax.
  104. *
  105. * @param string table name
  106. * @return string
  107. */
  108. abstract public function escape_table($table);
  109. /**
  110. * Escape a column/field name, has support for special commands.
  111. *
  112. * @param string column name
  113. * @return string
  114. */
  115. abstract public function escape_column($column);
  116. /**
  117. * Builds a WHERE portion of a query.
  118. *
  119. * @param mixed key
  120. * @param string value
  121. * @param string type
  122. * @param int number of where clauses
  123. * @param boolean escape the value
  124. * @return string
  125. */
  126. public function where($key, $value, $type, $num_wheres, $quote)
  127. {
  128. $prefix = ($num_wheres == 0) ? '' : $type;
  129. if ($quote === -1)
  130. {
  131. $value = '';
  132. }
  133. else
  134. {
  135. if ($value === NULL)
  136. {
  137. if ( ! $this->has_operator($key))
  138. {
  139. $key .= ' IS';
  140. }
  141. $value = ' NULL';
  142. }
  143. elseif (is_bool($value))
  144. {
  145. if ( ! $this->has_operator($key))
  146. {
  147. $key .= ' =';
  148. }
  149. $value = ($value == TRUE) ? ' 1' : ' 0';
  150. }
  151. else
  152. {
  153. if ( ! $this->has_operator($key))
  154. {
  155. $key = $this->escape_column($key).' =';
  156. }
  157. else
  158. {
  159. preg_match('/^(.+?)([<>!=]+|\bIS(?:\s+NULL))\s*$/i', $key, $matches);
  160. if (isset($matches[1]) AND isset($matches[2]))
  161. {
  162. $key = $this->escape_column(trim($matches[1])).' '.trim($matches[2]);
  163. }
  164. }
  165. $value = ' '.(($quote == TRUE) ? $this->escape($value) : $value);
  166. }
  167. }
  168. return $prefix.$key.$value;
  169. }
  170. /**
  171. * Builds a LIKE portion of a query.
  172. *
  173. * @param mixed field name
  174. * @param string value to match with field
  175. * @param boolean add wildcards before and after the match
  176. * @param string clause type (AND or OR)
  177. * @param int number of likes
  178. * @return string
  179. */
  180. public function like($field, $match = '', $auto = TRUE, $type = 'AND ', $num_likes)
  181. {
  182. $prefix = ($num_likes == 0) ? '' : $type;
  183. $match = $this->escape_str($match);
  184. if ($auto === TRUE)
  185. {
  186. // Add the start and end quotes
  187. $match = '%'.str_replace('%', '\\%', $match).'%';
  188. }
  189. return $prefix.' '.$this->escape_column($field).' LIKE \''.$match . '\'';
  190. }
  191. /**
  192. * Builds a NOT LIKE portion of a query.
  193. *
  194. * @param mixed field name
  195. * @param string value to match with field
  196. * @param string clause type (AND or OR)
  197. * @param int number of likes
  198. * @return string
  199. */
  200. public function notlike($field, $match = '', $auto = TRUE, $type = 'AND ', $num_likes)
  201. {
  202. $prefix = ($num_likes == 0) ? '' : $type;
  203. $match = $this->escape_str($match);
  204. if ($auto === TRUE)
  205. {
  206. // Add the start and end quotes
  207. $match = '%'.$match.'%';
  208. }
  209. return $prefix.' '.$this->escape_column($field).' NOT LIKE \''.$match.'\'';
  210. }
  211. /**
  212. * Builds a NOT REGEX portion of a query.
  213. *
  214. * @param string field name
  215. * @param string value to match with field
  216. * @param string clause type (AND or OR)
  217. * @param integer number of regexes
  218. * @return string
  219. */
  220. public function notregex($field, $match, $type, $num_regexs)
  221. {
  222. throw new DatabaseException(
  223. 'database_not_implemented',
  224. array(
  225. 'function' => __FUNCTION__,
  226. )
  227. );
  228. }
  229. /**
  230. * Builds an INSERT query.
  231. *
  232. * @param string table name
  233. * @param array keys
  234. * @param array values
  235. * @return string
  236. */
  237. public function insert($table, $keys, $values)
  238. {
  239. // Escape the column names
  240. foreach ($keys as $key => $value)
  241. {
  242. $keys[$key] = $this->escape_column($value);
  243. }
  244. return 'INSERT INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
  245. }
  246. /**
  247. * Builds a MERGE portion of a query.
  248. *
  249. * @param string table name
  250. * @param array keys
  251. * @param array values
  252. * @return string
  253. */
  254. public function merge($table, $keys, $values)
  255. {
  256. throw new DatabaseException(
  257. 'database_not_implemented',
  258. array(
  259. 'function' => __FUNCTION__,
  260. )
  261. );
  262. }
  263. /**
  264. * Builds a LIMIT portion of a query.
  265. *
  266. * @param integer limit
  267. * @param integer offset
  268. * @return string
  269. */
  270. abstract public function limit($limit, $offset = 0);
  271. /**
  272. * Creates a prepared statement.
  273. *
  274. * @param string SQL query
  275. * @return Database_Stmt
  276. */
  277. public function stmt_prepare($sql = '')
  278. {
  279. throw new DatabaseException(
  280. 'database_not_implemented',
  281. array(
  282. 'function' => __FUNCTION__,
  283. )
  284. );
  285. }
  286. /**
  287. * Compiles the SELECT statement.
  288. * Generates a query string based on which functions were used.
  289. * Should not be called directly, the get() function calls it.
  290. *
  291. * @param array select query values
  292. * @return string
  293. */
  294. abstract public function compile_select($database);
  295. /**
  296. * Determines if the string has an arithmetic operator in it.
  297. *
  298. * @param string string to check
  299. * @return boolean
  300. */
  301. public function has_operator($str)
  302. {
  303. return (bool) preg_match('/[<>!=]|\sIS(?:\s+NOT\s+)?\b/i', trim($str));
  304. }
  305. /**
  306. * Escapes any input value.
  307. *
  308. * @param mixed value to escape
  309. * @return string
  310. */
  311. public function escape($value)
  312. {
  313. if ( ! $this->db_config['escape'])
  314. return $value;
  315. switch (gettype($value))
  316. {
  317. case 'string':
  318. $value = '\''.$this->escape_str($value).'\'';
  319. break;
  320. case 'boolean':
  321. $value = (int) $value;
  322. break;
  323. case 'double':
  324. // Convert to non-locale aware float to prevent possible commas
  325. $value = sprintf('%F', $value);
  326. break;
  327. default:
  328. $value = ($value === NULL) ? 'NULL' : $value;
  329. break;
  330. }
  331. return (string) $value;
  332. }
  333. /**
  334. * Escapes a string for a query.
  335. *
  336. * @param mixed value to escape
  337. * @return string
  338. */
  339. abstract public function escape_str($str);
  340. /**
  341. * Lists all tables in the database.
  342. *
  343. * @return array
  344. */
  345. abstract public function list_tables(DatabaseLibrary $db);
  346. /**
  347. * Lists all fields in a table.
  348. *
  349. * @param string table name
  350. * @return array
  351. */
  352. abstract function list_fields($table);
  353. /**
  354. * Returns the last database error.
  355. *
  356. * @return string
  357. */
  358. abstract public function show_error();
  359. /**
  360. * Returns field data about a table.
  361. *
  362. * @param string table name
  363. * @return array
  364. */
  365. abstract public function field_data($table);
  366. /**
  367. * Fetches SQL type information about a field, in a generic format.
  368. *
  369. * @param string field datatype
  370. * @return array
  371. */
  372. protected function sql_type($str)
  373. {
  374. $sql_types =& self::$sql_types;
  375. $str = strtolower(trim($str));
  376. if (($open = strpos($str, '(')) !== FALSE)
  377. {
  378. // Find closing bracket
  379. $close = strpos($str, ')', $open) - 1;
  380. // Find the type without the size
  381. $type = substr($str, 0, $open);
  382. }
  383. else
  384. {
  385. // No length
  386. $type = $str;
  387. }
  388. empty($sql_types[$type]) and exit
  389. (
  390. 'Unknown field type: '.$type.'. '
  391. );
  392. // Fetch the field definition
  393. $field = $sql_types[$type];
  394. switch ($field['type'])
  395. {
  396. case 'string':
  397. case 'float':
  398. if (isset($close))
  399. {
  400. // Add the length to the field info
  401. $field['length'] = substr($str, $open + 1, $close - $open);
  402. }
  403. break;
  404. case 'int':
  405. // Add unsigned value
  406. $field['unsigned'] = (strpos($str, 'unsigned') !== FALSE);
  407. // Switch to boolean type if tinyint(1)
  408. if ($str == 'tinyint(1)') {
  409. $field = $sql_types['boolean'];
  410. }
  411. break;
  412. }
  413. return $field;
  414. }
  415. /**
  416. * Clears the internal query cache.
  417. *
  418. * @param string SQL query
  419. */
  420. public function clear_cache($sql = NULL)
  421. {
  422. if (empty($sql))
  423. {
  424. self::$query_cache = array();
  425. }
  426. else
  427. {
  428. unset(self::$query_cache[$this->query_hash($sql)]);
  429. }
  430. }
  431. /**
  432. * Creates a hash for an SQL query string. Replaces newlines with spaces,
  433. * trims, and hashes.
  434. *
  435. * @param string SQL query
  436. * @return string
  437. */
  438. protected function query_hash($sql)
  439. {
  440. return sha1(str_replace("\n", ' ', trim($sql)));
  441. }
  442. } // End Database Driver Interface
  443. /**
  444. * Database_Result
  445. *
  446. */
  447. abstract class DatabaseResult implements ArrayAccess, Iterator, Countable {
  448. // Result resource, insert id, and SQL
  449. protected $result;
  450. protected $insert_id;
  451. protected $sql;
  452. // Current and total rows
  453. protected $current_row = 0;
  454. protected $total_rows = 0;
  455. // Fetch function and return type
  456. protected $fetch_type;
  457. protected $return_type;
  458. /**
  459. * Returns the SQL used to fetch the result.
  460. *
  461. * @return string
  462. */
  463. public function sql()
  464. {
  465. return $this->sql;
  466. }
  467. /**
  468. * Returns the insert id from the result.
  469. *
  470. * @return mixed
  471. */
  472. public function insert_id()
  473. {
  474. return $this->insert_id;
  475. }
  476. /**
  477. * Prepares the query result.
  478. *
  479. * @param boolean return rows as objects
  480. * @param mixed type
  481. * @return Database_Result
  482. */
  483. abstract function result($object = TRUE, $type = FALSE);
  484. /**
  485. * Builds an array of query results.
  486. *
  487. * @param boolean return rows as objects
  488. * @param mixed type
  489. * @return array
  490. */
  491. abstract function result_array($object = NULL, $type = FALSE);
  492. /**
  493. * Gets the fields of an already run query.
  494. *
  495. * @return array
  496. */
  497. abstract public function list_fields();
  498. /**
  499. * Seek to an offset in the results.
  500. *
  501. * @return boolean
  502. */
  503. abstract public function seek($offset);
  504. /**
  505. * Countable: count
  506. */
  507. public function count()
  508. {
  509. return $this->total_rows;
  510. }
  511. /**
  512. * ArrayAccess: offsetExists
  513. */
  514. public function offsetExists($offset)
  515. {
  516. if ($this->total_rows > 0)
  517. {
  518. $min = 0;
  519. $max = $this->total_rows - 1;
  520. return ! ($offset < $min OR $offset > $max);
  521. }
  522. return FALSE;
  523. }
  524. /**
  525. * ArrayAccess: offsetGet
  526. */
  527. public function offsetGet($offset)
  528. {
  529. if ( ! $this->seek($offset))
  530. return FALSE;
  531. // Return the row by calling the defined fetching callback
  532. return call_user_func($this->fetch_type, $this->result, $this->return_type);
  533. }
  534. /**
  535. * ArrayAccess: offsetSet
  536. *
  537. * @throws Kohana_Database_Exception
  538. */
  539. final public function offsetSet($offset, $value)
  540. {
  541. throw new DatabaseException(
  542. 'database_result_read_only',
  543. array(
  544. 'function' => __FUNCTION__,
  545. )
  546. );
  547. }
  548. /**
  549. * ArrayAccess: offsetUnset
  550. *
  551. * @throws Kohana_Database_Exception
  552. */
  553. final public function offsetUnset($offset)
  554. {
  555. throw new DatabaseException(
  556. 'database_result_read_only',
  557. array(
  558. 'function' => __FUNCTION__,
  559. )
  560. );
  561. }
  562. /**
  563. * Iterator: current
  564. */
  565. public function current()
  566. {
  567. return $this->offsetGet($this->current_row);
  568. }
  569. /**
  570. * Iterator: key
  571. */
  572. public function key()
  573. {
  574. return $this->current_row;
  575. }
  576. /**
  577. * Iterator: next
  578. */
  579. public function next()
  580. {
  581. ++$this->current_row;
  582. return $this;
  583. }
  584. /**
  585. * Iterator: prev
  586. */
  587. public function prev()
  588. {
  589. --$this->current_row;
  590. return $this;
  591. }
  592. /**
  593. * Iterator: rewind
  594. */
  595. public function rewind()
  596. {
  597. $this->current_row = 0;
  598. return $this;
  599. }
  600. /**
  601. * Iterator: valid
  602. */
  603. public function valid()
  604. {
  605. return $this->offsetExists($this->current_row);
  606. }
  607. } // End Database Result Interface