Abstract.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Db
  17. * @subpackage Adapter
  18. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * @see Zend_Db_Adapter_Abstract
  24. */
  25. require_once 'Zend/Db/Adapter/Abstract.php';
  26. /**
  27. * @see Zend_Loader
  28. */
  29. require_once 'Zend/Loader.php';
  30. /**
  31. * @see Zend_Db_Statement_Pdo
  32. */
  33. require_once 'Zend/Db/Statement/Pdo.php';
  34. /**
  35. * Class for connecting to SQL databases and performing common operations using PDO.
  36. *
  37. * @category Zend
  38. * @package Zend_Db
  39. * @subpackage Adapter
  40. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  41. * @license http://framework.zend.com/license/new-bsd New BSD License
  42. */
  43. abstract class Zend_Db_Adapter_Pdo_Abstract extends Zend_Db_Adapter_Abstract
  44. {
  45. /**
  46. * Default class name for a DB statement.
  47. *
  48. * @var string
  49. */
  50. protected $_defaultStmtClass = 'Zend_Db_Statement_Pdo';
  51. /**
  52. * Creates a PDO DSN for the adapter from $this->_config settings.
  53. *
  54. * @return string
  55. */
  56. protected function _dsn()
  57. {
  58. // baseline of DSN parts
  59. $dsn = $this->_config;
  60. // don't pass the username, password, and driver_options in the DSN
  61. unset($dsn['username']);
  62. unset($dsn['password']);
  63. unset($dsn['options']);
  64. unset($dsn['charset']);
  65. unset($dsn['driver_options']);
  66. // use all remaining parts in the DSN
  67. foreach ($dsn as $key => $val) {
  68. $dsn[$key] = "$key=$val";
  69. }
  70. return $this->_pdoType . ':' . implode(';', $dsn);
  71. }
  72. /**
  73. * Creates a PDO object and connects to the database.
  74. *
  75. * @return void
  76. * @throws Zend_Db_Adapter_Exception
  77. */
  78. protected function _connect()
  79. {
  80. // if we already have a PDO object, no need to re-connect.
  81. if ($this->_connection) {
  82. return;
  83. }
  84. // get the dsn first, because some adapters alter the $_pdoType
  85. $dsn = $this->_dsn();
  86. // check for PDO extension
  87. if (!extension_loaded('pdo')) {
  88. /**
  89. * @see Zend_Db_Adapter_Exception
  90. */
  91. require_once 'Zend/Db/Adapter/Exception.php';
  92. throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
  93. }
  94. // check the PDO driver is available
  95. if (!in_array($this->_pdoType, PDO::getAvailableDrivers())) {
  96. /**
  97. * @see Zend_Db_Adapter_Exception
  98. */
  99. require_once 'Zend/Db/Adapter/Exception.php';
  100. throw new Zend_Db_Adapter_Exception('The ' . $this->_pdoType . ' driver is not currently installed');
  101. }
  102. // create PDO connection
  103. $q = $this->_profiler->queryStart('connect', Zend_Db_Profiler::CONNECT);
  104. try {
  105. $this->_connection = new PDO(
  106. $dsn,
  107. $this->_config['username'],
  108. $this->_config['password'],
  109. $this->_config['driver_options']
  110. );
  111. $this->_profiler->queryEnd($q);
  112. // set the PDO connection to perform case-folding on array keys, or not
  113. $this->_connection->setAttribute(PDO::ATTR_CASE, $this->_caseFolding);
  114. // always use exceptions.
  115. $this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  116. } catch (PDOException $e) {
  117. /**
  118. * @see Zend_Db_Adapter_Exception
  119. */
  120. require_once 'Zend/Db/Adapter/Exception.php';
  121. throw new Zend_Db_Adapter_Exception($e->getMessage());
  122. }
  123. }
  124. /**
  125. * Test if a connection is active
  126. *
  127. * @return boolean
  128. */
  129. public function isConnected()
  130. {
  131. return ((bool) ($this->_connection instanceof PDO));
  132. }
  133. /**
  134. * Force the connection to close.
  135. *
  136. * @return void
  137. */
  138. public function closeConnection()
  139. {
  140. $this->_connection = null;
  141. }
  142. /**
  143. * Prepares an SQL statement.
  144. *
  145. * @param string $sql The SQL statement with placeholders.
  146. * @param array $bind An array of data to bind to the placeholders.
  147. * @return PDOStatement
  148. */
  149. public function prepare($sql)
  150. {
  151. $this->_connect();
  152. $stmtClass = $this->_defaultStmtClass;
  153. Zend_Loader::loadClass($stmtClass);
  154. $stmt = new $stmtClass($this, $sql);
  155. $stmt->setFetchMode($this->_fetchMode);
  156. return $stmt;
  157. }
  158. /**
  159. * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
  160. *
  161. * As a convention, on RDBMS brands that support sequences
  162. * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
  163. * from the arguments and returns the last id generated by that sequence.
  164. * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
  165. * returns the last value generated for such a column, and the table name
  166. * argument is disregarded.
  167. *
  168. * On RDBMS brands that don't support sequences, $tableName and $primaryKey
  169. * are ignored.
  170. *
  171. * @param string $tableName OPTIONAL Name of table.
  172. * @param string $primaryKey OPTIONAL Name of primary key column.
  173. * @return string
  174. */
  175. public function lastInsertId($tableName = null, $primaryKey = null)
  176. {
  177. $this->_connect();
  178. return $this->_connection->lastInsertId();
  179. }
  180. /**
  181. * Special handling for PDO query().
  182. * All bind parameter names must begin with ':'
  183. *
  184. * @param string|Zend_Db_Select $sql The SQL statement with placeholders.
  185. * @param array $bind An array of data to bind to the placeholders.
  186. * @return Zend_Db_Statement_Pdo
  187. * @throws Zend_Db_Adapter_Exception To re-throw PDOException.
  188. */
  189. public function query($sql, $bind = array())
  190. {
  191. if (empty($bind) && $sql instanceof Zend_Db_Select) {
  192. $bind = $sql->getBind();
  193. }
  194. if (is_array($bind)) {
  195. foreach ($bind as $name => $value) {
  196. if (!is_int($name) && !preg_match('/^:/', $name)) {
  197. $newName = ":$name";
  198. unset($bind[$name]);
  199. $bind[$newName] = $value;
  200. }
  201. }
  202. }
  203. try {
  204. return parent::query($sql, $bind);
  205. } catch (PDOException $e) {
  206. /**
  207. * @see Zend_Db_Statement_Exception
  208. */
  209. require_once 'Zend/Db/Statement/Exception.php';
  210. throw new Zend_Db_Statement_Exception($e->getMessage());
  211. }
  212. }
  213. /**
  214. * Executes an SQL statement and return the number of affected rows
  215. *
  216. * @param mixed $sql The SQL statement with placeholders.
  217. * May be a string or Zend_Db_Select.
  218. * @return integer Number of rows that were modified
  219. * or deleted by the SQL statement
  220. */
  221. public function exec($sql)
  222. {
  223. if ($sql instanceof Zend_Db_Select) {
  224. $sql = $sql->assemble();
  225. }
  226. try {
  227. $affected = $this->getConnection()->exec($sql);
  228. if ($affected === false) {
  229. $errorInfo = $this->getConnection()->errorInfo();
  230. /**
  231. * @see Zend_Db_Adapter_Exception
  232. */
  233. require_once 'Zend/Db/Adapter/Exception.php';
  234. throw new Zend_Db_Adapter_Exception($errorInfo[2]);
  235. }
  236. return $affected;
  237. } catch (PDOException $e) {
  238. /**
  239. * @see Zend_Db_Adapter_Exception
  240. */
  241. require_once 'Zend/Db/Adapter/Exception.php';
  242. throw new Zend_Db_Adapter_Exception($e->getMessage());
  243. }
  244. }
  245. /**
  246. * Quote a raw string.
  247. *
  248. * @param string $value Raw string
  249. * @return string Quoted string
  250. */
  251. protected function _quote($value)
  252. {
  253. if (is_int($value) || is_float($value)) {
  254. return $value;
  255. }
  256. $this->_connect();
  257. return $this->_connection->quote($value);
  258. }
  259. /**
  260. * Begin a transaction.
  261. */
  262. protected function _beginTransaction()
  263. {
  264. $this->_connect();
  265. $this->_connection->beginTransaction();
  266. }
  267. /**
  268. * Commit a transaction.
  269. */
  270. protected function _commit()
  271. {
  272. $this->_connect();
  273. $this->_connection->commit();
  274. }
  275. /**
  276. * Roll-back a transaction.
  277. */
  278. protected function _rollBack() {
  279. $this->_connect();
  280. $this->_connection->rollBack();
  281. }
  282. /**
  283. * Set the PDO fetch mode.
  284. *
  285. * @todo Support FETCH_CLASS and FETCH_INTO.
  286. *
  287. * @param int $mode A PDO fetch mode.
  288. * @return void
  289. * @throws Zend_Db_Adapter_Exception
  290. */
  291. public function setFetchMode($mode)
  292. {
  293. //check for PDO extension
  294. if (!extension_loaded('pdo')) {
  295. /**
  296. * @see Zend_Db_Adapter_Exception
  297. */
  298. require_once 'Zend/Db/Adapter/Exception.php';
  299. throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
  300. }
  301. switch ($mode) {
  302. case PDO::FETCH_LAZY:
  303. case PDO::FETCH_ASSOC:
  304. case PDO::FETCH_NUM:
  305. case PDO::FETCH_BOTH:
  306. case PDO::FETCH_NAMED:
  307. case PDO::FETCH_OBJ:
  308. $this->_fetchMode = $mode;
  309. break;
  310. default:
  311. /**
  312. * @see Zend_Db_Adapter_Exception
  313. */
  314. require_once 'Zend/Db/Adapter/Exception.php';
  315. throw new Zend_Db_Adapter_Exception("Invalid fetch mode '$mode' specified");
  316. break;
  317. }
  318. }
  319. /**
  320. * Check if the adapter supports real SQL parameters.
  321. *
  322. * @param string $type 'positional' or 'named'
  323. * @return bool
  324. */
  325. public function supportsParameters($type)
  326. {
  327. switch ($type) {
  328. case 'positional':
  329. case 'named':
  330. default:
  331. return true;
  332. }
  333. }
  334. /**
  335. * Retrieve server version in PHP style
  336. *
  337. * @return string
  338. */
  339. public function getServerVersion()
  340. {
  341. $this->_connect();
  342. try {
  343. $version = $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
  344. } catch (PDOException $e) {
  345. // In case of the driver doesn't support getting attributes
  346. return null;
  347. }
  348. $matches = null;
  349. if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
  350. return $matches[1];
  351. } else {
  352. return null;
  353. }
  354. }
  355. }