File indexing completed on 2025-01-19 05:21:02

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 Statement
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  * @see Zend_Db_Statement
0025  */
0026 // require_once 'Zend/Db/Statement.php';
0027 
0028 /**
0029  * Extends for Microsoft SQL Server Driver for PHP
0030  *
0031  * @category   Zend
0032  * @package    Zend_Db
0033  * @subpackage Statement
0034  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0035  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0036  */
0037 class Zend_Db_Statement_Sqlsrv extends Zend_Db_Statement
0038 {
0039 
0040     /**
0041      * The connection_stmt object original string.
0042      */
0043     protected $_originalSQL;
0044 
0045     /**
0046      * Column names.
0047      */
0048     protected $_keys;
0049 
0050     /**
0051      * Query executed
0052      */
0053     protected $_executed = false;
0054 
0055     /**
0056      * Prepares statement handle
0057      *
0058      * @param string $sql
0059      * @return void
0060      * @throws Zend_Db_Statement_Sqlsrv_Exception
0061      */
0062     protected function _prepare($sql)
0063     {
0064         $connection = $this->_adapter->getConnection();
0065 
0066         $this->_stmt = sqlsrv_prepare($connection, $sql);
0067 
0068         if (!$this->_stmt) {
0069             // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0070             throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
0071         }
0072 
0073         $this->_originalSQL = $sql;
0074     }
0075 
0076     /**
0077      * Binds a parameter to the specified variable name.
0078      *
0079      * @param mixed $parameter Name the parameter, either integer or string.
0080      * @param mixed $variable  Reference to PHP variable containing the value.
0081      * @param mixed $type      OPTIONAL Datatype of SQL parameter.
0082      * @param mixed $length    OPTIONAL Length of SQL parameter.
0083      * @param mixed $options   OPTIONAL Other options.
0084      * @return bool
0085      * @throws Zend_Db_Statement_Exception
0086      */
0087     protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
0088     {
0089         //Sql server doesn't support bind by name
0090         return true;
0091     }
0092 
0093     /**
0094      * Closes the cursor, allowing the statement to be executed again.
0095      *
0096      * @return bool
0097      */
0098     public function closeCursor()
0099     {
0100         if (!$this->_stmt) {
0101             return false;
0102         }
0103 
0104         sqlsrv_free_stmt($this->_stmt);
0105         $this->_stmt = false;
0106         return true;
0107     }
0108 
0109     /**
0110      * Returns the number of columns in the result set.
0111      * Returns null if the statement has no result set metadata.
0112      *
0113      * @return int The number of columns.
0114      */
0115     public function columnCount()
0116     {
0117         if ($this->_stmt && $this->_executed) {
0118             return sqlsrv_num_fields($this->_stmt);
0119         }
0120 
0121         return 0;
0122     }
0123 
0124 
0125     /**
0126      * Retrieves the error code, if any, associated with the last operation on
0127      * the statement handle.
0128      *
0129      * @return string error code.
0130      */
0131     public function errorCode()
0132     {
0133         if (!$this->_stmt) {
0134             return false;
0135         }
0136 
0137         $error = sqlsrv_errors();
0138         if (!$error) {
0139             return false;
0140         }
0141 
0142         return $error[0]['code'];
0143     }
0144 
0145 
0146     /**
0147      * Retrieves an array of error information, if any, associated with the
0148      * last operation on the statement handle.
0149      *
0150      * @return array
0151      */
0152     public function errorInfo()
0153     {
0154         if (!$this->_stmt) {
0155             return false;
0156         }
0157 
0158         $error = sqlsrv_errors();
0159         if (!$error) {
0160             return false;
0161         }
0162 
0163         return array(
0164             $error[0]['code'],
0165             $error[0]['message'],
0166         );
0167     }
0168 
0169 
0170     /**
0171      * Executes a prepared statement.
0172      *
0173      * @param array $params OPTIONAL Values to bind to parameter placeholders.
0174      * @return bool
0175      * @throws Zend_Db_Statement_Exception
0176      */
0177     public function _execute(array $params = null)
0178     {
0179         $connection = $this->_adapter->getConnection();
0180         if (!$this->_stmt) {
0181             return false;
0182         }
0183 
0184         if ($params !== null) {
0185             if (!is_array($params)) {
0186                 $params = array($params);
0187             }
0188             $error = false;
0189 
0190             // make all params passed by reference
0191             $params_ = array();
0192             $temp    = array();
0193             $i       = 1;
0194             foreach ($params as $param) {
0195                 $temp[$i]  = $param;
0196                 $params_[] = &$temp[$i];
0197                 $i++;
0198             }
0199             $params = $params_;
0200         }
0201 
0202         $this->_stmt = sqlsrv_query($connection, $this->_originalSQL, $params);
0203 
0204         if (!$this->_stmt) {
0205             // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0206             throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
0207         }
0208 
0209         $this->_executed = true;
0210 
0211         return (!$this->_stmt);
0212     }
0213 
0214     /**
0215      * Fetches a row from the result set.
0216      *
0217      * @param  int $style  OPTIONAL Fetch mode for this fetch operation.
0218      * @param  int $cursor OPTIONAL Absolute, relative, or other.
0219      * @param  int $offset OPTIONAL Number for absolute or relative cursors.
0220      * @return mixed Array, object, or scalar depending on fetch mode.
0221      * @throws Zend_Db_Statement_Exception
0222      */
0223     public function fetch($style = null, $cursor = null, $offset = null)
0224     {
0225         if (!$this->_stmt) {
0226             return false;
0227         }
0228 
0229         if (null === $style) {
0230             $style = $this->_fetchMode;
0231         }
0232 
0233         $values = sqlsrv_fetch_array($this->_stmt, SQLSRV_FETCH_ASSOC);
0234 
0235         if (!$values && (null !== $error = sqlsrv_errors())) {
0236             // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0237             throw new Zend_Db_Statement_Sqlsrv_Exception($error);
0238         }
0239 
0240         if (null === $values) {
0241             return null;
0242         }
0243 
0244         if (!$this->_keys) {
0245             foreach ($values as $key => $value) {
0246                 $this->_keys[] = $this->_adapter->foldCase($key);
0247             }
0248         }
0249 
0250         $values = array_values($values);
0251 
0252         $row = false;
0253         switch ($style) {
0254             case Zend_Db::FETCH_NUM:
0255                 $row = $values;
0256                 break;
0257             case Zend_Db::FETCH_ASSOC:
0258                 $row = array_combine($this->_keys, $values);
0259                 break;
0260             case Zend_Db::FETCH_BOTH:
0261                 $assoc = array_combine($this->_keys, $values);
0262                 $row   = array_merge($values, $assoc);
0263                 break;
0264             case Zend_Db::FETCH_OBJ:
0265                 $row = (object) array_combine($this->_keys, $values);
0266                 break;
0267             case Zend_Db::FETCH_BOUND:
0268                 $assoc = array_combine($this->_keys, $values);
0269                 $row   = array_merge($values, $assoc);
0270                 $row   = $this->_fetchBound($row);
0271                 break;
0272             default:
0273                 // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0274                 throw new Zend_Db_Statement_Sqlsrv_Exception("Invalid fetch mode '$style' specified");
0275                 break;
0276         }
0277 
0278         return $row;
0279     }
0280 
0281     /**
0282      * Returns a single column from the next row of a result set.
0283      *
0284      * @param int $col OPTIONAL Position of the column to fetch.
0285      * @return string
0286      * @throws Zend_Db_Statement_Exception
0287      */
0288     public function fetchColumn($col = 0)
0289     {
0290         if (!$this->_stmt) {
0291             return false;
0292         }
0293 
0294         if (!sqlsrv_fetch($this->_stmt)) {
0295             if (null !== $error = sqlsrv_errors()) {
0296                 // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0297                 throw new Zend_Db_Statement_Sqlsrv_Exception($error);
0298             }
0299 
0300             // If no error, there is simply no record
0301             return false;
0302         }
0303 
0304         $data = sqlsrv_get_field($this->_stmt, $col); //0-based
0305         if ($data === false) {
0306             // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0307             throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
0308         }
0309 
0310         return $data;
0311     }
0312 
0313     /**
0314      * Fetches the next row and returns it as an object.
0315      *
0316      * @param string $class  OPTIONAL Name of the class to create.
0317      * @param array  $config OPTIONAL Constructor arguments for the class.
0318      * @return mixed One object instance of the specified class.
0319      * @throws Zend_Db_Statement_Exception
0320      */
0321     public function fetchObject($class = 'stdClass', array $config = array())
0322     {
0323         if (!$this->_stmt) {
0324             return false;
0325         }
0326 
0327         $obj = sqlsrv_fetch_object($this->_stmt);
0328 
0329         if ($error = sqlsrv_errors()) {
0330             // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0331             throw new Zend_Db_Statement_Sqlsrv_Exception($error);
0332         }
0333 
0334         /* @todo XXX handle parameters */
0335 
0336         if (null === $obj) {
0337             return false;
0338         }
0339 
0340         return $obj;
0341     }
0342 
0343     /**
0344      * Returns metadata for a column in a result set.
0345      *
0346      * @param int $column
0347      * @return mixed
0348      * @throws Zend_Db_Statement_Sqlsrv_Exception
0349      */
0350     public function getColumnMeta($column)
0351     {
0352         $fields = sqlsrv_field_metadata($this->_stmt);
0353 
0354         if (!$fields) {
0355             throw new Zend_Db_Statement_Sqlsrv_Exception('Column metadata can not be fetched');
0356         }
0357 
0358         if (!isset($fields[$column])) {
0359             throw new Zend_Db_Statement_Sqlsrv_Exception('Column index does not exist in statement');
0360         }
0361 
0362         return $fields[$column];
0363     }
0364 
0365     /**
0366      * Retrieves the next rowset (result set) for a SQL statement that has
0367      * multiple result sets.  An example is a stored procedure that returns
0368      * the results of multiple queries.
0369      *
0370      * @return bool
0371      * @throws Zend_Db_Statement_Exception
0372      */
0373     public function nextRowset()
0374     {
0375         if (sqlsrv_next_result($this->_stmt) === false) {
0376             // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0377             throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
0378         }
0379         
0380         // reset column keys
0381         $this->_keys = null;
0382 
0383         return true;
0384     }
0385 
0386     /**
0387      * Returns the number of rows affected by the execution of the
0388      * last INSERT, DELETE, or UPDATE statement executed by this
0389      * statement object.
0390      *
0391      * @return int     The number of rows affected.
0392      * @throws Zend_Db_Statement_Exception
0393      */
0394     public function rowCount()
0395     {
0396         if (!$this->_stmt) {
0397             return false;
0398         }
0399 
0400         if (!$this->_executed) {
0401             return 0;
0402         }
0403 
0404         $num_rows = sqlsrv_rows_affected($this->_stmt);
0405 
0406         // Strict check is necessary; 0 is a valid return value
0407         if ($num_rows === false) {
0408             // require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
0409             throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
0410         }
0411 
0412         return $num_rows;
0413     }
0414     
0415     /**
0416      * Returns an array containing all of the result set rows.
0417      *
0418      * @param int $style OPTIONAL Fetch mode.
0419      * @param int $col   OPTIONAL Column number, if fetch mode is by column.
0420      * @return array Collection of rows, each in a format by the fetch mode.
0421      *
0422      * Behaves like parent, but if limit()
0423      * is used, the final result removes the extra column
0424      * 'zend_db_rownum'
0425      */
0426     public function fetchAll($style = null, $col = null)
0427     {
0428         $data = parent::fetchAll($style, $col);
0429         $results = array();
0430         $remove = $this->_adapter->foldCase('ZEND_DB_ROWNUM');
0431 
0432         foreach ($data as $row) {
0433             if (is_array($row) && array_key_exists($remove, $row)) {
0434                 unset($row[$remove]);
0435             }
0436             $results[] = $row;
0437         }
0438         return $results;
0439     }
0440 }