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 | * MySQL class |
||||||
15 | * |
||||||
16 | * This is a database driver class to connect to MySQL |
||||||
17 | */ |
||||||
18 | class MySQL extends AbstractDriver implements DriverInterface |
||||||
19 | { |
||||||
20 | /** |
||||||
21 | * {@inheritdoc} |
||||||
22 | * |
||||||
23 | * @var object |
||||||
24 | */ |
||||||
25 | protected $dbconn; |
||||||
26 | |||||||
27 | /** |
||||||
28 | * {@inheritdoc} |
||||||
29 | * |
||||||
30 | * @param array $options |
||||||
31 | */ |
||||||
32 | 21 | public function __construct($options) |
|||||
33 | { |
||||||
34 | 21 | $this->driverName = 'Mysqli'; |
|||||
35 | |||||||
36 | 21 | if (!array_key_exists("dbchar", $options)) { |
|||||
37 | $options["dbchar"] = "utf8"; |
||||||
38 | } |
||||||
39 | |||||||
40 | 21 | parent::__construct($options); |
|||||
41 | |||||||
42 | 21 | $auto_connect = array_key_exists('auto_connect', $options) ? $options["auto_connect"] : true; |
|||||
43 | |||||||
44 | 21 | if ($auto_connect) { |
|||||
45 | 11 | $this->connect(); |
|||||
46 | } |
||||||
47 | 21 | } |
|||||
48 | |||||||
49 | /** |
||||||
50 | * Connects to database |
||||||
51 | * |
||||||
52 | * @throws RuntimeException |
||||||
53 | * @throws Exception\ConnectionException |
||||||
54 | * |
||||||
55 | * @return \mysqli |
||||||
56 | */ |
||||||
57 | 18 | public function connect() |
|||||
58 | { |
||||||
59 | 18 | if (!extension_loaded('mysqli')) { |
|||||
60 | throw new \RuntimeException("The Mysqli extension is not loaded"); |
||||||
61 | } |
||||||
62 | |||||||
63 | 18 | if (!is_null($this->dbport) && !empty($this->dbport)) { |
|||||
0 ignored issues
–
show
introduced
by
![]() |
|||||||
64 | 18 | $conn = @new \mysqli($this->dbhost, $this->dbuser, $this->dbpass, $this->dbname, $this->dbport); |
|||||
65 | } else { |
||||||
66 | $conn = @new \mysqli($this->dbhost, $this->dbuser, $this->dbpass, $this->dbname); |
||||||
67 | } |
||||||
68 | |||||||
69 | 18 | if ($conn->connect_errno) { |
|||||
70 | /* |
||||||
71 | * Use ever mysqli_connect_errno() and mysqli_connect_error() |
||||||
72 | * over $this->dbconn->errno and $this->dbconn->error to prevent |
||||||
73 | * the warning message "Property access is not allowed yet". |
||||||
74 | */ |
||||||
75 | 2 | throw new Exception\ConnectionException(mysqli_connect_error(), mysqli_connect_errno()); |
|||||
76 | } else { |
||||||
77 | 16 | $this->dbconn = $conn; |
|||||
78 | 16 | $this->dbconn->set_charset($this->dbchar); |
|||||
79 | } |
||||||
80 | |||||||
81 | 16 | return $this->dbconn; |
|||||
82 | } |
||||||
83 | |||||||
84 | /** |
||||||
85 | * Excecutes a statement |
||||||
86 | * |
||||||
87 | * @param string $sql |
||||||
88 | * @param array $params |
||||||
89 | * |
||||||
90 | * @throws RuntimeException |
||||||
91 | * @throws Exception\InvalidQueryException |
||||||
92 | * |
||||||
93 | * @return \mysqli_result |
||||||
94 | */ |
||||||
95 | 23 | public function execute($sql, array $params = []) |
|||||
96 | { |
||||||
97 | 23 | $this->numRows = 0; |
|||||
98 | 23 | $this->numFields = 0; |
|||||
99 | 23 | $this->rowsAffected = 0; |
|||||
100 | |||||||
101 | 23 | $this->arrayResult = null; |
|||||
102 | |||||||
103 | # Bound variables |
||||||
104 | 23 | if (count($params)) { |
|||||
105 | 9 | $this->result = $stmt = @$this->dbconn->prepare($sql); |
|||||
106 | |||||||
107 | 9 | if (!$stmt) { |
|||||
108 | $this->error($this->dbconn->errno, $this->dbconn->error); |
||||||
0 ignored issues
–
show
The method
error() does not exist on Drone\Db\Driver\MySQL . 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
![]() |
|||||||
109 | throw new Exception\InvalidQueryException($this->dbconn->error, $this->dbconn->errno); |
||||||
110 | } |
||||||
111 | |||||||
112 | 9 | $param_values = array_values($params); |
|||||
113 | |||||||
114 | 9 | $n_params = count($param_values); |
|||||
115 | 9 | $bind_values = []; |
|||||
116 | 9 | $bind_types = ""; |
|||||
117 | |||||||
118 | 9 | for ($i = 0; $i < $n_params; $i++) { |
|||||
119 | 9 | if (is_string($param_values[$i])) { |
|||||
120 | 5 | $bind_types .= 's'; |
|||||
121 | 9 | } elseif (is_float($param_values[$i])) { |
|||||
122 | $bind_types .= 'd'; |
||||||
123 | } else { |
||||||
124 | # [POSSIBLE BUG] - To Future revision (What about non-string and non-decimal types ?) |
||||||
125 | 9 | $bind_types .= 's'; |
|||||
126 | } |
||||||
127 | |||||||
128 | 9 | $bind_values[] = '$param_values[' . $i . ']'; |
|||||
129 | } |
||||||
130 | |||||||
131 | 9 | $values = implode(', ', $bind_values); |
|||||
132 | 9 | eval('$stmt->bind_param(\'' . $bind_types . '\', ' . $values . ');'); |
|||||
0 ignored issues
–
show
|
|||||||
133 | |||||||
134 | 9 | $r = $stmt->execute(); |
|||||
135 | } else { |
||||||
136 | 16 | $prev_error_handler = set_error_handler(['\Drone\Error\ErrorHandler', 'errorControlOperator'], E_ALL); |
|||||
137 | |||||||
138 | // may be throw a Fatal error (Ex: Maximum execution time) |
||||||
139 | 16 | $r = $this->result = $this->dbconn->query($sql); |
|||||
140 | |||||||
141 | 16 | set_error_handler($prev_error_handler); |
|||||
142 | } |
||||||
143 | |||||||
144 | 23 | if (!$r) { |
|||||
145 | 3 | $this->error($this->dbconn->errno, $this->dbconn->error); |
|||||
146 | 3 | throw new Exception\InvalidQueryException($this->dbconn->error, $this->dbconn->errno); |
|||||
147 | } |
||||||
148 | |||||||
149 | 21 | $is_stmt_result = is_object($this->result) && get_class($this->result) == 'mysqli_stmt'; |
|||||
150 | |||||||
151 | 21 | if ($is_stmt_result) { |
|||||
152 | 9 | $this->rowsAffected = $this->result->affected_rows; |
|||||
153 | |||||||
154 | 9 | $res = $this->result->get_result(); |
|||||
155 | |||||||
156 | /* |
||||||
157 | * if $res is false then there aren't results. |
||||||
158 | * It is useful to prevent rollback transactions on insert statements because |
||||||
159 | * insert statement do not free results. |
||||||
160 | */ |
||||||
161 | 9 | if ($res) { |
|||||
162 | 2 | $this->result = $res; |
|||||
163 | } |
||||||
164 | } |
||||||
165 | |||||||
166 | # identify SELECT, SHOW, DESCRIBE or EXPLAIN queries |
||||||
167 | 21 | if (is_object($this->result) && property_exists($this->result, 'num_rows')) { |
|||||
168 | 16 | $this->numRows = $this->result->num_rows; |
|||||
169 | } else { |
||||||
170 | 13 | if (property_exists($this->dbconn, 'affected_rows') && !$is_stmt_result) { |
|||||
171 | 13 | $this->rowsAffected = $this->dbconn->affected_rows; |
|||||
172 | } |
||||||
173 | } |
||||||
174 | |||||||
175 | # affected_rows return the same of num_rows on select statements! |
||||||
176 | 21 | if ($this->numRows > 0) { |
|||||
177 | 9 | $this->rowsAffected = 0; |
|||||
178 | } |
||||||
179 | |||||||
180 | 21 | if (property_exists($this->dbconn, 'field_count')) { |
|||||
181 | 21 | $this->numFields = $this->dbconn->field_count; |
|||||
182 | } |
||||||
183 | |||||||
184 | 21 | if ($this->transac_mode) { |
|||||
185 | 2 | $this->transac_result = is_null($this->transac_result) |
|||||
0 ignored issues
–
show
|
|||||||
186 | 2 | ? $this->result |
|||||
187 | 1 | : $this->transac_result && $this->result; |
|||||
188 | } |
||||||
189 | /* |
||||||
190 | * Because mysqli_query() returns FALSE on failure, a mysqli_result object for |
||||||
191 | * SELECT, SHOW, DESCRIBE or EXPLAIN queries, and TRUE for other successful queries, |
||||||
192 | * it should be handled to return only objects or resources. |
||||||
193 | * |
||||||
194 | * Ref: http://php.net/manual/en/mysqli.query.php |
||||||
195 | */ |
||||||
196 | 21 | return is_bool($this->result) ? $this->dbconn : $this->result; |
|||||
197 | } |
||||||
198 | |||||||
199 | /** |
||||||
200 | * {@inheritdoc} |
||||||
201 | */ |
||||||
202 | 4 | public function commit() |
|||||
203 | { |
||||||
204 | 4 | return $this->dbconn->commit(); |
|||||
205 | } |
||||||
206 | |||||||
207 | /** |
||||||
208 | * {@inheritdoc} |
||||||
209 | */ |
||||||
210 | 2 | public function rollback() |
|||||
211 | { |
||||||
212 | 2 | return $this->dbconn->rollback(); |
|||||
213 | } |
||||||
214 | |||||||
215 | /** |
||||||
216 | * {@inheritdoc} |
||||||
217 | */ |
||||||
218 | 3 | public function disconnect() |
|||||
219 | { |
||||||
220 | 3 | parent::disconnect(); |
|||||
221 | |||||||
222 | 2 | if ($this->dbconn->close()) { |
|||||
223 | 2 | $this->dbconn = null; |
|||||
224 | |||||||
225 | 2 | return true; |
|||||
226 | } |
||||||
227 | |||||||
228 | return false; |
||||||
229 | } |
||||||
230 | |||||||
231 | /** |
||||||
232 | * {@inheritdoc} |
||||||
233 | */ |
||||||
234 | 4 | public function autocommit($value) |
|||||
235 | { |
||||||
236 | 4 | parent::autocommit($value); |
|||||
237 | 4 | $this->dbconn->autocommit($value); |
|||||
238 | 4 | } |
|||||
239 | |||||||
240 | /** |
||||||
241 | * Returns an array with the rows fetched |
||||||
242 | * |
||||||
243 | * @throws LogicException |
||||||
244 | * |
||||||
245 | * @return array |
||||||
246 | */ |
||||||
247 | 10 | protected function toArray() |
|||||
248 | { |
||||||
249 | 10 | $data = []; |
|||||
250 | |||||||
251 | 10 | if ($this->result && !is_bool($this->result)) { |
|||||
252 | 9 | while ($row = $this->result->fetch_array(MYSQLI_BOTH)) { |
|||||
253 | 9 | $data[] = $row; |
|||||
254 | } |
||||||
255 | } else { # This error is thrown because of 'execute' method has not been executed. |
||||||
256 | 1 | throw new \LogicException('There are not data in the buffer!'); |
|||||
257 | } |
||||||
258 | |||||||
259 | 9 | $this->arrayResult = $data; |
|||||
260 | |||||||
261 | 9 | return $data; |
|||||
262 | } |
||||||
263 | |||||||
264 | /** |
||||||
265 | * By default __destruct() disconnects to database |
||||||
266 | * |
||||||
267 | * @return null |
||||||
268 | */ |
||||||
269 | 18 | public function __destruct() |
|||||
270 | { |
||||||
271 | # prevent "Property access is not allowed yet" with @ on failure connections |
||||||
272 | 18 | if ($this->dbconn !== false && !is_null($this->dbconn)) { |
|||||
273 | 13 | @$this->dbconn->close(); |
|||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
close() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||||||
274 | } |
||||||
275 | 18 | } |
|||||
276 | } |
||||||
277 |