2
0

Abstract.php 11 KB

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