File indexing completed on 2025-03-02 05:29:17

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Db
0017  * @subpackage Adapter
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 
0024 /**
0025  * @see Zend_Db_Adapter_Abstract
0026  */
0027 // require_once 'Zend/Db/Adapter/Abstract.php';
0028 
0029 
0030 /**
0031  * @see Zend_Db_Statement_Pdo
0032  */
0033 // require_once 'Zend/Db/Statement/Pdo.php';
0034 
0035 
0036 /**
0037  * Class for connecting to SQL databases and performing common operations using PDO.
0038  *
0039  * @category   Zend
0040  * @package    Zend_Db
0041  * @subpackage Adapter
0042  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0043  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0044  */
0045 abstract class Zend_Db_Adapter_Pdo_Abstract extends Zend_Db_Adapter_Abstract
0046 {
0047 
0048     /**
0049      * Default class name for a DB statement.
0050      *
0051      * @var string
0052      */
0053     protected $_defaultStmtClass = 'Zend_Db_Statement_Pdo';
0054 
0055     /**
0056      * Creates a PDO DSN for the adapter from $this->_config settings.
0057      *
0058      * @return string
0059      */
0060     protected function _dsn()
0061     {
0062         // baseline of DSN parts
0063         $dsn = $this->_config;
0064 
0065         // don't pass the username, password, charset, persistent and driver_options in the DSN
0066         unset($dsn['username']);
0067         unset($dsn['password']);
0068         unset($dsn['options']);
0069         unset($dsn['charset']);
0070         unset($dsn['persistent']);
0071         unset($dsn['driver_options']);
0072 
0073         // use all remaining parts in the DSN
0074         foreach ($dsn as $key => $val) {
0075             $dsn[$key] = "$key=$val";
0076         }
0077 
0078         return $this->_pdoType . ':' . implode(';', $dsn);
0079     }
0080 
0081     /**
0082      * Creates a PDO object and connects to the database.
0083      *
0084      * @return void
0085      * @throws Zend_Db_Adapter_Exception
0086      */
0087     protected function _connect()
0088     {
0089         // if we already have a PDO object, no need to re-connect.
0090         if ($this->_connection) {
0091             return;
0092         }
0093 
0094         // get the dsn first, because some adapters alter the $_pdoType
0095         $dsn = $this->_dsn();
0096 
0097         // check for PDO extension
0098         if (!extension_loaded('pdo')) {
0099             /**
0100              * @see Zend_Db_Adapter_Exception
0101              */
0102             // require_once 'Zend/Db/Adapter/Exception.php';
0103             throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
0104         }
0105 
0106         // check the PDO driver is available
0107         if (!in_array($this->_pdoType, PDO::getAvailableDrivers())) {
0108             /**
0109              * @see Zend_Db_Adapter_Exception
0110              */
0111             // require_once 'Zend/Db/Adapter/Exception.php';
0112             throw new Zend_Db_Adapter_Exception('The ' . $this->_pdoType . ' driver is not currently installed');
0113         }
0114 
0115         // create PDO connection
0116         $q = $this->_profiler->queryStart('connect', Zend_Db_Profiler::CONNECT);
0117 
0118         // add the persistence flag if we find it in our config array
0119         if (isset($this->_config['persistent']) && ($this->_config['persistent'] == true)) {
0120             $this->_config['driver_options'][PDO::ATTR_PERSISTENT] = true;
0121         }
0122 
0123         try {
0124             $this->_connection = new PDO(
0125                 $dsn,
0126                 $this->_config['username'],
0127                 $this->_config['password'],
0128                 $this->_config['driver_options']
0129             );
0130 
0131             $this->_profiler->queryEnd($q);
0132 
0133             // set the PDO connection to perform case-folding on array keys, or not
0134             $this->_connection->setAttribute(PDO::ATTR_CASE, $this->_caseFolding);
0135 
0136             // always use exceptions.
0137             $this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
0138 
0139         } catch (PDOException $e) {
0140             /**
0141              * @see Zend_Db_Adapter_Exception
0142              */
0143             // require_once 'Zend/Db/Adapter/Exception.php';
0144             throw new Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
0145         }
0146 
0147     }
0148 
0149     /**
0150      * Test if a connection is active
0151      *
0152      * @return boolean
0153      */
0154     public function isConnected()
0155     {
0156         return ((bool) ($this->_connection instanceof PDO));
0157     }
0158 
0159     /**
0160      * Force the connection to close.
0161      *
0162      * @return void
0163      */
0164     public function closeConnection()
0165     {
0166         $this->_connection = null;
0167     }
0168 
0169     /**
0170      * Prepares an SQL statement.
0171      *
0172      * @param string $sql The SQL statement with placeholders.
0173      * @param array $bind An array of data to bind to the placeholders.
0174      * @return PDOStatement
0175      */
0176     public function prepare($sql)
0177     {
0178         $this->_connect();
0179         $stmtClass = $this->_defaultStmtClass;
0180         if (!class_exists($stmtClass)) {
0181             // require_once 'Zend/Loader.php';
0182             Zend_Loader::loadClass($stmtClass);
0183         }
0184         $stmt = new $stmtClass($this, $sql);
0185         $stmt->setFetchMode($this->_fetchMode);
0186         return $stmt;
0187     }
0188 
0189     /**
0190      * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
0191      *
0192      * As a convention, on RDBMS brands that support sequences
0193      * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
0194      * from the arguments and returns the last id generated by that sequence.
0195      * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
0196      * returns the last value generated for such a column, and the table name
0197      * argument is disregarded.
0198      *
0199      * On RDBMS brands that don't support sequences, $tableName and $primaryKey
0200      * are ignored.
0201      *
0202      * @param string $tableName   OPTIONAL Name of table.
0203      * @param string $primaryKey  OPTIONAL Name of primary key column.
0204      * @return string
0205      */
0206     public function lastInsertId($tableName = null, $primaryKey = null)
0207     {
0208         $this->_connect();
0209         return $this->_connection->lastInsertId();
0210     }
0211 
0212     /**
0213      * Special handling for PDO query().
0214      * All bind parameter names must begin with ':'
0215      *
0216      * @param string|Zend_Db_Select $sql The SQL statement with placeholders.
0217      * @param array $bind An array of data to bind to the placeholders.
0218      * @return Zend_Db_Statement_Pdo
0219      * @throws Zend_Db_Adapter_Exception To re-throw PDOException.
0220      */
0221     public function query($sql, $bind = array())
0222     {
0223         if (empty($bind) && $sql instanceof Zend_Db_Select) {
0224             $bind = $sql->getBind();
0225         }
0226 
0227         if (is_array($bind)) {
0228             foreach ($bind as $name => $value) {
0229                 if (!is_int($name) && !preg_match('/^:/', $name)) {
0230                     $newName = ":$name";
0231                     unset($bind[$name]);
0232                     $bind[$newName] = $value;
0233                 }
0234             }
0235         }
0236 
0237         try {
0238             return parent::query($sql, $bind);
0239         } catch (PDOException $e) {
0240             /**
0241              * @see Zend_Db_Statement_Exception
0242              */
0243             // require_once 'Zend/Db/Statement/Exception.php';
0244             throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
0245         }
0246     }
0247 
0248     /**
0249      * Executes an SQL statement and return the number of affected rows
0250      *
0251      * @param  mixed  $sql  The SQL statement with placeholders.
0252      *                      May be a string or Zend_Db_Select.
0253      * @return integer      Number of rows that were modified
0254      *                      or deleted by the SQL statement
0255      */
0256     public function exec($sql)
0257     {
0258         if ($sql instanceof Zend_Db_Select) {
0259             $sql = $sql->assemble();
0260         }
0261 
0262         try {
0263             $affected = $this->getConnection()->exec($sql);
0264 
0265             if ($affected === false) {
0266                 $errorInfo = $this->getConnection()->errorInfo();
0267                 /**
0268                  * @see Zend_Db_Adapter_Exception
0269                  */
0270                 // require_once 'Zend/Db/Adapter/Exception.php';
0271                 throw new Zend_Db_Adapter_Exception($errorInfo[2]);
0272             }
0273 
0274             return $affected;
0275         } catch (PDOException $e) {
0276             /**
0277              * @see Zend_Db_Adapter_Exception
0278              */
0279             // require_once 'Zend/Db/Adapter/Exception.php';
0280             throw new Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
0281         }
0282     }
0283 
0284     /**
0285      * Quote a raw string.
0286      *
0287      * @param string $value     Raw string
0288      * @return string           Quoted string
0289      */
0290     protected function _quote($value)
0291     {
0292         if (is_int($value) || is_float($value)) {
0293             return $value;
0294         }
0295         $this->_connect();
0296         return $this->_connection->quote($value);
0297     }
0298 
0299     /**
0300      * Begin a transaction.
0301      */
0302     protected function _beginTransaction()
0303     {
0304         $this->_connect();
0305         $this->_connection->beginTransaction();
0306     }
0307 
0308     /**
0309      * Commit a transaction.
0310      */
0311     protected function _commit()
0312     {
0313         $this->_connect();
0314         $this->_connection->commit();
0315     }
0316 
0317     /**
0318      * Roll-back a transaction.
0319      */
0320     protected function _rollBack() {
0321         $this->_connect();
0322         $this->_connection->rollBack();
0323     }
0324 
0325     /**
0326      * Set the PDO fetch mode.
0327      *
0328      * @todo Support FETCH_CLASS and FETCH_INTO.
0329      *
0330      * @param int $mode A PDO fetch mode.
0331      * @return void
0332      * @throws Zend_Db_Adapter_Exception
0333      */
0334     public function setFetchMode($mode)
0335     {
0336         //check for PDO extension
0337         if (!extension_loaded('pdo')) {
0338             /**
0339              * @see Zend_Db_Adapter_Exception
0340              */
0341             // require_once 'Zend/Db/Adapter/Exception.php';
0342             throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
0343         }
0344         switch ($mode) {
0345             case PDO::FETCH_LAZY:
0346             case PDO::FETCH_ASSOC:
0347             case PDO::FETCH_NUM:
0348             case PDO::FETCH_BOTH:
0349             case PDO::FETCH_NAMED:
0350             case PDO::FETCH_OBJ:
0351                 $this->_fetchMode = $mode;
0352                 break;
0353             default:
0354                 /**
0355                  * @see Zend_Db_Adapter_Exception
0356                  */
0357                 // require_once 'Zend/Db/Adapter/Exception.php';
0358                 throw new Zend_Db_Adapter_Exception("Invalid fetch mode '$mode' specified");
0359                 break;
0360         }
0361     }
0362 
0363     /**
0364      * Check if the adapter supports real SQL parameters.
0365      *
0366      * @param string $type 'positional' or 'named'
0367      * @return bool
0368      */
0369     public function supportsParameters($type)
0370     {
0371         switch ($type) {
0372             case 'positional':
0373             case 'named':
0374             default:
0375                 return true;
0376         }
0377     }
0378 
0379     /**
0380      * Retrieve server version in PHP style
0381      *
0382      * @return string
0383      */
0384     public function getServerVersion()
0385     {
0386         $this->_connect();
0387         try {
0388             $version = $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
0389         } catch (PDOException $e) {
0390             // In case of the driver doesn't support getting attributes
0391             return null;
0392         }
0393         $matches = null;
0394         if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
0395             return $matches[1];
0396         } else {
0397             return null;
0398         }
0399     }
0400 }