PageRenderTime 60ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/classes/database/pdo/connection.php

https://bitbucket.org/trujka/codegrounds
PHP | 398 lines | 284 code | 50 blank | 64 comment | 24 complexity | ce2e71ada4a1a65cd9d5d29100a714b0 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * PDO 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_PDO_Connection extends \Database_Connection
  13. {
  14. /**
  15. * @var \PDO Raw server connection
  16. */
  17. protected $_connection;
  18. /**
  19. * @var string PDO uses no quoting by default for identifiers
  20. */
  21. protected $_identifier = '';
  22. /**
  23. * @var bool Allows transactions
  24. */
  25. protected $_in_transaction = false;
  26. /**
  27. * @var string Which kind of DB is used
  28. */
  29. public $_db_type = '';
  30. protected function __construct($name, array $config)
  31. {
  32. parent::__construct($name, $config);
  33. if (isset($this->_config['identifier']))
  34. {
  35. // Allow the identifier to be overloaded per-connection
  36. $this->_identifier = (string) $this->_config['identifier'];
  37. }
  38. }
  39. public function connect()
  40. {
  41. if ($this->_connection)
  42. {
  43. return;
  44. }
  45. // Extract the connection parameters, adding required variabels
  46. extract($this->_config['connection'] + array(
  47. 'dsn' => '',
  48. 'username' => null,
  49. 'password' => null,
  50. 'persistent' => false,
  51. 'compress' => false,
  52. ));
  53. // Clear the connection parameters for security
  54. $this->_config['connection'] = array();
  55. // determine db type
  56. $_dsn_find_collon = strpos($dsn, ':');
  57. $this->_db_type = $_dsn_find_collon ? substr($dsn, 0, $_dsn_find_collon) : null;
  58. // Force PDO to use exceptions for all errors
  59. $attrs = array(\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION);
  60. if ( ! empty($persistent))
  61. {
  62. // Make the connection persistent
  63. $attrs[\PDO::ATTR_PERSISTENT] = true;
  64. }
  65. if (in_array(strtolower($this->_db_type), array('mysql', 'mysqli')) and $compress)
  66. {
  67. // Use client compression with mysql or mysqli (doesn't work with mysqlnd)
  68. $attrs[\PDO::MYSQL_ATTR_COMPRESS] = true;
  69. }
  70. try
  71. {
  72. // Create a new PDO connection
  73. $this->_connection = new \PDO($dsn, $username, $password, $attrs);
  74. }
  75. catch (\PDOException $e)
  76. {
  77. $error_code = is_numeric($e->getCode()) ? $e->getCode() : 0;
  78. throw new \Database_Exception($e->getMessage(), $error_code, $e);
  79. }
  80. if ( ! empty($this->_config['charset']))
  81. {
  82. // Set Charset for SQL Server connection
  83. if (strtolower($this->driver_name()) == 'sqlsrv')
  84. {
  85. $this->_connection->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_SYSTEM);
  86. }
  87. else
  88. {
  89. // Set the character set
  90. $this->set_charset($this->_config['charset']);
  91. }
  92. }
  93. }
  94. public function disconnect()
  95. {
  96. // Destroy the PDO object
  97. $this->_connection = null;
  98. return true;
  99. }
  100. /**
  101. * Get the current PDO Driver name
  102. * @return string
  103. */
  104. public function driver_name()
  105. {
  106. return $this->_connection->getAttribute(\PDO::ATTR_DRIVER_NAME);
  107. }
  108. public function set_charset($charset)
  109. {
  110. // Make sure the database is connected
  111. $this->_connection or $this->connect();
  112. // Execute a raw SET NAMES query
  113. $this->_connection->exec('SET NAMES '.$this->quote($charset));
  114. }
  115. public function query($type, $sql, $as_object)
  116. {
  117. // Make sure the database is connected
  118. $this->_connection or $this->connect();
  119. if ( ! empty($this->_config['profiling']))
  120. {
  121. // Get the paths defined in config
  122. $paths = \Config::get('profiling_paths');
  123. // Storage for the trace information
  124. $stacktrace = array();
  125. // Get the execution trace of this query
  126. $include = false;
  127. foreach (debug_backtrace() as $index => $page)
  128. {
  129. // Skip first entry and entries without a filename
  130. if ($index > 0 and empty($page['file']) === false)
  131. {
  132. // Checks to see what paths you want backtrace
  133. foreach($paths as $index => $path)
  134. {
  135. if (strpos($page['file'], $path) !== false)
  136. {
  137. $include = true;
  138. break;
  139. }
  140. }
  141. // Only log if no paths we defined, or we have a path match
  142. if ($include or empty($paths))
  143. {
  144. $stacktrace[] = array('file' => Fuel::clean_path($page['file']), 'line' => $page['line']);
  145. }
  146. }
  147. }
  148. $benchmark = \Profiler::start("Database ({$this->_instance})", $sql, $stacktrace);
  149. }
  150. // run the query. if the connection is lost, try 3 times to reconnect
  151. $attempts = 3;
  152. do
  153. {
  154. try
  155. {
  156. // try to run the query
  157. $result = $this->_connection->query($sql);
  158. break;
  159. }
  160. catch (\Exception $e)
  161. {
  162. // if failed and we have attempts left
  163. if ($attempts > 0)
  164. {
  165. // try reconnecting if it was a MySQL disconnected error
  166. if (strpos($e->getMessage(), '2006 MySQL') !== false)
  167. {
  168. $this->disconnect();
  169. $this->connect();
  170. }
  171. else
  172. {
  173. // other database error, cleanup the profiler
  174. isset($benchmark) and \Profiler::delete($benchmark);
  175. // and convert the exception in a database exception
  176. $error_code = is_numeric($e->getCode()) ? $e->getCode() : 0;
  177. throw new \Database_Exception($e->getMessage().' with query: "'.$sql.'"', $error_code, $e);
  178. }
  179. }
  180. // no more attempts left, bail out
  181. else
  182. {
  183. // and convert the exception in a database exception
  184. $error_code = is_numeric($e->getCode()) ? $e->getCode() : 0;
  185. throw new \Database_Exception($e->getMessage().' with query: "'.$sql.'"', $error_code, $e);
  186. }
  187. }
  188. }
  189. while ($attempts-- > 0);
  190. if (isset($benchmark))
  191. {
  192. \Profiler::stop($benchmark);
  193. }
  194. // Set the last query
  195. $this->last_query = $sql;
  196. if ($type === \DB::SELECT)
  197. {
  198. // Convert the result into an array, as PDOStatement::rowCount is not reliable
  199. if ($as_object === false)
  200. {
  201. $result = $result->fetchAll(\PDO::FETCH_ASSOC);
  202. }
  203. elseif (is_string($as_object))
  204. {
  205. $result = $result->fetchAll(\PDO::FETCH_CLASS, $as_object);
  206. }
  207. else
  208. {
  209. $result = $result->fetchAll(\PDO::FETCH_CLASS, 'stdClass');
  210. }
  211. // Return an iterator of results
  212. return new \Database_Result_Cached($result, $sql, $as_object);
  213. }
  214. elseif ($type === \DB::INSERT)
  215. {
  216. // Return a list of insert id and rows created
  217. return array(
  218. $this->_connection->lastInsertId(),
  219. $result->rowCount(),
  220. );
  221. }
  222. else
  223. {
  224. // Return the number of rows affected
  225. return $result->errorCode() === '00000' ? $result->rowCount() : -1;
  226. }
  227. }
  228. public function list_tables($like = null)
  229. {
  230. throw new \FuelException('Database method '.__METHOD__.' is not supported by '.__CLASS__);
  231. }
  232. public function list_columns($table, $like = null)
  233. {
  234. $this->_connection or $this->connect();
  235. $q = $this->_connection->prepare("DESCRIBE ".$table);
  236. $q->execute();
  237. $result = $q->fetchAll();
  238. $count = 0;
  239. $columns = array();
  240. ! is_null($like) and $like = str_replace('%', '.*', $like);
  241. foreach ($result as $row)
  242. {
  243. if ( ! is_null($like) and ! preg_match('#'.$like.'#', $row['Field'])) continue;
  244. list($type, $length) = $this->_parse_type($row['Type']);
  245. $column = $this->datatype($type);
  246. $column['name'] = $row['Field'];
  247. $column['default'] = $row['Default'];
  248. $column['data_type'] = $type;
  249. $column['null'] = ($row['Null'] == 'YES');
  250. $column['ordinal_position'] = ++$count;
  251. switch ($column['type'])
  252. {
  253. case 'float':
  254. if (isset($length))
  255. {
  256. list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
  257. }
  258. break;
  259. case 'int':
  260. if (isset($length))
  261. {
  262. // MySQL attribute
  263. $column['display'] = $length;
  264. }
  265. break;
  266. case 'string':
  267. switch ($column['data_type'])
  268. {
  269. case 'binary':
  270. case 'varbinary':
  271. $column['character_maximum_length'] = $length;
  272. break;
  273. case 'char':
  274. case 'varchar':
  275. $column['character_maximum_length'] = $length;
  276. case 'text':
  277. case 'tinytext':
  278. case 'mediumtext':
  279. case 'longtext':
  280. $column['collation_name'] = isset($row['Collation']) ? $row['Collation'] : null;
  281. break;
  282. case 'enum':
  283. case 'set':
  284. $column['collation_name'] = isset($row['Collation']) ? $row['Collation'] : null;
  285. $column['options'] = explode('\',\'', substr($length, 1, - 1));
  286. break;
  287. }
  288. break;
  289. }
  290. // MySQL attributes
  291. $column['comment'] = isset($row['Comment']) ? $row['Comment'] : null;
  292. $column['extra'] = $row['Extra'];
  293. $column['key'] = $row['Key'];
  294. $column['privileges'] = isset($row['Privileges']) ? $row['Privileges'] : null;
  295. $columns[$row['Field']] = $column;
  296. }
  297. return $columns;
  298. }
  299. public function datatype($type)
  300. {
  301. // try to determine the datatype
  302. $datatype = parent::datatype($type);
  303. // if not an ANSI database, assume it's string
  304. return empty($datatype) ? array('type' => 'string') : $datatype;
  305. }
  306. public function escape($value)
  307. {
  308. // Make sure the database is connected
  309. $this->_connection or $this->connect();
  310. $result = $this->_connection->quote($value);
  311. // poor-mans workaround for the fact that not all drivers implement quote()
  312. if (empty($result))
  313. {
  314. $result = "'".str_replace("'", "''", $value)."'";
  315. }
  316. return $result;
  317. }
  318. public function error_info()
  319. {
  320. return $this->_connection->errorInfo();
  321. }
  322. public function in_transaction()
  323. {
  324. return $this->_in_transaction;
  325. }
  326. public function start_transaction()
  327. {
  328. $this->_connection or $this->connect();
  329. $this->_in_transaction = true;
  330. return $this->_connection->beginTransaction();
  331. }
  332. public function commit_transaction()
  333. {
  334. $this->_in_transaction = false;
  335. return $this->_connection->commit();
  336. }
  337. public function rollback_transaction()
  338. {
  339. $this->_in_transaction = false;
  340. return $this->_connection->rollBack();
  341. }
  342. }