Total Complexity | 80 |
Total Lines | 631 |
Duplicated Lines | 0 % |
Changes | 4 | ||
Bugs | 1 | Features | 0 |
Complex classes like Connection often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Connection, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
29 | class Connection extends AbstractConnection |
||
30 | { |
||
31 | /** |
||
32 | * Connection::$isDeleteHack |
||
33 | * |
||
34 | * DELETE hack flag |
||
35 | * |
||
36 | * Whether to use the MySql "delete hack" which allows the number |
||
37 | * of affected rows to be shown. Uses a preg_replace when enabled, |
||
38 | * adding a bit more processing to all queries. |
||
39 | * |
||
40 | * @var bool |
||
41 | */ |
||
42 | public $isDeleteHack = true; |
||
43 | |||
44 | /** |
||
45 | * Connection::$platform |
||
46 | * |
||
47 | * Database driver platform name. |
||
48 | * |
||
49 | * @var string |
||
50 | */ |
||
51 | protected $platform = 'MySQL'; |
||
52 | |||
53 | /** |
||
54 | * Connection::$config |
||
55 | * |
||
56 | * Connection configurations. |
||
57 | * |
||
58 | * @var array |
||
59 | */ |
||
60 | protected $config |
||
61 | = [ |
||
62 | 'escapeCharacter' => '`', |
||
63 | 'reservedIdentifiers' => ['*'], |
||
64 | 'likeEscapeStatement' => ' ESCAPE \'%s\' ', |
||
65 | 'likeEscapeCharacter' => '!', |
||
66 | ]; |
||
67 | |||
68 | /** |
||
69 | * Connection::$handle |
||
70 | * |
||
71 | * MySqli Connection Instance. |
||
72 | * |
||
73 | * @var \mysqli |
||
74 | */ |
||
75 | protected $handle; |
||
76 | |||
77 | // ------------------------------------------------------------------------ |
||
78 | |||
79 | /** |
||
80 | * Connection::isSupported |
||
81 | * |
||
82 | * Check if the platform is supported. |
||
83 | * |
||
84 | * @return bool |
||
85 | */ |
||
86 | public function isSupported() |
||
87 | { |
||
88 | return extension_loaded('mysqli'); |
||
89 | } |
||
90 | |||
91 | // ------------------------------------------------------------------------ |
||
92 | |||
93 | /** |
||
94 | * Connection::setDatabase |
||
95 | * |
||
96 | * Set a specific database table to use. |
||
97 | * |
||
98 | * @param string $database Database name. |
||
99 | * |
||
100 | * @return static |
||
101 | */ |
||
102 | public function setDatabase($database) |
||
103 | { |
||
104 | $database = empty($database) |
||
105 | ? $this->database |
||
106 | : $database; |
||
107 | |||
108 | if ($this->handle->select_db($database)) { |
||
109 | $this->database = $database; |
||
110 | } |
||
111 | |||
112 | return $this; |
||
113 | } |
||
114 | |||
115 | // ------------------------------------------------------------------------ |
||
116 | |||
117 | /** |
||
118 | * Connection::getDatabases |
||
119 | * |
||
120 | * Get list of current connection databases. |
||
121 | * |
||
122 | * @return array Returns an array. |
||
123 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||
124 | * @throws \Psr\Cache\InvalidArgumentException |
||
125 | */ |
||
126 | public function getDatabases() |
||
127 | { |
||
128 | if (empty($this->queriesResultCache[ 'databaseNames' ])) { |
||
129 | $result = $this->query('SHOW DATABASES'); |
||
130 | |||
131 | if ($result->count()) { |
||
132 | foreach ($result as $row) { |
||
133 | |||
134 | if ( ! isset($key)) { |
||
135 | if (isset($row[ 'database' ])) { |
||
136 | $key = 'database'; |
||
137 | } elseif (isset($row[ 'Database' ])) { |
||
138 | $key = 'Database'; |
||
139 | } elseif (isset($row[ 'DATABASE' ])) { |
||
140 | $key = 'DATABASE'; |
||
141 | } else { |
||
142 | /* We have no other choice but to just get the first element's key. |
||
143 | * Due to array_shift() accepting its argument by reference, if |
||
144 | * E_STRICT is on, this would trigger a warning. So we'll have to |
||
145 | * assign it first. |
||
146 | */ |
||
147 | $key = array_keys($row); |
||
148 | $key = array_shift($key); |
||
149 | } |
||
150 | } |
||
151 | |||
152 | $this->queriesResultCache[ 'databaseNames' ][] = $row->offsetGet($key); |
||
153 | } |
||
154 | } |
||
155 | } |
||
156 | |||
157 | return $this->queriesResultCache[ 'databaseNames' ]; |
||
158 | } |
||
159 | |||
160 | // ------------------------------------------------------------------------ |
||
161 | |||
162 | /** |
||
163 | * Connection::getTables |
||
164 | * |
||
165 | * Get list of current database tables. |
||
166 | * |
||
167 | * @param bool $prefixLimit If sets TRUE the query will be limit using database table prefix. |
||
168 | * |
||
169 | * @return array Returns an array |
||
170 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||
171 | * @throws \Psr\Cache\InvalidArgumentException |
||
172 | */ |
||
173 | public function getTables($prefixLimit = false) |
||
210 | } |
||
211 | |||
212 | // ------------------------------------------------------------------------ |
||
213 | |||
214 | /** |
||
215 | * Connection::getColumns |
||
216 | * |
||
217 | * @param string $table The database table name. |
||
218 | * |
||
219 | * @return array |
||
220 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||
221 | * @throws \Psr\Cache\InvalidArgumentException |
||
222 | */ |
||
223 | public function getColumns($table) |
||
224 | { |
||
225 | $table = $this->prefixTable($table); |
||
226 | |||
227 | if (empty($this->queriesResultCache[ 'tableColumns' ][ $table ])) { |
||
228 | $result = $this->query('SHOW COLUMNS FROM ' . $this->protectIdentifiers($table, true, null, false)); |
||
229 | |||
230 | if ($result->count()) { |
||
231 | foreach ($result as $row) { |
||
232 | // Do we know from where to get the column's name? |
||
233 | if ( ! isset($key)) { |
||
234 | if (isset($row[ 'column_name' ])) { |
||
235 | $key = 'column_name'; |
||
236 | } elseif (isset($row[ 'COLUMN_NAME' ])) { |
||
237 | $key = 'COLUMN_NAME'; |
||
238 | } else { |
||
239 | /* We have no other choice but to just get the first element's key. |
||
240 | * Due to array_shift() accepting its argument by reference, if |
||
241 | * E_STRICT is on, this would trigger a warning. So we'll have to |
||
242 | * assign it first. |
||
243 | */ |
||
244 | $key = array_keys($row->getArrayCopy()); |
||
245 | $key = array_shift($key); |
||
246 | } |
||
247 | } |
||
248 | |||
249 | $this->queriesResultCache[ 'tableColumns' ][ $table ][ $row->offsetGet($key) ] = $row; |
||
250 | } |
||
251 | } |
||
252 | } |
||
253 | |||
254 | return $this->queriesResultCache[ 'tableColumns' ][ $table ]; |
||
255 | } |
||
256 | |||
257 | // ------------------------------------------------------------------------ |
||
258 | |||
259 | /** |
||
260 | * Connection::reconnect |
||
261 | * |
||
262 | * Keep or establish the connection if no queries have been sent for |
||
263 | * a length of time exceeding the server's idle timeout. |
||
264 | * |
||
265 | * @return void |
||
266 | */ |
||
267 | public function reconnect() |
||
268 | { |
||
269 | if ($this->handle !== false && $this->handle->ping() === false) { |
||
270 | $this->handle = false; |
||
271 | } |
||
272 | } |
||
273 | |||
274 | // ------------------------------------------------------------------------ |
||
275 | |||
276 | /** |
||
277 | * Connection::getAffectedRows |
||
278 | * |
||
279 | * Get the total number of affected rows from the last query execution. |
||
280 | * |
||
281 | * @return int Returns total number of affected rows |
||
282 | */ |
||
283 | public function getAffectedRows() |
||
284 | { |
||
285 | return $this->handle->affected_rows; |
||
286 | } |
||
287 | |||
288 | //-------------------------------------------------------------------- |
||
289 | |||
290 | /** |
||
291 | * Connection::getLastInsertId |
||
292 | * |
||
293 | * Get last insert id from the last insert query execution. |
||
294 | * |
||
295 | * @return int Returns total number of affected rows |
||
296 | */ |
||
297 | public function getLastInsertId() |
||
298 | { |
||
299 | return $this->handle->insert_id; |
||
300 | } |
||
301 | |||
302 | // ------------------------------------------------------------------------ |
||
303 | |||
304 | /** |
||
305 | * Connection::getLastQuery |
||
306 | * |
||
307 | * Returns the last query's statement object. |
||
308 | * |
||
309 | * @return string |
||
310 | */ |
||
311 | public function getLastQuery() |
||
312 | { |
||
313 | $last = end($this->queriesCache); |
||
314 | |||
315 | if ($last->getSqlStatement() === 'SELECT FOUND_ROWS() AS numrows;') { |
||
316 | $last = prev($this->queriesCache); |
||
317 | } |
||
318 | |||
319 | return $last; |
||
320 | } |
||
321 | |||
322 | //-------------------------------------------------------------------- |
||
323 | |||
324 | /** |
||
325 | * Connection::platformGetPlatformVersionHandler |
||
326 | * |
||
327 | * Platform getting version handler. |
||
328 | * |
||
329 | * @return SplArrayObject |
||
330 | */ |
||
331 | protected function platformGetPlatformInfoHandler() |
||
332 | { |
||
333 | $server[ 'version' ][ 'string' ] = $this->handle->server_info; |
||
334 | $server[ 'version' ][ 'number' ] = $this->handle->server_version; |
||
335 | $server[ 'stats' ] = $this->handle->get_connection_stats(); |
||
336 | |||
337 | $client[ 'version' ][ 'string' ] = $this->handle->client_info; |
||
338 | $client[ 'version' ][ 'number' ] = $this->handle->client_version; |
||
339 | $client[ 'stats' ] = mysqli_get_client_stats(); |
||
340 | |||
341 | return new SplArrayObject([ |
||
342 | 'name' => $this->getPlatform(), |
||
343 | 'host' => $this->handle->host_info, |
||
344 | 'state' => $this->handle->sqlstate, |
||
345 | 'protocol' => $this->handle->protocol_version, |
||
346 | 'server' => $server, |
||
347 | 'client' => $client, |
||
348 | ]); |
||
349 | } |
||
350 | |||
351 | // ------------------------------------------------------------------------ |
||
352 | |||
353 | /** |
||
354 | * Connection::platformConnectHandler |
||
355 | * |
||
356 | * Establish the connection. |
||
357 | * |
||
358 | * @param Config $config |
||
359 | * |
||
360 | * @return void |
||
361 | * @throws RuntimeException |
||
362 | */ |
||
363 | protected function platformConnectHandler(Config $config) |
||
364 | { |
||
365 | // Do we have a socket path? |
||
366 | if ($config->hostname[ 0 ] === '/') { |
||
367 | $hostname = null; |
||
368 | $port = null; |
||
369 | $socket = $config->hostname; |
||
370 | } else { |
||
371 | $hostname = ($config->persistent === true) |
||
372 | ? 'p:' . $config->hostname |
||
373 | : $config->hostname; |
||
374 | $port = empty($config->port) |
||
375 | ? null |
||
376 | : $config->port; |
||
377 | $socket = null; |
||
378 | } |
||
379 | |||
380 | $flags = ($config->compress === true) |
||
381 | ? MYSQLI_CLIENT_COMPRESS |
||
382 | : 0; |
||
383 | $this->handle = mysqli_init(); |
||
384 | //$this->handle->autocommit( ( $this->transactionEnable ? true : false ) ); |
||
385 | |||
386 | $this->handle->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10); |
||
387 | |||
388 | if (isset($config->strictOn)) { |
||
389 | if ($config->strictOn) { |
||
390 | $this->handle->options( |
||
391 | MYSQLI_INIT_COMMAND, |
||
392 | 'SET SESSION Sql_mode = CONCAT(@@Sql_mode, ",", "STRICT_ALL_TABLES")' |
||
393 | ); |
||
394 | } else { |
||
395 | $this->handle->options( |
||
396 | MYSQLI_INIT_COMMAND, |
||
397 | 'SET SESSION Sql_mode = |
||
398 | REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE( |
||
399 | @@Sql_mode, |
||
400 | "STRICT_ALL_TABLES,", ""), |
||
401 | ",STRICT_ALL_TABLES", ""), |
||
402 | "STRICT_ALL_TABLES", ""), |
||
403 | "STRICT_TRANS_TABLES,", ""), |
||
404 | ",STRICT_TRANS_TABLES", ""), |
||
405 | "STRICT_TRANS_TABLES", "")' |
||
406 | ); |
||
407 | } |
||
408 | } |
||
409 | |||
410 | if (is_array($config->encrypt)) { |
||
411 | $ssl = []; |
||
412 | empty($config->encrypt[ 'ssl_key' ]) OR $ssl[ 'key' ] = $config->encrypt[ 'ssl_key' ]; |
||
413 | empty($config->encrypt[ 'ssl_cert' ]) OR $ssl[ 'cert' ] = $config->encrypt[ 'ssl_cert' ]; |
||
414 | empty($config->encrypt[ 'ssl_ca' ]) OR $ssl[ 'ca' ] = $config->encrypt[ 'ssl_ca' ]; |
||
415 | empty($config->encrypt[ 'ssl_capath' ]) OR $ssl[ 'capath' ] = $config->encrypt[ 'ssl_capath' ]; |
||
416 | empty($config->encrypt[ 'ssl_cipher' ]) OR $ssl[ 'cipher' ] = $config->encrypt[ 'ssl_cipher' ]; |
||
417 | |||
418 | if ( ! empty($ssl)) { |
||
419 | if (isset($config->encrypt[ 'ssl_verify' ])) { |
||
420 | if ($config->encrypt[ 'ssl_verify' ]) { |
||
421 | defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT') |
||
422 | && $this->handle->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true); |
||
423 | } |
||
424 | // Apparently (when it exists), setting MYSQLI_OPT_SSL_VERIFY_SERVER_CERT |
||
425 | // to FALSE didn't do anything, so PHP 5.6.16 introduced yet another |
||
426 | // constant ... |
||
427 | // |
||
428 | // https://secure.php.net/ChangeLog-5.php#5.6.16 |
||
429 | // https://bugs.php.net/bug.php?id=68344 |
||
430 | elseif (defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT')) { |
||
431 | $this->handle->options(MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT, true); |
||
432 | } |
||
433 | } |
||
434 | |||
435 | $flags |= MYSQLI_CLIENT_SSL; |
||
436 | $this->handle->ssl_set( |
||
437 | isset($ssl[ 'key' ]) |
||
438 | ? $ssl[ 'key' ] |
||
439 | : null, |
||
440 | isset($ssl[ 'cert' ]) |
||
441 | ? $ssl[ 'cert' ] |
||
442 | : null, |
||
443 | isset($ssl[ 'ca' ]) |
||
444 | ? $ssl[ 'ca' ] |
||
445 | : null, |
||
446 | isset($ssl[ 'capath' ]) |
||
447 | ? $ssl[ 'capath' ] |
||
448 | : null, |
||
449 | isset($ssl[ 'cipher' ]) |
||
450 | ? $ssl[ 'cipher' ] |
||
451 | : null |
||
452 | ); |
||
453 | } |
||
454 | } |
||
455 | |||
456 | if ($this->handle->real_connect( |
||
457 | $hostname, |
||
458 | $config->username, |
||
459 | $config->password, |
||
460 | $config->database, |
||
461 | $port, |
||
462 | $socket, |
||
463 | $flags |
||
464 | ) |
||
465 | ) { |
||
466 | // Prior to version 5.7.3, MySql silently downgrades to an unencrypted connection if SSL setup fails |
||
467 | if ( |
||
468 | ($flags & MYSQLI_CLIENT_SSL) |
||
469 | AND version_compare($this->handle->client_info, '5.7.3', '<=') |
||
470 | AND empty($this->handle->query("SHOW STATUS LIKE 'ssl_cipher'") |
||
471 | ->fetch_object()->Value) |
||
472 | ) { |
||
473 | $this->handle->close(); |
||
474 | // 'MySqli was configured for an SSL connection, but got an unencrypted connection instead!'; |
||
475 | logger()->error('E_DB_CONNECTION_SSL', [$this->platform]); |
||
476 | |||
477 | if ($config->debugEnable) { |
||
478 | throw new RuntimeException('E_DB_CONNECTION_SSL'); |
||
479 | } |
||
480 | |||
481 | return; |
||
482 | } |
||
483 | |||
484 | if ( ! $this->handle->set_charset($config->charset)) { |
||
485 | // "Database: Unable to set the configured connection charset ('{$this->charset}')." |
||
486 | logger()->error('E_DB_CONNECTION_CHARSET', [$config->charset]); |
||
487 | $this->handle->close(); |
||
488 | |||
489 | if ($config->debugEnable) { |
||
490 | // 'Unable to set client connection character set: ' . $this->charset |
||
491 | throw new RuntimeException('E_DB_CONNECTION_CHARSET', [$config->charset]); |
||
492 | } |
||
493 | } |
||
494 | } |
||
495 | } |
||
496 | |||
497 | // ------------------------------------------------------------------------ |
||
498 | |||
499 | /** |
||
500 | * Connection::disconnectHandler |
||
501 | * |
||
502 | * Driver dependent way method for closing the connection. |
||
503 | * |
||
504 | * @return mixed |
||
505 | */ |
||
506 | protected function platformDisconnectHandler() |
||
507 | { |
||
508 | $this->handle->close(); |
||
509 | } |
||
510 | |||
511 | // ------------------------------------------------------------------------ |
||
512 | |||
513 | /** |
||
514 | * Connection::executeHandler |
||
515 | * |
||
516 | * Driver dependent way method for execute the Sql statement. |
||
517 | * |
||
518 | * @param Query\Statement $statement Query object. |
||
519 | * |
||
520 | * @return bool |
||
521 | */ |
||
522 | protected function platformExecuteHandler(Query\Statement &$statement) |
||
523 | { |
||
524 | if (false !== $this->handle->query($statement->getSqlFinalStatement())) { |
||
525 | return true; |
||
526 | } |
||
527 | |||
528 | // Set query error information |
||
529 | $statement->addError($this->handle->errno, $this->handle->error); |
||
530 | |||
531 | return false; |
||
532 | |||
533 | } |
||
534 | |||
535 | // ------------------------------------------------------------------------ |
||
536 | |||
537 | /** |
||
538 | * Connection::platformQueryHandler |
||
539 | * |
||
540 | * Driver dependent way method for execute the Sql statement. |
||
541 | * |
||
542 | * @param Query\Statement $statement Query object. |
||
543 | * |
||
544 | * @return array |
||
545 | */ |
||
546 | protected function platformQueryHandler(Query\Statement &$statement) |
||
557 | } |
||
558 | |||
559 | //-------------------------------------------------------------------- |
||
560 | |||
561 | /** |
||
562 | * Connection::platformTransactionBeginHandler |
||
563 | * |
||
564 | * Platform beginning a transaction handler. |
||
565 | * |
||
566 | * @return bool |
||
567 | */ |
||
568 | protected function platformTransactionBeginHandler() |
||
569 | { |
||
570 | if ($this->transactionInProgress === false) { |
||
571 | // Begin transaction using autocommit function set to false |
||
572 | $this->handle->autocommit(false); |
||
573 | |||
574 | // Flag for there is a transaction progress |
||
575 | $this->transactionInProgress = true; |
||
576 | |||
577 | // Flag for error checking |
||
578 | $this->transactionStatus = true; |
||
579 | } |
||
580 | |||
581 | |||
582 | return $this->transactionInProgress; |
||
583 | } |
||
584 | |||
585 | // ------------------------------------------------------------------------ |
||
586 | |||
587 | /** |
||
588 | * Connection::platformTransactionCommitHandler |
||
589 | * |
||
590 | * Platform committing a transaction handler. |
||
591 | * |
||
592 | * @return bool |
||
593 | */ |
||
594 | protected function platformTransactionCommitHandler() |
||
595 | { |
||
596 | if ($this->transactionStatus === true) { |
||
597 | $this->handle->commit(); |
||
598 | $this->handle->autocommit(true); |
||
599 | $this->transactionInProgress = false; |
||
600 | |||
601 | return true; |
||
602 | } |
||
603 | |||
604 | return false; |
||
605 | } |
||
606 | |||
607 | // ------------------------------------------------------------------------ |
||
608 | |||
609 | /** |
||
610 | * Connection::platformTransactionRollBackHandler |
||
611 | * |
||
612 | * Platform rolling back a transaction handler. |
||
613 | * |
||
614 | * @return void |
||
615 | */ |
||
616 | protected function platformTransactionRollBackHandler() |
||
621 | } |
||
622 | |||
623 | // ------------------------------------------------------------------------ |
||
624 | |||
625 | /** |
||
626 | * Connection::prepareSqlStatement |
||
627 | * |
||
628 | * Platform preparing a Sql statement. |
||
629 | * |
||
630 | * @param string $sqlStatement Sql Statement to be prepared. |
||
631 | * @param array $options Preparing Sql statement options. |
||
632 | * |
||
633 | * @return string |
||
634 | */ |
||
635 | protected function platformPrepareSqlStatement($sqlStatement, array $options = []) |
||
644 | } |
||
645 | |||
646 | // ------------------------------------------------------------------------ |
||
647 | |||
648 | /** |
||
649 | * Connection::platformEscapeStringHandler |
||
650 | * |
||
651 | * Platform escape string handler. |
||
652 | * |
||
653 | * @param string $string |
||
654 | * |
||
655 | * @return string |
||
656 | */ |
||
657 | protected function platformEscapeStringHandler($string) |
||
660 | } |
||
661 | } |
||
662 |