Completed
Pull Request — master (#31)
by Damian
02:13
created

SQLServerConnector::connect()   C

Complexity

Conditions 12
Paths 96

Size

Total Lines 37
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 37
rs 5.1612
cc 12
eloc 21
nc 96
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SilverStripe\MSSQL;
4
5
6
use SilverStripe\ORM\Connect\DBConnector;
7
8
9
10
/**
11
 * Database connector driver for sqlsrv_ library
12
 *
13
 * @package mssql
14
 */
15
class SQLServerConnector extends DBConnector
16
{
17
18
    /**
19
     * Connection to the DBMS.
20
     *
21
     * @var resource
22
     */
23
    protected $dbConn = null;
24
25
    /**
26
     * Stores the affected rows of the last query.
27
     * Used by sqlsrv functions only, as sqlsrv_rows_affected
28
     * accepts a result instead of a database handle.
29
     *
30
     * @var integer
31
     */
32
    protected $lastAffectedRows;
33
34
    /**
35
     * Name of the currently selected database
36
     *
37
     * @var string
38
     */
39
    protected $selectedDatabase = null;
40
41
    public function connect($parameters, $selectDB = false)
42
    {
43
        // Disable default warnings as errors behaviour for sqlsrv to keep it in line with mssql functions
44
        if (ini_get('sqlsrv.WarningsReturnAsErrors')) {
45
            ini_set('sqlsrv.WarningsReturnAsErrors', 'Off');
46
        }
47
48
        $charset = isset($parameters['charset']) ? $parameters : 'UTF-8';
49
        $multiResultSets = isset($parameters['multipleactiveresultsets'])
50
                ? $parameters['multipleactiveresultsets']
51
                : true;
52
        $options = array(
53
            'CharacterSet' => $charset,
54
            'MultipleActiveResultSets' => $multiResultSets
55
        );
56
57
        if (!(defined('MSSQL_USE_WINDOWS_AUTHENTICATION') && MSSQL_USE_WINDOWS_AUTHENTICATION == true)
58
            && empty($parameters['windowsauthentication'])
59
        ) {
60
            $options['UID'] = $parameters['username'];
61
            $options['PWD'] = $parameters['password'];
62
        }
63
64
        // Required by MS Azure database
65
        if ($selectDB && !empty($parameters['database'])) {
66
            $options['Database'] = $parameters['database'];
67
        }
68
69
        $this->dbConn = sqlsrv_connect($parameters['server'], $options);
70
71
        if (empty($this->dbConn)) {
72
            $this->databaseError("Couldn't connect to SQL Server database");
73
        } elseif ($selectDB && !empty($parameters['database'])) {
74
            // Check selected database (Azure)
75
            $this->selectedDatabase = $parameters['database'];
76
        }
77
    }
78
79
    /**
80
     * Start transaction. READ ONLY not supported.
81
     */
82
    public function transactionStart()
83
    {
84
        $result = sqlsrv_begin_transaction($this->dbConn);
85
        if (!$result) {
86
            $this->databaseError("Couldn't start the transaction.");
87
        }
88
    }
89
90
    /**
91
     * Commit everything inside this transaction so far
92
     */
93
    public function transactionEnd()
94
    {
95
        $result = sqlsrv_commit($this->dbConn);
96
        if (!$result) {
97
            $this->databaseError("Couldn't commit the transaction.");
98
        }
99
    }
100
101
    /**
102
     * Rollback or revert to a savepoint if your queries encounter problems
103
     * If you encounter a problem at any point during a transaction, you may
104
     * need to rollback that particular query, or return to a savepoint
105
     */
106
    public function transactionRollback()
107
    {
108
        $result = sqlsrv_rollback($this->dbConn);
109
        if (!$result) {
110
            $this->databaseError("Couldn't rollback the transaction.");
111
        }
112
    }
113
114
    public function affectedRows()
115
    {
116
        return $this->lastAffectedRows;
117
    }
118
119
    public function getLastError()
120
    {
121
        $errorMessages = array();
122
        $errors = sqlsrv_errors();
123
        if ($errors) {
124
            foreach ($errors as $info) {
125
                $errorMessages[] = implode(', ', array($info['SQLSTATE'], $info['code'], $info['message']));
126
            }
127
        }
128
        return implode('; ', $errorMessages);
129
    }
130
131
    public function isActive()
132
    {
133
        return $this->dbConn && $this->selectedDatabase;
134
    }
135
136
    public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR)
137
    {
138
        // Reset state
139
        $this->lastAffectedRows = 0;
140
141
        // Run query
142
        $parsedParameters = $this->parameterValues($parameters);
143
        if (empty($parsedParameters)) {
144
            $handle = sqlsrv_query($this->dbConn, $sql);
145
        } else {
146
            $handle = sqlsrv_query($this->dbConn, $sql, $parsedParameters);
147
        }
148
149
        // Check for error
150
        if (!$handle) {
151
            $this->databaseError($this->getLastError(), $errorLevel, $sql, $parsedParameters);
152
            return null;
153
        }
154
155
        // Report result
156
        $this->lastAffectedRows = sqlsrv_rows_affected($handle);
157
        return new SQLServerQuery($this, $handle);
158
    }
159
160
    public function query($sql, $errorLevel = E_USER_ERROR)
161
    {
162
        return $this->preparedQuery($sql, array(), $errorLevel);
163
    }
164
165
    public function selectDatabase($name)
166
    {
167
        $this->query("USE \"$name\"");
168
        $this->selectedDatabase = $name;
169
        return true;
170
    }
171
172
    public function __destruct()
173
    {
174
        if (is_resource($this->dbConn)) {
175
            sqlsrv_close($this->dbConn);
176
        }
177
    }
178
179
    public function getVersion()
180
    {
181
        // @todo - use sqlsrv_server_info?
182
        return trim($this->query("SELECT CONVERT(char(15), SERVERPROPERTY('ProductVersion'))")->value());
183
    }
184
185
    public function getGeneratedID($table)
186
    {
187
        return $this->query("SELECT IDENT_CURRENT('$table')")->value();
188
    }
189
190
    public function getSelectedDatabase()
191
    {
192
        return $this->selectedDatabase;
193
    }
194
195
    public function unloadDatabase()
196
    {
197
        $this->selectDatabase('Master');
198
        $this->selectedDatabase = null;
199
    }
200
201
    /**
202
     * Quotes a string, including the "N" prefix so unicode
203
     * strings are saved to the database correctly.
204
     *
205
     * @param string $value String to be encoded
206
     * @return string Processed string ready for DB
207
     */
208
    public function quoteString($value)
209
    {
210
        return "N'" . $this->escapeString($value) . "'";
211
    }
212
213
    public function escapeString($value)
214
    {
215
        $value = str_replace("'", "''", $value);
216
        $value = str_replace("\0", "[NULL]", $value);
217
        return $value;
218
    }
219
}
220