SQLServer::rollback()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * DronePHP (http://www.dronephp.com)
4
 *
5
 * @link      http://github.com/Pleets/DronePHP
6
 * @copyright Copyright (c) 2016-2018 Pleets. (http://www.pleets.org)
7
 * @license   http://www.dronephp.com/license
8
 * @author    Darío Rivera <[email protected]>
9
 */
10
11
namespace Drone\Db\Driver;
12
13
/**
14
 * SQLServer class
15
 *
16
 * This is a database driver class to connect to SQLServer
17
 */
18
class SQLServer extends AbstractDriver implements DriverInterface
19
{
20
    /**
21
     * {@inheritdoc}
22
     *
23
     * @var resource
24
     */
25
    protected $dbconn;
26
27
    /**
28
     * {@inheritdoc}
29
     *
30
     * @param array $options
31
     */
32
    public function __construct($options)
33
    {
34
        $this->driverName = 'Sqlsrv';
35
36
        if (!array_key_exists("dbchar", $options)) {
37
            $options["dbchar"] = "UTF-8";
38
        }
39
40
        parent::__construct($options);
41
42
        $auto_connect = array_key_exists('auto_connect', $options) ? $options["auto_connect"] : true;
43
44
        if ($auto_connect) {
45
            $this->connect();
46
        }
47
    }
48
49
    /**
50
     * Connects to database
51
     *
52
     * @throws RuntimeException
53
     * @throws Exception\ConnectionException
54
     *
55
     * @return resource
56
     */
57
    public function connect()
58
    {
59
        if (!extension_loaded('sqlsrv')) {
60
            throw new \RuntimeException("The Sqlsrv extension is not loaded");
61
        }
62
63
        if (!is_null($this->dbport) && !empty($this->dbport)) {
0 ignored issues
show
introduced by
The condition is_null($this->dbport) is always false.
Loading history...
64
            $this->dbhost .= ', ' . $this->dbport;
65
        }
66
67
        $db_info = [
68
            "Database" => $this->dbname,
69
            "UID" => $this->dbuser,
70
            "PWD" => $this->dbpass,
71
            "CharacterSet" => $this->dbchar,
72
        ];
73
        $conn = sqlsrv_connect($this->dbhost, $db_info);
74
75
        if ($conn === false) {
76
            $errors = sqlsrv_errors();
77
78
            $previousException = null;
79
80
            foreach ($errors as $error) {
81
                $previousException = new Exception\ConnectionException(
82
                    $error["message"],
83
                    $error["code"],
84
                    $previousException
85
                );
86
            }
87
88
            throw $previousException;
89
        }
90
91
        $this->dbconn = $conn;
92
93
        return $this->dbconn;
94
    }
95
96
    /**
97
     * Executes a statement
98
     *
99
     * @param string $sql
100
     * @param array $params
101
     *
102
     * @throws Exception\InvalidQueryException
103
     *
104
     * @return mixed
105
     */
106
    public function execute($sql, array $params = [])
107
    {
108
        $this->numRows = 0;
109
        $this->numFields = 0;
110
        $this->rowsAffected = 0;
111
112
        $this->arrayResult = null;
113
114
        // (/**/)
115
        $clean_code = preg_replace('/(\s)*\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\//', '', $sql);
116
117
        // (--)
118
        $clean_code = preg_replace('/(\s)*--.*\n/', "", $clean_code);
119
120
        # clean other characters starting senteces
121
        $clean_code = preg_replace('/^[\n\t\s]*/', "", $clean_code);
122
123
        # indicates if SQL is a selection statement
124
        $isSelectStm = (preg_match('/^SELECT/i', $clean_code));
125
126
        # indicates if SQL is a insert statement
127
        $isInsertStm = (preg_match('/^INSERT/i', $clean_code));
128
129
        # indicates if SQL is a insert statement
130
        $isUpdateStm = (preg_match('/^UPDATE/i', $clean_code));
131
132
        # indicates if SQL is a insert statement
133
        $isDeleteStm = (preg_match('/^DELETE/i', $clean_code));
134
135
        # Bound variables
136
        if (count($params)) {
137
            $stmt = sqlsrv_prepare($this->dbconn, $sql, $params);
138
139
            if (!$stmt) {
0 ignored issues
show
introduced by
$stmt is of type false|resource, thus it always evaluated to false.
Loading history...
140
                $errors = sqlsrv_errors();
141
142
                foreach ($errors as $error) {
143
                    $this->error($error["code"], $error["message"]);
0 ignored issues
show
Bug introduced by
The method error() does not exist on Drone\Db\Driver\SQLServer. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

143
                    $this->/** @scrutinizer ignore-call */ 
144
                           error($error["code"], $error["message"]);
Loading history...
144
                }
145
146
                throw new Exception\InvalidQueryException($error["message"], $error["code"]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $error seems to be defined by a foreach iteration on line 142. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
147
            }
148
149
            $exec = sqlsrv_execute($stmt);
150
        } else {
151
            if ($isSelectStm) {
152
                $exec = $this->result = sqlsrv_query(
153
                    $this->dbconn,
154
                    $sql,
155
                    $params,
156
                    ["Scrollable" => SQLSRV_CURSOR_KEYSET]
157
                );
158
            } else {
159
                $exec = $this->result = sqlsrv_query($this->dbconn, $sql, $params);
160
            }
161
        }
162
163
        if ($exec === false) {
164
            $errors = sqlsrv_errors();
165
166
            foreach ($errors as $error) {
167
                $this->error($error["code"], $error["message"]);
168
            }
169
170
            throw new Exception\InvalidQueryException($error["message"], $error["code"]);
171
        }
172
173
        $this->getArrayResult();
174
175
        $this->numRows = sqlsrv_has_rows($this->result) ? sqlsrv_num_rows($this->result) : $this->numRows;
0 ignored issues
show
Bug introduced by
It seems like $this->result can also be of type false; however, parameter $stmt of sqlsrv_has_rows() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
        $this->numRows = sqlsrv_has_rows(/** @scrutinizer ignore-type */ $this->result) ? sqlsrv_num_rows($this->result) : $this->numRows;
Loading history...
Bug introduced by
It seems like $this->result can also be of type false; however, parameter $stmt of sqlsrv_num_rows() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
        $this->numRows = sqlsrv_has_rows($this->result) ? sqlsrv_num_rows(/** @scrutinizer ignore-type */ $this->result) : $this->numRows;
Loading history...
176
        $this->numFields = sqlsrv_num_fields($this->result);
0 ignored issues
show
Bug introduced by
It seems like $this->result can also be of type false; however, parameter $stmt of sqlsrv_num_fields() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
        $this->numFields = sqlsrv_num_fields(/** @scrutinizer ignore-type */ $this->result);
Loading history...
177
178
        if ($isInsertStm || $isUpdateStm || $isDeleteStm) {
179
            $this->rowsAffected = sqlsrv_rows_affected($this->result);
0 ignored issues
show
Bug introduced by
It seems like $this->result can also be of type false; however, parameter $stmt of sqlsrv_rows_affected() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

179
            $this->rowsAffected = sqlsrv_rows_affected(/** @scrutinizer ignore-type */ $this->result);
Loading history...
180
        }
181
182
        if ($this->transac_mode) {
183
            $this->transac_result = is_null($this->transac_result)
0 ignored issues
show
introduced by
The condition is_null($this->transac_result) is always false.
Loading history...
184
                ? $this->result
185
                : $this->transac_result && $this->result;
186
        }
187
188
        return $this->result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->result also could return the type false which is incompatible with the return type mandated by Drone\Db\Driver\AbstractDriver::execute() of object|resource.
Loading history...
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194
    public function commit()
195
    {
196
        return sqlsrv_commit($this->dbconn);
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202
    public function rollback()
203
    {
204
        return sqlsrv_rollback($this->dbconn);
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210
    public function disconnect()
211
    {
212
        parent::disconnect();
213
214
        return sqlsrv_close($this->dbconn);
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220
    public function beginTransaction()
221
    {
222
        if (sqlsrv_begin_transaction($this->dbconn) === false) {
223
            $errors = sqlsrv_errors();
224
225
            foreach ($errors as $error) {
226
                $this->error($error["code"], $error["message"]);
227
            }
228
229
            throw new \RuntimeException("Could not begin transaction");
230
        }
231
232
        parent::beginTransaction();
233
    }
234
235
    /**
236
     * Returns an array with the rows fetched
237
     *
238
     * @throws LogicException
239
     *
240
     * @return array
241
     */
242
    protected function toArray()
243
    {
244
        $data = [];
245
246
        if ($this->result) {
247
            while ($row = sqlsrv_fetch_array($this->result)) {
248
                $data[] = $row;
249
            }
250
        } else {             # This error is thrown because of 'execute' method has not been executed.
251
            throw new \LogicException('There are not data in the buffer!');
252
        }
253
254
        $this->arrayResult = $data;
255
256
        return $data;
257
    }
258
259
    /**
260
     * By default __destruct() disconnects to database
261
     *
262
     * @return null
263
     */
264
    public function __destruct()
265
    {
266
        if ($this->dbconn) {
267
            sqlsrv_close($this->dbconn);
268
        }
269
    }
270
}
271