PageRenderTime 26ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/database/classes/kohana/database.php

https://bitbucket.org/seyar/kinda.local
PHP | 600 lines | 263 code | 59 blank | 278 comment | 18 complexity | f75fc7dc1d1c54cd7228507e955f55ea MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Database connection wrapper/helper.
  4. *
  5. * You may get a database instance using `Database::instance('name')` where
  6. * name is the [config](database/config) group.
  7. *
  8. * This class provides connection instance management via Database Drivers, as
  9. * well as quoting, escaping and other related functions. Querys are done using
  10. * [Database_Query] and [Database_Query_Builder] objects, which can be easily
  11. * created using the [DB] helper class.
  12. *
  13. * @package Kohana/Database
  14. * @category Base
  15. * @author Kohana Team
  16. * @copyright (c) 2008-2010 Kohana Team
  17. * @license http://kohanaphp.com/license
  18. */
  19. abstract class Kohana_Database {
  20. // Query types
  21. const SELECT = 1;
  22. const INSERT = 2;
  23. const UPDATE = 3;
  24. const DELETE = 4;
  25. /**
  26. * @var string default instance name
  27. */
  28. public static $default = 'default';
  29. /**
  30. * @var array Database instances
  31. */
  32. public static $instances = array();
  33. /**
  34. * Get a singleton Database instance. If configuration is not specified,
  35. * it will be loaded from the database configuration file using the same
  36. * group as the name.
  37. *
  38. * // Load the default database
  39. * $db = Database::instance();
  40. *
  41. * // Create a custom configured instance
  42. * $db = Database::instance('custom', $config);
  43. *
  44. * @param string instance name
  45. * @param array configuration parameters
  46. * @return Database
  47. */
  48. public static function instance($name = NULL, array $config = NULL)
  49. {
  50. if ($name === NULL)
  51. {
  52. // Use the default instance name
  53. $name = Database::$default;
  54. }
  55. if ( ! isset(Database::$instances[$name]))
  56. {
  57. if ($config === NULL)
  58. {
  59. // Load the configuration for this database
  60. $config = Kohana::config('database')->$name;
  61. }
  62. if ( ! isset($config['type']))
  63. {
  64. throw new Kohana_Exception('Database type not defined in :name configuration',
  65. array(':name' => $name));
  66. }
  67. // Set the driver class name
  68. $driver = 'Database_'.ucfirst($config['type']);
  69. // Create the database connection instance
  70. new $driver($name, $config);
  71. }
  72. return Database::$instances[$name];
  73. }
  74. /**
  75. * @var string the last query executed
  76. */
  77. public $last_query;
  78. // Character that is used to quote identifiers
  79. protected $_identifier = '"';
  80. // Instance name
  81. protected $_instance;
  82. // Raw server connection
  83. protected $_connection;
  84. // Configuration array
  85. protected $_config;
  86. /**
  87. * Stores the database configuration locally and name the instance.
  88. *
  89. * [!!] This method cannot be accessed directly, you must use [Database::instance].
  90. *
  91. * @return void
  92. */
  93. protected function __construct($name, array $config)
  94. {
  95. // Set the instance name
  96. $this->_instance = $name;
  97. // Store the config locally
  98. $this->_config = $config;
  99. // Store the database instance
  100. Database::$instances[$name] = $this;
  101. }
  102. /**
  103. * Disconnect from the database when the object is destroyed.
  104. *
  105. * // Destroy the database instance
  106. * unset(Database::instances[(string) $db], $db);
  107. *
  108. * [!!] Calling `unset($db)` is not enough to destroy the database, as it
  109. * will still be stored in `Database::$instances`.
  110. *
  111. * @return void
  112. */
  113. final public function __destruct()
  114. {
  115. $this->disconnect();
  116. }
  117. /**
  118. * Returns the database instance name.
  119. *
  120. * echo (string) $db;
  121. *
  122. * @return string
  123. */
  124. final public function __toString()
  125. {
  126. return $this->_instance;
  127. }
  128. /**
  129. * Connect to the database. This is called automatically when the first
  130. * query is executed.
  131. *
  132. * $db->connect();
  133. *
  134. * @throws Database_Exception
  135. * @return void
  136. */
  137. abstract public function connect();
  138. /**
  139. * Disconnect from the database. This is called automatically by [Database::__destruct].
  140. *
  141. * $db->disconnect();
  142. *
  143. * @return boolean
  144. */
  145. abstract public function disconnect();
  146. /**
  147. * Set the connection character set. This is called automatically by [Database::connect].
  148. *
  149. * $db->set_charset('utf8');
  150. *
  151. * @throws Database_Exception
  152. * @param string character set name
  153. * @return void
  154. */
  155. abstract public function set_charset($charset);
  156. /**
  157. * Perform an SQL query of the given type.
  158. *
  159. * // Make a SELECT query and use objects for results
  160. * $db->query(Database::SELECT, 'SELECT * FROM groups', TRUE);
  161. *
  162. * // Make a SELECT query and use "Model_User" for the results
  163. * $db->query(Database::SELECT, 'SELECT * FROM users LIMIT 1', 'Model_User');
  164. *
  165. * @param integer Database::SELECT, Database::INSERT, etc
  166. * @param string SQL query
  167. * @param mixed result object class string, TRUE for stdClass, FALSE for assoc array
  168. * @param array object construct parameters for result class
  169. * @return object Database_Result for SELECT queries
  170. * @return array list (insert id, row count) for INSERT queries
  171. * @return integer number of affected rows for all other queries
  172. */
  173. abstract public function query($type, $sql, $as_object = FALSE, array $params = NULL);
  174. /**
  175. * Count the number of records in the last query, without LIMIT or OFFSET applied.
  176. *
  177. * // Get the total number of records that match the last query
  178. * $count = $db->count_last_query();
  179. *
  180. * @deprecated since v3.0.9
  181. * @return integer
  182. */
  183. public function count_last_query()
  184. {
  185. if ($sql = $this->last_query)
  186. {
  187. $sql = trim($sql);
  188. if (stripos($sql, 'SELECT') !== 0)
  189. {
  190. return FALSE;
  191. }
  192. if (stripos($sql, 'LIMIT') !== FALSE)
  193. {
  194. // Remove LIMIT from the SQL
  195. $sql = preg_replace('/\sLIMIT\s+[^a-z]+/i', ' ', $sql);
  196. }
  197. if (stripos($sql, 'OFFSET') !== FALSE)
  198. {
  199. // Remove OFFSET from the SQL
  200. $sql = preg_replace('/\sOFFSET\s+\d+/i', '', $sql);
  201. }
  202. // Get the total rows from the last query executed
  203. $result = $this->query
  204. (
  205. Database::SELECT,
  206. 'SELECT COUNT(*) AS '.$this->quote_identifier('total_rows').' '
  207. .'FROM ('.$sql.') AS '.$this->quote_table('counted_results'),
  208. TRUE
  209. );
  210. // Return the total number of rows from the query
  211. return (int) $result->current()->total_rows;
  212. }
  213. return FALSE;
  214. }
  215. /**
  216. * Count the number of records in a table.
  217. *
  218. * // Get the total number of records in the "users" table
  219. * $count = $db->count_records('users');
  220. *
  221. * @param mixed table name string or array(query, alias)
  222. * @return integer
  223. */
  224. public function count_records($table)
  225. {
  226. // Quote the table name
  227. $table = $this->quote_identifier($table);
  228. return $this->query(Database::SELECT, 'SELECT COUNT(*) AS total_row_count FROM '.$table, FALSE)
  229. ->get('total_row_count');
  230. }
  231. /**
  232. * Returns a normalized array describing the SQL data type
  233. *
  234. * $db->datatype('char');
  235. *
  236. * @param string SQL data type
  237. * @return array
  238. */
  239. public function datatype($type)
  240. {
  241. static $types = array
  242. (
  243. // SQL-92
  244. 'bit' => array('type' => 'string', 'exact' => TRUE),
  245. 'bit varying' => array('type' => 'string'),
  246. 'char' => array('type' => 'string', 'exact' => TRUE),
  247. 'char varying' => array('type' => 'string'),
  248. 'character' => array('type' => 'string', 'exact' => TRUE),
  249. 'character varying' => array('type' => 'string'),
  250. 'date' => array('type' => 'string'),
  251. 'dec' => array('type' => 'float', 'exact' => TRUE),
  252. 'decimal' => array('type' => 'float', 'exact' => TRUE),
  253. 'double precision' => array('type' => 'float'),
  254. 'float' => array('type' => 'float'),
  255. 'int' => array('type' => 'int', 'min' => '-2147483648', 'max' => '2147483647'),
  256. 'integer' => array('type' => 'int', 'min' => '-2147483648', 'max' => '2147483647'),
  257. 'interval' => array('type' => 'string'),
  258. 'national char' => array('type' => 'string', 'exact' => TRUE),
  259. 'national char varying' => array('type' => 'string'),
  260. 'national character' => array('type' => 'string', 'exact' => TRUE),
  261. 'national character varying' => array('type' => 'string'),
  262. 'nchar' => array('type' => 'string', 'exact' => TRUE),
  263. 'nchar varying' => array('type' => 'string'),
  264. 'numeric' => array('type' => 'float', 'exact' => TRUE),
  265. 'real' => array('type' => 'float'),
  266. 'smallint' => array('type' => 'int', 'min' => '-32768', 'max' => '32767'),
  267. 'time' => array('type' => 'string'),
  268. 'time with time zone' => array('type' => 'string'),
  269. 'timestamp' => array('type' => 'string'),
  270. 'timestamp with time zone' => array('type' => 'string'),
  271. 'varchar' => array('type' => 'string'),
  272. // SQL:1999
  273. 'binary large object' => array('type' => 'string', 'binary' => TRUE),
  274. 'blob' => array('type' => 'string', 'binary' => TRUE),
  275. 'boolean' => array('type' => 'bool'),
  276. 'char large object' => array('type' => 'string'),
  277. 'character large object' => array('type' => 'string'),
  278. 'clob' => array('type' => 'string'),
  279. 'national character large object' => array('type' => 'string'),
  280. 'nchar large object' => array('type' => 'string'),
  281. 'nclob' => array('type' => 'string'),
  282. 'time without time zone' => array('type' => 'string'),
  283. 'timestamp without time zone' => array('type' => 'string'),
  284. // SQL:2003
  285. 'bigint' => array('type' => 'int', 'min' => '-9223372036854775808', 'max' => '9223372036854775807'),
  286. // SQL:2008
  287. 'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE),
  288. 'binary varying' => array('type' => 'string', 'binary' => TRUE),
  289. 'varbinary' => array('type' => 'string', 'binary' => TRUE),
  290. );
  291. if (isset($types[$type]))
  292. return $types[$type];
  293. return array();
  294. }
  295. /**
  296. * List all of the tables in the database. Optionally, a LIKE string can
  297. * be used to search for specific tables.
  298. *
  299. * // Get all tables in the current database
  300. * $tables = $db->list_tables();
  301. *
  302. * // Get all user-related tables
  303. * $tables = $db->list_tables('user%');
  304. *
  305. * @param string table to search for
  306. * @return array
  307. */
  308. abstract public function list_tables($like = NULL);
  309. /**
  310. * Lists all of the columns in a table. Optionally, a LIKE string can be
  311. * used to search for specific fields.
  312. *
  313. * // Get all columns from the "users" table
  314. * $columns = $db->list_columns('users');
  315. *
  316. * // Get all name-related columns
  317. * $columns = $db->list_columns('users', '%name%');
  318. *
  319. * // Get the columns from a table that doesn't use the table prefix
  320. * $columns = $db->list_columns('users', NULL, FALSE);
  321. *
  322. * @param string table to get columns from
  323. * @param string column to search for
  324. * @param boolean whether to add the table prefix automatically or not
  325. * @return array
  326. */
  327. abstract public function list_columns($table, $like = NULL, $add_prefix = TRUE);
  328. /**
  329. * Extracts the text between parentheses, if any.
  330. *
  331. * // Returns: array('CHAR', '6')
  332. * list($type, $length) = $db->_parse_type('CHAR(6)');
  333. *
  334. * @param string
  335. * @return array list containing the type and length, if any
  336. */
  337. protected function _parse_type($type)
  338. {
  339. if (($open = strpos($type, '(')) === FALSE)
  340. {
  341. // No length specified
  342. return array($type, NULL);
  343. }
  344. // Closing parenthesis
  345. $close = strpos($type, ')', $open);
  346. // Length without parentheses
  347. $length = substr($type, $open + 1, $close - 1 - $open);
  348. // Type without the length
  349. $type = substr($type, 0, $open).substr($type, $close + 1);
  350. return array($type, $length);
  351. }
  352. /**
  353. * Return the table prefix defined in the current configuration.
  354. *
  355. * $prefix = $db->table_prefix();
  356. *
  357. * @return string
  358. */
  359. public function table_prefix()
  360. {
  361. return $this->_config['table_prefix'];
  362. }
  363. /**
  364. * Quote a value for an SQL query.
  365. *
  366. * $db->quote(NULL); // 'NULL'
  367. * $db->quote(10); // 10
  368. * $db->quote('fred'); // 'fred'
  369. *
  370. * Objects passed to this function will be converted to strings.
  371. * [Database_Expression] objects will use the value of the expression.
  372. * [Database_Query] objects will be compiled and converted to a sub-query.
  373. * All other objects will be converted using the `__toString` method.
  374. *
  375. * @param mixed any value to quote
  376. * @return string
  377. * @uses Database::escape
  378. */
  379. public function quote($value)
  380. {
  381. if ($value === NULL)
  382. {
  383. return 'NULL';
  384. }
  385. elseif ($value === TRUE)
  386. {
  387. return "'1'";
  388. }
  389. elseif ($value === FALSE)
  390. {
  391. return "'0'";
  392. }
  393. elseif (is_object($value))
  394. {
  395. if ($value instanceof Database_Query)
  396. {
  397. // Create a sub-query
  398. return '('.$value->compile($this).')';
  399. }
  400. elseif ($value instanceof Database_Expression)
  401. {
  402. // Use a raw expression
  403. return $value->value();
  404. }
  405. else
  406. {
  407. // Convert the object to a string
  408. return $this->quote( (string) $value);
  409. }
  410. }
  411. elseif (is_array($value))
  412. {
  413. return '('.implode(', ', array_map(array($this, __FUNCTION__), $value)).')';
  414. }
  415. elseif (is_int($value))
  416. {
  417. return (int) $value;
  418. }
  419. elseif (is_float($value))
  420. {
  421. // Convert to non-locale aware float to prevent possible commas
  422. return sprintf('%F', $value);
  423. }
  424. return $this->escape($value);
  425. }
  426. /**
  427. * Quote a database table name and adds the table prefix if needed.
  428. *
  429. * $table = $db->quote_table($table);
  430. *
  431. * @param mixed table name or array(table, alias)
  432. * @return string
  433. * @uses Database::quote_identifier
  434. * @uses Database::table_prefix
  435. */
  436. public function quote_table($value)
  437. {
  438. // Assign the table by reference from the value
  439. if (is_array($value))
  440. {
  441. $table =& $value[0];
  442. // Attach table prefix to alias
  443. $value[1] = $this->table_prefix().$value[1];
  444. }
  445. else
  446. {
  447. $table =& $value;
  448. }
  449. if (is_string($table) AND strpos($table, '.') === FALSE)
  450. {
  451. // Add the table prefix for tables
  452. $table = $this->table_prefix().$table;
  453. }
  454. return $this->quote_identifier($value);
  455. }
  456. /**
  457. * Quote a database identifier, such as a column name. Adds the
  458. * table prefix to the identifier if a table name is present.
  459. *
  460. * $column = $db->quote_identifier($column);
  461. *
  462. * You can also use SQL methods within identifiers.
  463. *
  464. * // The value of "column" will be quoted
  465. * $column = $db->quote_identifier('COUNT("column")');
  466. *
  467. * Objects passed to this function will be converted to strings.
  468. * [Database_Expression] objects will use the value of the expression.
  469. * [Database_Query] objects will be compiled and converted to a sub-query.
  470. * All other objects will be converted using the `__toString` method.
  471. *
  472. * @param mixed any identifier
  473. * @return string
  474. * @uses Database::table_prefix
  475. */
  476. public function quote_identifier($value)
  477. {
  478. if ($value === '*')
  479. {
  480. return $value;
  481. }
  482. elseif (is_object($value))
  483. {
  484. if ($value instanceof Database_Query)
  485. {
  486. // Create a sub-query
  487. return '('.$value->compile($this).')';
  488. }
  489. elseif ($value instanceof Database_Expression)
  490. {
  491. // Use a raw expression
  492. return $value->value();
  493. }
  494. else
  495. {
  496. // Convert the object to a string
  497. return $this->quote_identifier( (string) $value);
  498. }
  499. }
  500. elseif (is_array($value))
  501. {
  502. // Separate the column and alias
  503. list ($value, $alias) = $value;
  504. return $this->quote_identifier($value).' AS '.$this->quote_identifier($alias);
  505. }
  506. if (strpos($value, '"') !== FALSE)
  507. {
  508. // Quote the column in FUNC("ident") identifiers
  509. return preg_replace('/"(.+?)"/e', '$this->quote_identifier("$1")', $value);
  510. }
  511. elseif (strpos($value, '.') !== FALSE)
  512. {
  513. // Split the identifier into the individual parts
  514. $parts = explode('.', $value);
  515. if ($prefix = $this->table_prefix())
  516. {
  517. // Get the offset of the table name, 2nd-to-last part
  518. // This works for databases that can have 3 identifiers (Postgre)
  519. $offset = count($parts) - 2;
  520. // Add the table prefix to the table name
  521. $parts[$offset] = $prefix.$parts[$offset];
  522. }
  523. // Quote each of the parts
  524. return implode('.', array_map(array($this, __FUNCTION__), $parts));
  525. }
  526. else
  527. {
  528. return $this->_identifier.$value.$this->_identifier;
  529. }
  530. }
  531. /**
  532. * Sanitize a string by escaping characters that could cause an SQL
  533. * injection attack.
  534. *
  535. * $value = $db->escape('any string');
  536. *
  537. * @param string value to quote
  538. * @return string
  539. */
  540. abstract public function escape($value);
  541. } // End Database_Connection