File indexing completed on 2024-06-16 05:29:58

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  * @see Zend_Db_Adapter_Abstract
0025  */
0026 // require_once 'Zend/Db/Adapter/Abstract.php';
0027 
0028 /**
0029  * @see Zend_Db_Statement_Sqlsrv
0030  */
0031 // require_once 'Zend/Db/Statement/Sqlsrv.php';
0032 
0033 /**
0034  * @category   Zend
0035  * @package    Zend_Db
0036  * @subpackage Adapter
0037  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0038  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0039  */
0040 class Zend_Db_Adapter_Sqlsrv extends Zend_Db_Adapter_Abstract
0041 {
0042     /**
0043      * User-provided configuration.
0044      *
0045      * Basic keys are:
0046      *
0047      * username => (string) Connect to the database as this username.
0048      * password => (string) Password associated with the username.
0049      * dbname   => The name of the local SQL Server instance
0050      *
0051      * @var array
0052      */
0053     protected $_config = array(
0054         'dbname'       => null,
0055         'username'     => null,
0056         'password'     => null,
0057     );
0058 
0059     /**
0060      * Last insert id from INSERT query
0061      *
0062      * @var int
0063      */
0064     protected $_lastInsertId;
0065 
0066     /**
0067      * Query used to fetch last insert id
0068      *
0069      * @var string
0070      */
0071     protected $_lastInsertSQL = 'SELECT SCOPE_IDENTITY() as Current_Identity';
0072 
0073     /**
0074      * Keys are UPPERCASE SQL datatypes or the constants
0075      * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE.
0076      *
0077      * Values are:
0078      * 0 = 32-bit integer
0079      * 1 = 64-bit integer
0080      * 2 = float or decimal
0081      *
0082      * @var array Associative array of datatypes to values 0, 1, or 2.
0083      */
0084     protected $_numericDataTypes = array(
0085         Zend_Db::INT_TYPE    => Zend_Db::INT_TYPE,
0086         Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
0087         Zend_Db::FLOAT_TYPE  => Zend_Db::FLOAT_TYPE,
0088         'INT'                => Zend_Db::INT_TYPE,
0089         'SMALLINT'           => Zend_Db::INT_TYPE,
0090         'TINYINT'            => Zend_Db::INT_TYPE,
0091         'BIGINT'             => Zend_Db::BIGINT_TYPE,
0092         'DECIMAL'            => Zend_Db::FLOAT_TYPE,
0093         'FLOAT'              => Zend_Db::FLOAT_TYPE,
0094         'MONEY'              => Zend_Db::FLOAT_TYPE,
0095         'NUMERIC'            => Zend_Db::FLOAT_TYPE,
0096         'REAL'               => Zend_Db::FLOAT_TYPE,
0097         'SMALLMONEY'         => Zend_Db::FLOAT_TYPE,
0098     );
0099 
0100     /**
0101      * Default class name for a DB statement.
0102      *
0103      * @var string
0104      */
0105     protected $_defaultStmtClass = 'Zend_Db_Statement_Sqlsrv';
0106 
0107     /**
0108      * Creates a connection resource.
0109      *
0110      * @return void
0111      * @throws Zend_Db_Adapter_Sqlsrv_Exception
0112      */
0113     protected function _connect()
0114     {
0115         if (is_resource($this->_connection)) {
0116             // connection already exists
0117             return;
0118         }
0119 
0120         if (!extension_loaded('sqlsrv')) {
0121             /**
0122              * @see Zend_Db_Adapter_Sqlsrv_Exception
0123              */
0124             // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0125             throw new Zend_Db_Adapter_Sqlsrv_Exception('The Sqlsrv extension is required for this adapter but the extension is not loaded');
0126         }
0127 
0128         $serverName = $this->_config['host'];
0129         if (isset($this->_config['port'])) {
0130             $port        = (integer) $this->_config['port'];
0131             $serverName .= ', ' . $port;
0132         }
0133 
0134         $connectionInfo = array(
0135             'Database' => $this->_config['dbname'],
0136         );
0137 
0138         if (isset($this->_config['username']) && isset($this->_config['password']))
0139         {
0140             $connectionInfo += array(
0141                 'UID'      => $this->_config['username'],
0142                 'PWD'      => $this->_config['password'],
0143             );
0144         }
0145         // else - windows authentication
0146 
0147         if (!empty($this->_config['driver_options'])) {
0148             foreach ($this->_config['driver_options'] as $option => $value) {
0149                 // A value may be a constant.
0150                 if (is_string($value)) {
0151                     $constantName = strtoupper($value);
0152                     if (defined($constantName)) {
0153                         $connectionInfo[$option] = constant($constantName);
0154                     } else {
0155                         $connectionInfo[$option] = $value;
0156                     }
0157                 }
0158             }
0159         }
0160 
0161         $this->_connection = sqlsrv_connect($serverName, $connectionInfo);
0162 
0163         if (!$this->_connection) {
0164             /**
0165              * @see Zend_Db_Adapter_Sqlsrv_Exception
0166              */
0167             // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0168             throw new Zend_Db_Adapter_Sqlsrv_Exception(sqlsrv_errors());
0169         }
0170     }
0171 
0172     /**
0173      * Check for config options that are mandatory.
0174      * Throw exceptions if any are missing.
0175      *
0176      * @param array $config
0177      * @throws Zend_Db_Adapter_Exception
0178      */
0179     protected function _checkRequiredOptions(array $config)
0180     {
0181         // we need at least a dbname
0182         if (! array_key_exists('dbname', $config)) {
0183             /** @see Zend_Db_Adapter_Exception */
0184             // require_once 'Zend/Db/Adapter/Exception.php';
0185             throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'dbname' that names the database instance");
0186         }
0187 
0188         if (! array_key_exists('password', $config) && array_key_exists('username', $config)) {
0189             /**
0190              * @see Zend_Db_Adapter_Exception
0191              */
0192             // require_once 'Zend/Db/Adapter/Exception.php';
0193             throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'password' for login credentials.
0194                                                 If Windows Authentication is desired, both keys 'username' and 'password' should be ommited from config.");
0195         }
0196 
0197         if (array_key_exists('password', $config) && !array_key_exists('username', $config)) {
0198             /**
0199              * @see Zend_Db_Adapter_Exception
0200              */
0201             // require_once 'Zend/Db/Adapter/Exception.php';
0202             throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'username' for login credentials.
0203                                                 If Windows Authentication is desired, both keys 'username' and 'password' should be ommited from config.");
0204         }
0205     }
0206 
0207     /**
0208      * Set the transaction isoltion level.
0209      *
0210      * @param integer|null $level A fetch mode from SQLSRV_TXN_*.
0211      * @return true
0212      * @throws Zend_Db_Adapter_Sqlsrv_Exception
0213      */
0214     public function setTransactionIsolationLevel($level = null)
0215     {
0216         $this->_connect();
0217         $sql = null;
0218 
0219         // Default transaction level in sql server
0220         if ($level === null)
0221         {
0222             $level = SQLSRV_TXN_READ_COMMITTED;
0223         }
0224 
0225         switch ($level) {
0226             case SQLSRV_TXN_READ_UNCOMMITTED:
0227                 $sql = "READ UNCOMMITTED";
0228                 break;
0229             case SQLSRV_TXN_READ_COMMITTED:
0230                 $sql = "READ COMMITTED";
0231                 break;
0232             case SQLSRV_TXN_REPEATABLE_READ:
0233                 $sql = "REPEATABLE READ";
0234                 break;
0235             case SQLSRV_TXN_SNAPSHOT:
0236                 $sql = "SNAPSHOT";
0237                 break;
0238             case SQLSRV_TXN_SERIALIZABLE:
0239                 $sql = "SERIALIZABLE";
0240                 break;
0241             default:
0242                 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0243                 throw new Zend_Db_Adapter_Sqlsrv_Exception("Invalid transaction isolation level mode '$level' specified");
0244         }
0245 
0246         if (!sqlsrv_query($this->_connection, "SET TRANSACTION ISOLATION LEVEL $sql;")) {
0247             // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0248             throw new Zend_Db_Adapter_Sqlsrv_Exception("Transaction cannot be changed to '$level'");
0249         }
0250 
0251         return true;
0252     }
0253 
0254     /**
0255      * Test if a connection is active
0256      *
0257      * @return boolean
0258      */
0259     public function isConnected()
0260     {
0261         return (is_resource($this->_connection)
0262                 && (get_resource_type($this->_connection) == 'SQL Server Connection')
0263         );
0264     }
0265 
0266     /**
0267      * Force the connection to close.
0268      *
0269      * @return void
0270      */
0271     public function closeConnection()
0272     {
0273         if ($this->isConnected()) {
0274             sqlsrv_close($this->_connection);
0275         }
0276         $this->_connection = null;
0277     }
0278 
0279     /**
0280      * Returns an SQL statement for preparation.
0281      *
0282      * @param string $sql The SQL statement with placeholders.
0283      * @return Zend_Db_Statement_Sqlsrv
0284      */
0285     public function prepare($sql)
0286     {
0287         $this->_connect();
0288         $stmtClass = $this->_defaultStmtClass;
0289 
0290         if (!class_exists($stmtClass)) {
0291             /**
0292              * @see Zend_Loader
0293              */
0294             // require_once 'Zend/Loader.php';
0295             Zend_Loader::loadClass($stmtClass);
0296         }
0297 
0298         $stmt = new $stmtClass($this, $sql);
0299         $stmt->setFetchMode($this->_fetchMode);
0300         return $stmt;
0301     }
0302 
0303     /**
0304      * Quote a raw string.
0305      *
0306      * @param string $value     Raw string
0307      * @return string           Quoted string
0308      */
0309     protected function _quote($value)
0310     {
0311         if (is_int($value)) {
0312             return $value;
0313         } elseif (is_float($value)) {
0314             return sprintf('%F', $value);
0315         }
0316 
0317         $value = addcslashes($value, "\000\032");
0318         return "'" . str_replace("'", "''", $value) . "'";
0319     }
0320 
0321     /**
0322      * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
0323      *
0324      * As a convention, on RDBMS brands that support sequences
0325      * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
0326      * from the arguments and returns the last id generated by that sequence.
0327      * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
0328      * returns the last value generated for such a column, and the table name
0329      * argument is disregarded.
0330      *
0331      * @param string $tableName   OPTIONAL Name of table.
0332      * @param string $primaryKey  OPTIONAL Name of primary key column.
0333      * @return string
0334      */
0335     public function lastInsertId($tableName = null, $primaryKey = null)
0336     {
0337         if ($tableName) {
0338             $tableName = $this->quote($tableName);
0339             $sql       = 'SELECT IDENT_CURRENT (' . $tableName . ') as Current_Identity';
0340             return (string) $this->fetchOne($sql);
0341         }
0342 
0343         if ($this->_lastInsertId > 0) {
0344             return (string) $this->_lastInsertId;
0345         }
0346 
0347         $sql = $this->_lastInsertSQL;
0348         return (string) $this->fetchOne($sql);
0349     }
0350 
0351     /**
0352      * Inserts a table row with specified data.
0353      *
0354      * @param mixed $table The table to insert data into.
0355      * @param array $bind Column-value pairs.
0356      * @return int The number of affected rows.
0357      */
0358     public function insert($table, array $bind)
0359     {
0360         // extract and quote col names from the array keys
0361         $cols = array();
0362         $vals = array();
0363         foreach ($bind as $col => $val) {
0364             $cols[] = $this->quoteIdentifier($col, true);
0365             if ($val instanceof Zend_Db_Expr) {
0366                 $vals[] = $val->__toString();
0367                 unset($bind[$col]);
0368             } else {
0369                 $vals[] = '?';
0370             }
0371         }
0372 
0373         // build the statement
0374         $sql = "INSERT INTO "
0375              . $this->quoteIdentifier($table, true)
0376              . ' (' . implode(', ', $cols) . ') '
0377              . 'VALUES (' . implode(', ', $vals) . ')'
0378              . ' ' . $this->_lastInsertSQL;
0379 
0380         // execute the statement and return the number of affected rows
0381         $stmt   = $this->query($sql, array_values($bind));
0382         $result = $stmt->rowCount();
0383 
0384         $stmt->nextRowset();
0385 
0386         $this->_lastInsertId = $stmt->fetchColumn();
0387 
0388         return $result;
0389     }
0390 
0391     /**
0392      * Returns a list of the tables in the database.
0393      *
0394      * @return array
0395      */
0396     public function listTables()
0397     {
0398         $this->_connect();
0399         $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
0400         return $this->fetchCol($sql);
0401     }
0402 
0403     /**
0404      * Returns the column descriptions for a table.
0405      *
0406      * The return value is an associative array keyed by the column name,
0407      * as returned by the RDBMS.
0408      *
0409      * The value of each array element is an associative array
0410      * with the following keys:
0411      *
0412      * SCHEMA_NAME      => string; name of schema
0413      * TABLE_NAME       => string;
0414      * COLUMN_NAME      => string; column name
0415      * COLUMN_POSITION  => number; ordinal position of column in table
0416      * DATA_TYPE        => string; SQL datatype name of column
0417      * DEFAULT          => string; default expression of column, null if none
0418      * NULLABLE         => boolean; true if column can have nulls
0419      * LENGTH           => number; length of CHAR/VARCHAR
0420      * SCALE            => number; scale of NUMERIC/DECIMAL
0421      * PRECISION        => number; precision of NUMERIC/DECIMAL
0422      * UNSIGNED         => boolean; unsigned property of an integer type
0423      * PRIMARY          => boolean; true if column is part of the primary key
0424      * PRIMARY_POSITION => integer; position of column in primary key
0425      * IDENTITY         => integer; true if column is auto-generated with unique values
0426      *
0427      * @todo Discover integer unsigned property.
0428      *
0429      * @param string $tableName
0430      * @param string $schemaName OPTIONAL
0431      * @return array
0432      */
0433     public function describeTable($tableName, $schemaName = null)
0434     {
0435         /**
0436          * Discover metadata information about this table.
0437          */
0438         $sql    = "exec sp_columns @table_name = " . $this->quoteIdentifier($tableName, true);
0439         $stmt   = $this->query($sql);
0440         $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
0441         
0442         // ZF-7698
0443         $stmt->closeCursor();
0444 
0445         if (count($result) == 0) {
0446             return array();
0447         }
0448 
0449         $owner           = 1;
0450         $table_name      = 2;
0451         $column_name     = 3;
0452         $type_name       = 5;
0453         $precision       = 6;
0454         $length          = 7;
0455         $scale           = 8;
0456         $nullable        = 10;
0457         $column_def      = 12;
0458         $column_position = 16;
0459 
0460         /**
0461          * Discover primary key column(s) for this table.
0462          */
0463         $tableOwner = $result[0][$owner];
0464         $sql        = "exec sp_pkeys @table_owner = " . $tableOwner
0465                     . ", @table_name = " . $this->quoteIdentifier($tableName, true);
0466         $stmt       = $this->query($sql);
0467 
0468         $primaryKeysResult = $stmt->fetchAll(Zend_Db::FETCH_NUM);
0469         $primaryKeyColumn  = array();
0470 
0471         // Per http://msdn.microsoft.com/en-us/library/ms189813.aspx,
0472         // results from sp_keys stored procedure are:
0473         // 0=TABLE_QUALIFIER 1=TABLE_OWNER 2=TABLE_NAME 3=COLUMN_NAME 4=KEY_SEQ 5=PK_NAME
0474 
0475         $pkey_column_name = 3;
0476         $pkey_key_seq     = 4;
0477         foreach ($primaryKeysResult as $pkeysRow) {
0478             $primaryKeyColumn[$pkeysRow[$pkey_column_name]] = $pkeysRow[$pkey_key_seq];
0479         }
0480 
0481         $desc = array();
0482         $p    = 1;
0483         foreach ($result as $key => $row) {
0484             $identity = false;
0485             $words    = explode(' ', $row[$type_name], 2);
0486             if (isset($words[0])) {
0487                 $type = $words[0];
0488                 if (isset($words[1])) {
0489                     $identity = (bool) preg_match('/identity/', $words[1]);
0490                 }
0491             }
0492 
0493             $isPrimary = array_key_exists($row[$column_name], $primaryKeyColumn);
0494             if ($isPrimary) {
0495                 $primaryPosition = $primaryKeyColumn[$row[$column_name]];
0496             } else {
0497                 $primaryPosition = null;
0498             }
0499 
0500             $desc[$this->foldCase($row[$column_name])] = array(
0501                 'SCHEMA_NAME'      => null, // @todo
0502                 'TABLE_NAME'       => $this->foldCase($row[$table_name]),
0503                 'COLUMN_NAME'      => $this->foldCase($row[$column_name]),
0504                 'COLUMN_POSITION'  => (int) $row[$column_position],
0505                 'DATA_TYPE'        => $type,
0506                 'DEFAULT'          => $row[$column_def],
0507                 'NULLABLE'         => (bool) $row[$nullable],
0508                 'LENGTH'           => $row[$length],
0509                 'SCALE'            => $row[$scale],
0510                 'PRECISION'        => $row[$precision],
0511                 'UNSIGNED'         => null, // @todo
0512                 'PRIMARY'          => $isPrimary,
0513                 'PRIMARY_POSITION' => $primaryPosition,
0514                 'IDENTITY'         => $identity,
0515             );
0516         }
0517 
0518         return $desc;
0519     }
0520 
0521     /**
0522      * Leave autocommit mode and begin a transaction.
0523      *
0524      * @return void
0525      * @throws Zend_Db_Adapter_Sqlsrv_Exception
0526      */
0527     protected function _beginTransaction()
0528     {
0529         if (!sqlsrv_begin_transaction($this->_connection)) {
0530             // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0531             throw new Zend_Db_Adapter_Sqlsrv_Exception(sqlsrv_errors());
0532         }
0533     }
0534 
0535     /**
0536      * Commit a transaction and return to autocommit mode.
0537      *
0538      * @return void
0539      * @throws Zend_Db_Adapter_Sqlsrv_Exception
0540      */
0541     protected function _commit()
0542     {
0543         if (!sqlsrv_commit($this->_connection)) {
0544             // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0545             throw new Zend_Db_Adapter_Sqlsrv_Exception(sqlsrv_errors());
0546         }
0547     }
0548 
0549     /**
0550      * Roll back a transaction and return to autocommit mode.
0551      *
0552      * @return void
0553      * @throws Zend_Db_Adapter_Sqlsrv_Exception
0554      */
0555     protected function _rollBack()
0556     {
0557         if (!sqlsrv_rollback($this->_connection)) {
0558             // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0559             throw new Zend_Db_Adapter_Sqlsrv_Exception(sqlsrv_errors());
0560         }
0561     }
0562 
0563     /**
0564      * Set the fetch mode.
0565      *
0566      * @todo Support FETCH_CLASS and FETCH_INTO.
0567      *
0568      * @param integer $mode A fetch mode.
0569      * @return void
0570      * @throws Zend_Db_Adapter_Sqlsrv_Exception
0571      */
0572     public function setFetchMode($mode)
0573     {
0574         switch ($mode) {
0575             case Zend_Db::FETCH_NUM:   // seq array
0576             case Zend_Db::FETCH_ASSOC: // assoc array
0577             case Zend_Db::FETCH_BOTH:  // seq+assoc array
0578             case Zend_Db::FETCH_OBJ:   // object
0579                 $this->_fetchMode = $mode;
0580                 break;
0581             case Zend_Db::FETCH_BOUND: // bound to PHP variable
0582                 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0583                 throw new Zend_Db_Adapter_Sqlsrv_Exception('FETCH_BOUND is not supported yet');
0584                 break;
0585             default:
0586                 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
0587                 throw new Zend_Db_Adapter_Sqlsrv_Exception("Invalid fetch mode '$mode' specified");
0588                 break;
0589         }
0590     }
0591 
0592     /**
0593      * Adds an adapter-specific LIMIT clause to the SELECT statement.
0594      *
0595      * @param string $sql
0596      * @param integer $count
0597      * @param integer $offset OPTIONAL
0598      * @return string
0599      * @throws Zend_Db_Adapter_Sqlsrv_Exception
0600      */
0601      public function limit($sql, $count, $offset = 0)
0602      {
0603         $count = intval($count);
0604         if ($count <= 0) {
0605             // require_once 'Zend/Db/Adapter/Exception.php';
0606             throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
0607         }
0608 
0609         $offset = intval($offset);
0610         if ($offset < 0) {
0611             /** @see Zend_Db_Adapter_Exception */
0612             // require_once 'Zend/Db/Adapter/Exception.php';
0613             throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
0614         }
0615 
0616         if ($offset == 0) {
0617             $sql = preg_replace('/^SELECT\s/i', 'SELECT TOP ' . $count . ' ', $sql);
0618         } else {
0619             $orderby = stristr($sql, 'ORDER BY');
0620 
0621             if (!$orderby) {
0622                 $over = 'ORDER BY (SELECT 0)';
0623             } else {
0624                 $over = preg_replace('/\"[^,]*\".\"([^,]*)\"/i', '"inner_tbl"."$1"', $orderby);
0625             }
0626 
0627             // Remove ORDER BY clause from $sql
0628             $sql = preg_replace('/\s+ORDER BY(.*)/', '', $sql);
0629 
0630             // Add ORDER BY clause as an argument for ROW_NUMBER()
0631             $sql = "SELECT ROW_NUMBER() OVER ($over) AS \"ZEND_DB_ROWNUM\", * FROM ($sql) AS inner_tbl";
0632 
0633             $start = $offset + 1;
0634 
0635             if ($count == PHP_INT_MAX) {
0636                 $sql = "WITH outer_tbl AS ($sql) SELECT * FROM outer_tbl WHERE \"ZEND_DB_ROWNUM\" >= $start";
0637             }
0638             else {
0639                 $end = $offset + $count;
0640                 $sql = "WITH outer_tbl AS ($sql) SELECT * FROM outer_tbl WHERE \"ZEND_DB_ROWNUM\" BETWEEN $start AND $end";
0641             }
0642         }
0643 
0644         return $sql;
0645     }
0646 
0647     /**
0648      * Check if the adapter supports real SQL parameters.
0649      *
0650      * @param string $type 'positional' or 'named'
0651      * @return bool
0652      */
0653     public function supportsParameters($type)
0654     {
0655         if ($type == 'positional') {
0656             return true;
0657         }
0658 
0659         // if its 'named' or anything else
0660         return false;
0661     }
0662 
0663     /**
0664      * Retrieve server version in PHP style
0665      *
0666      * @return string
0667      */
0668     public function getServerVersion()
0669     {
0670         $this->_connect();
0671         $serverInfo = sqlsrv_server_info($this->_connection);
0672 
0673         if ($serverInfo !== false) {
0674             return $serverInfo['SQLServerVersion'];
0675         }
0676 
0677         return null;
0678     }
0679 }