[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Part of the Joomla Framework Database Package 4 * 5 * @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved. 6 * @license GNU General Public License version 2 or later; see LICENSE 7 */ 8 9 namespace Joomla\Database\Mysqli; 10 11 use Joomla\Database\DatabaseDriver; 12 use Joomla\Database\DatabaseEvents; 13 use Joomla\Database\Event\ConnectionEvent; 14 use Joomla\Database\Exception\ConnectionFailureException; 15 use Joomla\Database\Exception\ExecutionFailureException; 16 use Joomla\Database\Exception\PrepareStatementFailureException; 17 use Joomla\Database\Exception\UnsupportedAdapterException; 18 use Joomla\Database\StatementInterface; 19 use Joomla\Database\UTF8MB4SupportInterface; 20 21 /** 22 * MySQLi Database Driver 23 * 24 * @link https://www.php.net/manual/en/book.mysqli.php 25 * @since 1.0 26 */ 27 class MysqliDriver extends DatabaseDriver implements UTF8MB4SupportInterface 28 { 29 /** 30 * The database connection resource. 31 * 32 * @var \mysqli 33 * @since 1.0 34 */ 35 protected $connection; 36 37 /** 38 * The name of the database driver. 39 * 40 * @var string 41 * @since 1.0 42 */ 43 public $name = 'mysqli'; 44 45 /** 46 * The character(s) used to quote SQL statement names such as table names or field names, etc. 47 * 48 * If a single character string the same character is used for both sides of the quoted name, else the first character will be used for the 49 * opening quote and the second for the closing quote. 50 * 51 * @var string 52 * @since 1.0 53 */ 54 protected $nameQuote = '`'; 55 56 /** 57 * The null or zero representation of a timestamp for the database driver. 58 * 59 * @var string 60 * @since 1.0 61 */ 62 protected $nullDate = '0000-00-00 00:00:00'; 63 64 /** 65 * True if the database engine supports UTF-8 Multibyte (utf8mb4) character encoding. 66 * 67 * @var boolean 68 * @since 1.4.0 69 */ 70 protected $utf8mb4 = false; 71 72 /** 73 * True if the database engine is MariaDB. 74 * 75 * @var boolean 76 * @since 2.0.0 77 */ 78 protected $mariadb = false; 79 80 /** 81 * The minimum supported MySQL database version. 82 * 83 * @var string 84 * @since 1.0 85 */ 86 protected static $dbMinimum = '5.6'; 87 88 /** 89 * The minimum supported MariaDB database version. 90 * 91 * @var string 92 * @since 2.0.0 93 */ 94 protected static $dbMinMariadb = '10.0'; 95 96 /** 97 * Constructor. 98 * 99 * @param array $options List of options used to configure the connection 100 * 101 * @since 1.0 102 */ 103 public function __construct(array $options) 104 { 105 /** 106 * sql_mode to MySql 5.7.8+ default strict mode minus ONLY_FULL_GROUP_BY 107 * 108 * @link https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-8.html#mysqld-5-7-8-sql-mode 109 */ 110 $sqlModes = [ 111 'STRICT_TRANS_TABLES', 112 'ERROR_FOR_DIVISION_BY_ZERO', 113 'NO_ENGINE_SUBSTITUTION', 114 ]; 115 116 // Get some basic values from the options. 117 $options['host'] = $options['host'] ?? 'localhost'; 118 $options['user'] = $options['user'] ?? 'root'; 119 $options['password'] = $options['password'] ?? ''; 120 $options['database'] = $options['database'] ?? ''; 121 $options['select'] = isset($options['select']) ? (bool) $options['select'] : true; 122 $options['port'] = isset($options['port']) ? (int) $options['port'] : null; 123 $options['socket'] = $options['socket'] ?? null; 124 $options['utf8mb4'] = isset($options['utf8mb4']) ? (bool) $options['utf8mb4'] : false; 125 $options['sqlModes'] = isset($options['sqlModes']) ? (array) $options['sqlModes'] : $sqlModes; 126 $options['ssl'] = isset($options['ssl']) ? $options['ssl'] : []; 127 128 if ($options['ssl'] !== []) 129 { 130 $options['ssl']['enable'] = isset($options['ssl']['enable']) ? $options['ssl']['enable'] : false; 131 $options['ssl']['cipher'] = isset($options['ssl']['cipher']) ? $options['ssl']['cipher'] : null; 132 $options['ssl']['ca'] = isset($options['ssl']['ca']) ? $options['ssl']['ca'] : null; 133 $options['ssl']['capath'] = isset($options['ssl']['capath']) ? $options['ssl']['capath'] : null; 134 $options['ssl']['key'] = isset($options['ssl']['key']) ? $options['ssl']['key'] : null; 135 $options['ssl']['cert'] = isset($options['ssl']['cert']) ? $options['ssl']['cert'] : null; 136 $options['ssl']['verify_server_cert'] = isset($options['ssl']['verify_server_cert']) ? $options['ssl']['verify_server_cert'] : null; 137 } 138 139 // Finalize initialisation. 140 parent::__construct($options); 141 } 142 143 /** 144 * Connects to the database if needed. 145 * 146 * @return void Returns void if the database connected successfully. 147 * 148 * @since 1.0 149 * @throws \RuntimeException 150 */ 151 public function connect() 152 { 153 if ($this->connection) 154 { 155 return; 156 } 157 158 // Make sure the MySQLi extension for PHP is installed and enabled. 159 if (!static::isSupported()) 160 { 161 throw new UnsupportedAdapterException('The MySQLi extension is not available'); 162 } 163 164 /* 165 * Unlike mysql_connect(), mysqli_connect() takes the port and socket as separate arguments. Therefore, we 166 * have to extract them from the host string. 167 */ 168 $port = isset($this->options['port']) ? $this->options['port'] : 3306; 169 170 if (preg_match('/^unix:(?P<socket>[^:]+)$/', $this->options['host'], $matches)) 171 { 172 // UNIX socket URI, e.g. 'unix:/path/to/unix/socket.sock' 173 $this->options['host'] = null; 174 $this->options['socket'] = $matches['socket']; 175 $this->options['port'] = null; 176 } 177 elseif (preg_match( 178 '/^(?P<host>((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(:(?P<port>.+))?$/', 179 $this->options['host'], 180 $matches 181 )) 182 { 183 // It's an IPv4 address with or without port 184 $this->options['host'] = $matches['host']; 185 186 if (!empty($matches['port'])) 187 { 188 $port = $matches['port']; 189 } 190 } 191 elseif (preg_match('/^(?P<host>\[.*\])(:(?P<port>.+))?$/', $this->options['host'], $matches)) 192 { 193 // We assume square-bracketed IPv6 address with or without port, e.g. [fe80:102::2%eth1]:3306 194 $this->options['host'] = $matches['host']; 195 196 if (!empty($matches['port'])) 197 { 198 $port = $matches['port']; 199 } 200 } 201 elseif (preg_match('/^(?P<host>(\w+:\/{2,3})?[a-z0-9\.\-]+)(:(?P<port>[^:]+))?$/i', $this->options['host'], $matches)) 202 { 203 // Named host (e.g example.com or localhost) with or without port 204 $this->options['host'] = $matches['host']; 205 206 if (!empty($matches['port'])) 207 { 208 $port = $matches['port']; 209 } 210 } 211 elseif (preg_match('/^:(?P<port>[^:]+)$/', $this->options['host'], $matches)) 212 { 213 // Empty host, just port, e.g. ':3306' 214 $this->options['host'] = 'localhost'; 215 $port = $matches['port']; 216 } 217 218 // ... else we assume normal (naked) IPv6 address, so host and port stay as they are or default 219 220 // Get the port number or socket name 221 if (is_numeric($port)) 222 { 223 $this->options['port'] = (int) $port; 224 } 225 else 226 { 227 $this->options['socket'] = $port; 228 } 229 230 $this->connection = mysqli_init(); 231 232 $connectionFlags = 0; 233 234 // For SSL/TLS connection encryption. 235 if ($this->options['ssl'] !== [] && $this->options['ssl']['enable'] === true) 236 { 237 $connectionFlags += MYSQLI_CLIENT_SSL; 238 239 // Verify server certificate is only available in PHP 5.6.16+. See https://www.php.net/ChangeLog-5.php#5.6.16 240 if (isset($this->options['ssl']['verify_server_cert'])) 241 { 242 // New constants in PHP 5.6.16+. See https://www.php.net/ChangeLog-5.php#5.6.16 243 if ($this->options['ssl']['verify_server_cert'] === true && defined('MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT')) 244 { 245 $connectionFlags += MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT; 246 } 247 elseif ($this->options['ssl']['verify_server_cert'] === false && defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT')) 248 { 249 $connectionFlags += MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; 250 } 251 elseif (defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT')) 252 { 253 $this->connection->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, $this->options['ssl']['verify_server_cert']); 254 } 255 } 256 257 // Add SSL/TLS options only if changed. 258 $this->connection->ssl_set( 259 $this->options['ssl']['key'], 260 $this->options['ssl']['cert'], 261 $this->options['ssl']['ca'], 262 $this->options['ssl']['capath'], 263 $this->options['ssl']['cipher'] 264 ); 265 } 266 267 // Attempt to connect to the server, use error suppression to silence warnings and allow us to throw an Exception separately. 268 $connected = @$this->connection->real_connect( 269 $this->options['host'], 270 $this->options['user'], 271 $this->options['password'], 272 null, 273 $this->options['port'], 274 $this->options['socket'], 275 $connectionFlags 276 ); 277 278 if (!$connected) 279 { 280 throw new ConnectionFailureException( 281 'Could not connect to database: ' . $this->connection->connect_error, 282 $this->connection->connect_errno 283 ); 284 } 285 286 // If needed, set the sql modes. 287 if ($this->options['sqlModes'] !== []) 288 { 289 $this->connection->query('SET @@SESSION.sql_mode = \'' . implode(',', $this->options['sqlModes']) . '\';'); 290 } 291 292 // And read the real sql mode to mitigate changes in mysql > 5.7.+ 293 $this->options['sqlModes'] = explode(',', $this->setQuery('SELECT @@SESSION.sql_mode;')->loadResult()); 294 295 // If auto-select is enabled select the given database. 296 if ($this->options['select'] && !empty($this->options['database'])) 297 { 298 $this->select($this->options['database']); 299 } 300 301 $this->mariadb = stripos($this->connection->server_info, 'mariadb') !== false; 302 303 $this->utf8mb4 = $this->serverClaimsUtf8mb4Support(); 304 305 // Set character sets (needed for MySQL 4.1.2+ and MariaDB). 306 $this->utf = $this->setUtf(); 307 308 $this->dispatchEvent(new ConnectionEvent(DatabaseEvents::POST_CONNECT, $this)); 309 } 310 311 /** 312 * Automatically downgrade a CREATE TABLE or ALTER TABLE query from utf8mb4 (UTF-8 Multibyte) to plain utf8. 313 * 314 * Used when the server doesn't support UTF-8 Multibyte. 315 * 316 * @param string $query The query to convert 317 * 318 * @return string The converted query 319 * 320 * @since 1.4.0 321 */ 322 public function convertUtf8mb4QueryToUtf8($query) 323 { 324 if ($this->utf8mb4) 325 { 326 return $query; 327 } 328 329 // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert 330 if (!preg_match('/^(ALTER|CREATE)\s+TABLE\s+/i', $query)) 331 { 332 return $query; 333 } 334 335 // Don't do preg replacement if string does not exist 336 if (stripos($query, 'utf8mb4') === false) 337 { 338 return $query; 339 } 340 341 // Replace utf8mb4 with utf8 if not within single or double quotes or name quotes 342 return preg_replace('/[`"\'][^`"\']*[`"\'](*SKIP)(*FAIL)|utf8mb4/i', 'utf8', $query); 343 } 344 345 /** 346 * Disconnects the database. 347 * 348 * @return void 349 * 350 * @since 1.0 351 */ 352 public function disconnect() 353 { 354 // Close the connection. 355 if (\is_callable($this->connection, 'close')) 356 { 357 $this->connection->close(); 358 } 359 360 parent::disconnect(); 361 } 362 363 /** 364 * Method to escape a string for usage in an SQL statement. 365 * 366 * @param string $text The string to be escaped. 367 * @param boolean $extra Optional parameter to provide extra escaping. 368 * 369 * @return string The escaped string. 370 * 371 * @since 1.0 372 */ 373 public function escape($text, $extra = false) 374 { 375 if (\is_int($text)) 376 { 377 return $text; 378 } 379 380 if (\is_float($text)) 381 { 382 // Force the dot as a decimal point. 383 return str_replace(',', '.', (string) $text); 384 } 385 386 $this->connect(); 387 388 $result = $this->connection->real_escape_string((string) $text); 389 390 if ($extra) 391 { 392 $result = addcslashes($result, '%_'); 393 } 394 395 return $result; 396 } 397 398 /** 399 * Test to see if the MySQLi connector is available. 400 * 401 * @return boolean True on success, false otherwise. 402 * 403 * @since 1.0 404 */ 405 public static function isSupported() 406 { 407 return \extension_loaded('mysqli'); 408 } 409 410 /** 411 * Determines if the connection to the server is active. 412 * 413 * @return boolean True if connected to the database engine. 414 * 415 * @since 1.0 416 */ 417 public function connected() 418 { 419 if (\is_object($this->connection)) 420 { 421 return $this->connection->ping(); 422 } 423 424 return false; 425 } 426 427 /** 428 * Return the query string to alter the database character set. 429 * 430 * @param string $dbName The database name 431 * 432 * @return string The query that alter the database query string 433 * 434 * @since 2.0.0 435 */ 436 public function getAlterDbCharacterSet($dbName) 437 { 438 $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; 439 440 return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' CHARACTER SET `' . $charset . '`'; 441 } 442 443 /** 444 * Method to get the database collation in use by sampling a text field of a table in the database. 445 * 446 * @return string|boolean The collation in use by the database (string) or boolean false if not supported. 447 * 448 * @since 1.0 449 * @throws \RuntimeException 450 */ 451 public function getCollation() 452 { 453 $this->connect(); 454 455 return $this->setQuery('SELECT @@collation_database;')->loadResult(); 456 } 457 458 /** 459 * Method to get the database connection collation in use by sampling a text field of a table in the database. 460 * 461 * @return string|boolean The collation in use by the database connection (string) or boolean false if not supported. 462 * 463 * @since 1.6.0 464 * @throws \RuntimeException 465 */ 466 public function getConnectionCollation() 467 { 468 $this->connect(); 469 470 return $this->setQuery('SELECT @@collation_connection;')->loadResult(); 471 } 472 473 /** 474 * Method to get the database encryption details (cipher and protocol) in use. 475 * 476 * @return string The database encryption details. 477 * 478 * @since 2.0.0 479 * @throws \RuntimeException 480 */ 481 public function getConnectionEncryption(): string 482 { 483 $this->connect(); 484 485 $variables = $this->setQuery('SHOW SESSION STATUS WHERE `Variable_name` IN (\'Ssl_version\', \'Ssl_cipher\')') 486 ->loadObjectList('Variable_name'); 487 488 if (!empty($variables['Ssl_cipher']->Value)) 489 { 490 return $variables['Ssl_version']->Value . ' (' . $variables['Ssl_cipher']->Value . ')'; 491 } 492 493 return ''; 494 } 495 496 /** 497 * Method to test if the database TLS connections encryption are supported. 498 * 499 * @return boolean Whether the database supports TLS connections encryption. 500 * 501 * @since 2.0.0 502 */ 503 public function isConnectionEncryptionSupported(): bool 504 { 505 $this->connect(); 506 507 $variables = $this->setQuery('SHOW SESSION VARIABLES WHERE `Variable_name` IN (\'have_ssl\')')->loadObjectList('Variable_name'); 508 509 return !empty($variables['have_ssl']->Value) && $variables['have_ssl']->Value === 'YES'; 510 } 511 512 /** 513 * Return the query string to create new Database. 514 * 515 * @param stdClass $options Object used to pass user and database name to database driver. This object must have "db_name" and "db_user" set. 516 * @param boolean $utf True if the database supports the UTF-8 character set. 517 * 518 * @return string The query that creates database 519 * 520 * @since 2.0.0 521 */ 522 protected function getCreateDatabaseQuery($options, $utf) 523 { 524 if ($utf) 525 { 526 $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; 527 $collation = $charset . '_unicode_ci'; 528 529 return 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' CHARACTER SET `' . $charset . '` COLLATE `' . $collation . '`'; 530 } 531 532 return 'CREATE DATABASE ' . $this->quoteName($options->db_name); 533 } 534 535 /** 536 * Shows the table CREATE statement that creates the given tables. 537 * 538 * @param mixed $tables A table name or a list of table names. 539 * 540 * @return array A list of the create SQL for the tables. 541 * 542 * @since 1.0 543 * @throws \RuntimeException 544 */ 545 public function getTableCreate($tables) 546 { 547 $this->connect(); 548 549 $result = []; 550 551 // Sanitize input to an array and iterate over the list. 552 $tables = (array) $tables; 553 554 foreach ($tables as $table) 555 { 556 // Set the query to get the table CREATE statement. 557 $row = $this->setQuery('SHOW CREATE TABLE ' . $this->quoteName($this->escape($table)))->loadRow(); 558 559 // Populate the result array based on the create statements. 560 $result[$table] = $row[1]; 561 } 562 563 return $result; 564 } 565 566 /** 567 * Retrieves field information about a given table. 568 * 569 * @param string $table The name of the database table. 570 * @param boolean $typeOnly True to only return field types. 571 * 572 * @return array An array of fields for the database table. 573 * 574 * @since 1.0 575 * @throws \RuntimeException 576 */ 577 public function getTableColumns($table, $typeOnly = true) 578 { 579 $this->connect(); 580 581 $result = []; 582 583 // Set the query to get the table fields statement. 584 $fields = $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($this->escape($table)))->loadObjectList(); 585 586 // If we only want the type as the value add just that to the list. 587 if ($typeOnly) 588 { 589 foreach ($fields as $field) 590 { 591 $result[$field->Field] = preg_replace('/[(0-9)]/', '', $field->Type); 592 } 593 } 594 else 595 { 596 // If we want the whole field data object add that to the list. 597 foreach ($fields as $field) 598 { 599 $result[$field->Field] = $field; 600 } 601 } 602 603 return $result; 604 } 605 606 /** 607 * Get the details list of keys for a table. 608 * 609 * @param string $table The name of the table. 610 * 611 * @return array An array of the column specification for the table. 612 * 613 * @since 1.0 614 * @throws \RuntimeException 615 */ 616 public function getTableKeys($table) 617 { 618 $this->connect(); 619 620 // Get the details columns information. 621 return $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table))->loadObjectList(); 622 } 623 624 /** 625 * Method to get an array of all tables in the database. 626 * 627 * @return array An array of all the tables in the database. 628 * 629 * @since 1.0 630 * @throws \RuntimeException 631 */ 632 public function getTableList() 633 { 634 $this->connect(); 635 636 // Set the query to get the tables statement. 637 return $this->setQuery('SHOW TABLES')->loadColumn(); 638 } 639 640 /** 641 * Get the version of the database connector. 642 * 643 * @return string The database connector version. 644 * 645 * @since 1.0 646 */ 647 public function getVersion() 648 { 649 $this->connect(); 650 651 if ($this->mariadb) 652 { 653 // MariaDB: Strip off any leading '5.5.5-', if present 654 return preg_replace('/^5\.5\.5-/', '', $this->connection->server_info); 655 } 656 657 return $this->connection->server_info; 658 } 659 660 /** 661 * Get the minimum supported database version. 662 * 663 * @return string 664 * 665 * @since 2.0.0 666 */ 667 public function getMinimum() 668 { 669 return $this->mariadb ? static::$dbMinMariadb : static::$dbMinimum; 670 } 671 672 /** 673 * Determine whether the database engine support the UTF-8 Multibyte (utf8mb4) character encoding. 674 * 675 * @return boolean True if the database engine supports UTF-8 Multibyte. 676 * 677 * @since 2.0.0 678 */ 679 public function hasUTF8mb4Support() 680 { 681 return $this->utf8mb4; 682 } 683 684 /** 685 * Determine if the database engine is MariaDB. 686 * 687 * @return boolean 688 * 689 * @since 2.0.0 690 */ 691 public function isMariaDb(): bool 692 { 693 $this->connect(); 694 695 return $this->mariadb; 696 } 697 698 /** 699 * Method to get the auto-incremented value from the last INSERT statement. 700 * 701 * @return mixed The value of the auto-increment field from the last inserted row. 702 * If the value is greater than maximal int value, it will return a string. 703 * 704 * @since 1.0 705 */ 706 public function insertid() 707 { 708 $this->connect(); 709 710 return $this->connection->insert_id; 711 } 712 713 /** 714 * Inserts a row into a table based on an object's properties. 715 * 716 * @param string $table The name of the database table to insert into. 717 * @param object $object A reference to an object whose public properties match the table fields. 718 * @param string $key The name of the primary key. If provided the object property is updated. 719 * 720 * @return boolean 721 * 722 * @since 2.0.0 723 * @throws \RuntimeException 724 */ 725 public function insertObject($table, &$object, $key = null) 726 { 727 $fields = []; 728 $values = []; 729 $tableColumns = $this->getTableColumns($table); 730 731 // Iterate over the object variables to build the query fields and values. 732 foreach (get_object_vars($object) as $k => $v) 733 { 734 // Skip columns that don't exist in the table. 735 if (!array_key_exists($k, $tableColumns)) 736 { 737 continue; 738 } 739 740 // Only process non-null scalars. 741 if (\is_array($v) || \is_object($v) || $v === null) 742 { 743 continue; 744 } 745 746 // Ignore any internal fields. 747 if ($k[0] === '_') 748 { 749 continue; 750 } 751 752 // Ignore null datetime fields. 753 if ($tableColumns[$k] === 'datetime' && empty($v)) 754 { 755 continue; 756 } 757 758 // Ignore null integer fields. 759 if (stristr($tableColumns[$k], 'int') !== false && $v === '') 760 { 761 continue; 762 } 763 764 // Prepare and sanitize the fields and values for the database query. 765 $fields[] = $this->quoteName($k); 766 $values[] = $this->quote($v); 767 } 768 769 // Create the base insert statement. 770 $query = $this->getQuery(true) 771 ->insert($this->quoteName($table)) 772 ->columns($fields) 773 ->values(implode(',', $values)); 774 775 // Set the query and execute the insert. 776 $this->setQuery($query)->execute(); 777 778 // Update the primary key if it exists. 779 $id = $this->insertid(); 780 781 if ($key && $id && \is_string($key)) 782 { 783 $object->$key = $id; 784 } 785 786 return true; 787 } 788 789 /** 790 * Locks a table in the database. 791 * 792 * @param string $table The name of the table to unlock. 793 * 794 * @return $this 795 * 796 * @since 1.0 797 * @throws \RuntimeException 798 */ 799 public function lockTable($table) 800 { 801 $this->executeUnpreparedQuery($this->replacePrefix('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')); 802 803 return $this; 804 } 805 806 /** 807 * Renames a table in the database. 808 * 809 * @param string $oldTable The name of the table to be renamed 810 * @param string $newTable The new name for the table. 811 * @param string $backup Not used by MySQL. 812 * @param string $prefix Not used by MySQL. 813 * 814 * @return $this 815 * 816 * @since 1.0 817 * @throws \RuntimeException 818 */ 819 public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) 820 { 821 $this->setQuery('RENAME TABLE ' . $oldTable . ' TO ' . $newTable)->execute(); 822 823 return $this; 824 } 825 826 /** 827 * Select a database for use. 828 * 829 * @param string $database The name of the database to select for use. 830 * 831 * @return boolean True if the database was successfully selected. 832 * 833 * @since 1.0 834 * @throws \RuntimeException 835 */ 836 public function select($database) 837 { 838 $this->connect(); 839 840 if (!$database) 841 { 842 return false; 843 } 844 845 if (!$this->connection->select_db($database)) 846 { 847 throw new ConnectionFailureException('Could not connect to database.'); 848 } 849 850 return true; 851 } 852 853 /** 854 * Set the connection to use UTF-8 character encoding. 855 * 856 * @return boolean True on success. 857 * 858 * @since 1.0 859 */ 860 public function setUtf() 861 { 862 // If UTF is not supported return false immediately 863 if (!$this->utf) 864 { 865 return false; 866 } 867 868 // Make sure we're connected to the server 869 $this->connect(); 870 871 // Which charset should I use, plain utf8 or multibyte utf8mb4? 872 $charset = $this->utf8mb4 && $this->options['utf8mb4'] ? 'utf8mb4' : 'utf8'; 873 874 $result = @$this->connection->set_charset($charset); 875 876 /* 877 * If I could not set the utf8mb4 charset then the server doesn't support utf8mb4 despite claiming otherwise. This happens on old MySQL 878 * server versions (less than 5.5.3) using the mysqlnd PHP driver. Since mysqlnd masks the server version and reports only its own we 879 * can not be sure if the server actually does support UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the utf8mb4 charset is 880 * undefined in this case we catch the error and determine that utf8mb4 is not supported! 881 */ 882 if (!$result && $this->utf8mb4 && $this->options['utf8mb4']) 883 { 884 $this->utf8mb4 = false; 885 $result = @$this->connection->set_charset('utf8'); 886 } 887 888 return $result; 889 } 890 891 /** 892 * Method to commit a transaction. 893 * 894 * @param boolean $toSavepoint If true, commit to the last savepoint. 895 * 896 * @return void 897 * 898 * @since 1.0 899 * @throws \RuntimeException 900 */ 901 public function transactionCommit($toSavepoint = false) 902 { 903 if (!$toSavepoint || $this->transactionDepth <= 1) 904 { 905 $this->connect(); 906 907 if ($this->connection->commit()) 908 { 909 $this->transactionDepth = 0; 910 } 911 912 return; 913 } 914 915 $this->transactionDepth--; 916 } 917 918 /** 919 * Method to roll back a transaction. 920 * 921 * @param boolean $toSavepoint If true, rollback to the last savepoint. 922 * 923 * @return void 924 * 925 * @since 1.0 926 * @throws \RuntimeException 927 */ 928 public function transactionRollback($toSavepoint = false) 929 { 930 if (!$toSavepoint || $this->transactionDepth <= 1) 931 { 932 $this->connect(); 933 934 if ($this->connection->rollback()) 935 { 936 $this->transactionDepth = 0; 937 } 938 939 return; 940 } 941 942 $savepoint = 'SP_' . ($this->transactionDepth - 1); 943 944 if ($this->executeUnpreparedQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint))) 945 { 946 $this->transactionDepth--; 947 } 948 } 949 950 /** 951 * Method to initialize a transaction. 952 * 953 * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. 954 * 955 * @return void 956 * 957 * @since 1.0 958 * @throws \RuntimeException 959 */ 960 public function transactionStart($asSavepoint = false) 961 { 962 $this->connect(); 963 964 if (!$asSavepoint || !$this->transactionDepth) 965 { 966 if ($this->connection->begin_transaction()) 967 { 968 $this->transactionDepth = 1; 969 } 970 971 return; 972 } 973 974 $savepoint = 'SP_' . $this->transactionDepth; 975 976 if ($this->connection->savepoint($savepoint)) 977 { 978 $this->transactionDepth++; 979 } 980 } 981 982 /** 983 * Internal method to execute queries which cannot be run as prepared statements. 984 * 985 * @param string $sql SQL statement to execute. 986 * 987 * @return boolean 988 * 989 * @since 1.5.0 990 */ 991 protected function executeUnpreparedQuery($sql) 992 { 993 $this->connect(); 994 995 $cursor = $this->connection->query($sql); 996 997 // If an error occurred handle it. 998 if (!$cursor) 999 { 1000 $errorNum = (int) $this->connection->errno; 1001 $errorMsg = (string) $this->connection->error; 1002 1003 // Check if the server was disconnected. 1004 if (!$this->connected()) 1005 { 1006 try 1007 { 1008 // Attempt to reconnect. 1009 $this->connection = null; 1010 $this->connect(); 1011 } 1012 catch (ConnectionFailureException $e) 1013 { 1014 // If connect fails, ignore that exception and throw the normal exception. 1015 throw new ExecutionFailureException($sql, $errorMsg, $errorNum); 1016 } 1017 1018 // Since we were able to reconnect, run the query again. 1019 return $this->executeUnpreparedQuery($sql); 1020 } 1021 1022 // The server was not disconnected. 1023 throw new ExecutionFailureException($sql, $errorMsg, $errorNum); 1024 } 1025 1026 $this->freeResult(); 1027 1028 if ($cursor instanceof \mysqli_result) 1029 { 1030 $cursor->free_result(); 1031 } 1032 1033 return true; 1034 } 1035 1036 /** 1037 * Prepares a SQL statement for execution 1038 * 1039 * @param string $query The SQL query to be prepared. 1040 * 1041 * @return StatementInterface 1042 * 1043 * @since 2.0.0 1044 * @throws PrepareStatementFailureException 1045 */ 1046 protected function prepareStatement(string $query): StatementInterface 1047 { 1048 return new MysqliStatement($this->connection, $query); 1049 } 1050 1051 /** 1052 * Unlocks tables in the database. 1053 * 1054 * @return $this 1055 * 1056 * @since 1.0 1057 * @throws \RuntimeException 1058 */ 1059 public function unlockTables() 1060 { 1061 $this->executeUnpreparedQuery('UNLOCK TABLES'); 1062 1063 return $this; 1064 } 1065 1066 /** 1067 * Does the database server claim to have support for UTF-8 Multibyte (utf8mb4) collation? 1068 * 1069 * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL server). mysqlnd supports utf8mb4 since 5.0.9. 1070 * 1071 * @return boolean 1072 * 1073 * @since 1.4.0 1074 */ 1075 private function serverClaimsUtf8mb4Support() 1076 { 1077 $client_version = mysqli_get_client_info(); 1078 $server_version = $this->getVersion(); 1079 1080 if (version_compare($server_version, '5.5.3', '<')) 1081 { 1082 return false; 1083 } 1084 1085 if ($this->mariadb && version_compare($server_version, '10.0.0', '<')) 1086 { 1087 return false; 1088 } 1089 1090 if (strpos($client_version, 'mysqlnd') !== false) 1091 { 1092 $client_version = preg_replace('/^\D+([\d.]+).*/', '$1', $client_version); 1093 1094 return version_compare($client_version, '5.0.9', '>='); 1095 } 1096 1097 return version_compare($client_version, '5.5.3', '>='); 1098 } 1099 1100 /** 1101 * Get the null or zero representation of a timestamp for the database driver. 1102 * 1103 * @return string 1104 * 1105 * @since 2.0.0 1106 */ 1107 public function getNullDate() 1108 { 1109 // Check the session sql mode; 1110 if (\in_array('NO_ZERO_DATE', $this->options['sqlModes']) !== false) 1111 { 1112 $this->nullDate = '1000-01-01 00:00:00'; 1113 } 1114 1115 return $this->nullDate; 1116 } 1117 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |