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
![]() |
|||||||
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
|
|||||||
140 | $errors = sqlsrv_errors(); |
||||||
141 | |||||||
142 | foreach ($errors as $error) { |
||||||
143 | $this->error($error["code"], $error["message"]); |
||||||
0 ignored issues
–
show
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
![]() |
|||||||
144 | } |
||||||
145 | |||||||
146 | throw new Exception\InvalidQueryException($error["message"], $error["code"]); |
||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||||
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
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
![]() 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
![]() |
|||||||
176 | $this->numFields = sqlsrv_num_fields($this->result); |
||||||
0 ignored issues
–
show
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
![]() |
|||||||
177 | |||||||
178 | if ($isInsertStm || $isUpdateStm || $isDeleteStm) { |
||||||
179 | $this->rowsAffected = sqlsrv_rows_affected($this->result); |
||||||
0 ignored issues
–
show
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
![]() |
|||||||
180 | } |
||||||
181 | |||||||
182 | if ($this->transac_mode) { |
||||||
183 | $this->transac_result = is_null($this->transac_result) |
||||||
0 ignored issues
–
show
|
|||||||
184 | ? $this->result |
||||||
185 | : $this->transac_result && $this->result; |
||||||
186 | } |
||||||
187 | |||||||
188 | return $this->result; |
||||||
0 ignored issues
–
show
|
|||||||
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 |