[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Installation 5 * @subpackage Model 6 * 7 * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\CMS\Installation\Model; 12 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Installation\Helper\DatabaseHelper; 15 use Joomla\CMS\Language\LanguageHelper; 16 use Joomla\CMS\Language\Text; 17 use Joomla\CMS\Object\CMSObject; 18 use Joomla\Database\DatabaseDriver; 19 use Joomla\Database\DatabaseInterface; 20 use Joomla\Utilities\ArrayHelper; 21 22 // phpcs:disable PSR1.Files.SideEffects 23 \defined('_JEXEC') or die; 24 // phpcs:enable PSR1.Files.SideEffects 25 26 /** 27 * Database configuration model for the Joomla Core Installer. 28 * 29 * @since 3.1 30 */ 31 class DatabaseModel extends BaseInstallationModel 32 { 33 /** 34 * Get the current setup options from the session. 35 * 36 * @return array An array of options from the session. 37 * 38 * @since 4.0.0 39 */ 40 public function getOptions() 41 { 42 return Factory::getSession()->get('setup.options', array()); 43 } 44 45 /** 46 * Method to initialise the database. 47 * 48 * @param boolean $select Select the database when creating the connections. 49 * 50 * @return DatabaseInterface|boolean Database object on success, boolean false on failure 51 * 52 * @since 3.1 53 */ 54 public function initialise($select = true) 55 { 56 $options = $this->getOptions(); 57 58 // Get the options as an object for easier handling. 59 $options = ArrayHelper::toObject($options); 60 61 // Load the backend language files so that the DB error messages work. 62 $lang = Factory::getLanguage(); 63 $currentLang = $lang->getTag(); 64 65 // Load the selected language 66 if (LanguageHelper::exists($currentLang, JPATH_ADMINISTRATOR)) { 67 $lang->load('joomla', JPATH_ADMINISTRATOR, $currentLang, true); 68 } else { 69 // Pre-load en-GB in case the chosen language files do not exist. 70 $lang->load('joomla', JPATH_ADMINISTRATOR, 'en-GB', true); 71 } 72 73 // Validate and clean up connection parameters 74 $paramsCheck = DatabaseHelper::validateConnectionParameters($options); 75 76 if ($paramsCheck) { 77 Factory::getApplication()->enqueueMessage($paramsCheck, 'warning'); 78 79 return false; 80 } 81 82 // Security check for remote db hosts 83 if (!DatabaseHelper::checkRemoteDbHost($options)) { 84 // Messages have been enqueued in the called function. 85 return false; 86 } 87 88 // Get a database object. 89 try { 90 return DatabaseHelper::getDbo( 91 $options->db_type, 92 $options->db_host, 93 $options->db_user, 94 $options->db_pass_plain, 95 $options->db_name, 96 $options->db_prefix, 97 $select, 98 DatabaseHelper::getEncryptionSettings($options) 99 ); 100 } catch (\RuntimeException $e) { 101 Factory::getApplication()->enqueueMessage(Text::sprintf('INSTL_DATABASE_COULD_NOT_CONNECT', $e->getMessage()), 'error'); 102 103 return false; 104 } 105 } 106 107 /** 108 * Method to create a new database. 109 * 110 * @return boolean 111 * 112 * @since 3.1 113 * @throws \RuntimeException 114 */ 115 public function createDatabase() 116 { 117 $options = (object) $this->getOptions(); 118 119 $db = $this->initialise(false); 120 121 if ($db === false) { 122 // Error messages are enqueued by the initialise function, we just need to tell the controller how to redirect 123 return false; 124 } 125 126 // Check database version. 127 $type = $options->db_type; 128 129 try { 130 $db_version = $db->getVersion(); 131 } catch (\RuntimeException $e) { 132 /* 133 * We may get here if the database doesn't exist, if so then explain that to users instead of showing the database connector's error 134 * This only supports PDO PostgreSQL and the PDO MySQL drivers presently 135 * 136 * Error Messages: 137 * PDO MySQL: [1049] Unknown database 'database_name' 138 * PDO PostgreSQL: database "database_name" does not exist 139 */ 140 if ( 141 $type === 'mysql' && strpos($e->getMessage(), '[1049] Unknown database') === 42 142 || $type === 'pgsql' && strpos($e->getMessage(), 'database "' . $options->db_name . '" does not exist') 143 ) { 144 /* 145 * Now we're really getting insane here; we're going to try building a new JDatabaseDriver instance 146 * in order to trick the connection into creating the database 147 */ 148 if ($type === 'mysql') { 149 // MySQL (PDO): Don't specify database name 150 $altDBoptions = array( 151 'driver' => $options->db_type, 152 'host' => $options->db_host, 153 'user' => $options->db_user, 154 'password' => $options->db_pass_plain, 155 'prefix' => $options->db_prefix, 156 'select' => false, 157 DatabaseHelper::getEncryptionSettings($options), 158 ); 159 } else { 160 // PostgreSQL (PDO): Use 'postgres' 161 $altDBoptions = array( 162 'driver' => $options->db_type, 163 'host' => $options->db_host, 164 'user' => $options->db_user, 165 'password' => $options->db_pass_plain, 166 'database' => 'postgres', 167 'prefix' => $options->db_prefix, 168 'select' => false, 169 DatabaseHelper::getEncryptionSettings($options), 170 ); 171 } 172 173 $altDB = DatabaseDriver::getInstance($altDBoptions); 174 175 // Check database server parameters 176 $dbServerCheck = DatabaseHelper::checkDbServerParameters($altDB, $options); 177 178 if ($dbServerCheck) { 179 // Some server parameter is not ok 180 throw new \RuntimeException($dbServerCheck, 500, $e); 181 } 182 183 // Try to create the database now using the alternate driver 184 try { 185 $this->createDb($altDB, $options, $altDB->hasUtfSupport()); 186 } catch (\RuntimeException $e) { 187 // We did everything we could 188 throw new \RuntimeException(Text::_('INSTL_DATABASE_COULD_NOT_CREATE_DATABASE'), 500, $e); 189 } 190 191 // If we got here, the database should have been successfully created, now try one more time to get the version 192 try { 193 $db_version = $db->getVersion(); 194 } catch (\RuntimeException $e) { 195 // We did everything we could 196 throw new \RuntimeException(Text::sprintf('INSTL_DATABASE_COULD_NOT_CONNECT', $e->getMessage()), 500, $e); 197 } 198 } else { 199 // Anything getting into this part of the conditional either doesn't support manually creating the database or isn't that type of error 200 throw new \RuntimeException(Text::sprintf('INSTL_DATABASE_COULD_NOT_CONNECT', $e->getMessage()), 500, $e); 201 } 202 } 203 204 // Check database server parameters 205 $dbServerCheck = DatabaseHelper::checkDbServerParameters($db, $options); 206 207 if ($dbServerCheck) { 208 // Some server parameter is not ok 209 throw new \RuntimeException($dbServerCheck, 500, $e); 210 } 211 212 // @internal Check for spaces in beginning or end of name. 213 if (strlen(trim($options->db_name)) <> strlen($options->db_name)) { 214 throw new \RuntimeException(Text::_('INSTL_DATABASE_NAME_INVALID_SPACES')); 215 } 216 217 // @internal Check for asc(00) Null in name. 218 if (strpos($options->db_name, chr(00)) !== false) { 219 throw new \RuntimeException(Text::_('INSTL_DATABASE_NAME_INVALID_CHAR')); 220 } 221 222 // Get database's UTF support. 223 $utfSupport = $db->hasUtfSupport(); 224 225 // Try to select the database. 226 try { 227 $db->select($options->db_name); 228 } catch (\RuntimeException $e) { 229 // If the database could not be selected, attempt to create it and then select it. 230 if (!$this->createDb($db, $options, $utfSupport)) { 231 throw new \RuntimeException(Text::sprintf('INSTL_DATABASE_ERROR_CREATE', $options->db_name), 500, $e); 232 } 233 234 $db->select($options->db_name); 235 } 236 237 // Set the character set to UTF-8 for pre-existing databases. 238 try { 239 $db->alterDbCharacterSet($options->db_name); 240 } catch (\RuntimeException $e) { 241 // Continue Anyhow 242 } 243 244 $options = (array) $options; 245 246 // Remove *_errors value. 247 foreach ($options as $i => $option) { 248 if (isset($i['1']) && $i['1'] == '*') { 249 unset($options[$i]); 250 251 break; 252 } 253 } 254 255 $options = array_merge(['db_created' => 1], $options); 256 257 Factory::getSession()->set('setup.options', $options); 258 259 return true; 260 } 261 262 /** 263 * Method to process the old database. 264 * 265 * @return boolean True on success. 266 * 267 * @since 3.1 268 */ 269 public function handleOldDatabase() 270 { 271 $options = $this->getOptions(); 272 273 if (!isset($options['db_created']) || !$options['db_created']) { 274 return $this->createDatabase($options); 275 } 276 277 // Get the options as an object for easier handling. 278 $options = ArrayHelper::toObject($options); 279 280 if (!$db = $this->initialise()) { 281 return false; 282 } 283 284 // Set the character set to UTF-8 for pre-existing databases. 285 try { 286 $db->alterDbCharacterSet($options->db_name); 287 } catch (\RuntimeException $e) { 288 // Continue Anyhow 289 } 290 291 // Backup any old database. 292 if (!$this->backupDatabase($db, $options->db_prefix)) { 293 return false; 294 } 295 296 return true; 297 } 298 299 /** 300 * Method to create the database tables. 301 * 302 * @param string $schema The SQL schema file to apply. 303 * 304 * @return boolean True on success. 305 * 306 * @since 3.1 307 */ 308 public function createTables($schema) 309 { 310 if (!$db = $this->initialise()) { 311 return false; 312 } 313 314 $serverType = $db->getServerType(); 315 316 // Set the appropriate schema script based on UTF-8 support. 317 $schemaFile = 'sql/' . $serverType . '/' . $schema . '.sql'; 318 319 // Check if the schema is a valid file 320 if (!is_file($schemaFile)) { 321 Factory::getApplication()->enqueueMessage(Text::sprintf('INSTL_ERROR_DB', Text::_('INSTL_DATABASE_NO_SCHEMA')), 'error'); 322 323 return false; 324 } 325 326 // Attempt to import the database schema. 327 if (!$this->populateDatabase($db, $schemaFile)) { 328 return false; 329 } 330 331 return true; 332 } 333 334 /** 335 * Method to backup all tables in a database with a given prefix. 336 * 337 * @param DatabaseDriver $db JDatabaseDriver object. 338 * @param string $prefix Database table prefix. 339 * 340 * @return boolean True on success. 341 * 342 * @since 3.1 343 */ 344 public function backupDatabase($db, $prefix) 345 { 346 $return = true; 347 $backup = 'bak_' . $prefix; 348 349 // Get the tables in the database. 350 $tables = $db->getTableList(); 351 352 if ($tables) { 353 foreach ($tables as $table) { 354 // If the table uses the given prefix, back it up. 355 if (strpos($table, $prefix) === 0) { 356 // Backup table name. 357 $backupTable = str_replace($prefix, $backup, $table); 358 359 // Drop the backup table. 360 try { 361 $db->dropTable($backupTable, true); 362 } catch (\RuntimeException $e) { 363 Factory::getApplication()->enqueueMessage(Text::sprintf('INSTL_DATABASE_ERROR_BACKINGUP', $e->getMessage()), 'error'); 364 365 $return = false; 366 } 367 368 // Rename the current table to the backup table. 369 try { 370 $db->renameTable($table, $backupTable, $backup, $prefix); 371 } catch (\RuntimeException $e) { 372 Factory::getApplication()->enqueueMessage(Text::sprintf('INSTL_DATABASE_ERROR_BACKINGUP', $e->getMessage()), 'error'); 373 374 $return = false; 375 } 376 } 377 } 378 } 379 380 return $return; 381 } 382 383 /** 384 * Method to create a new database. 385 * 386 * @param DatabaseDriver $db Database object. 387 * @param CMSObject $options CMSObject coming from "initialise" function to pass user 388 * and database name to database driver. 389 * @param boolean $utf True if the database supports the UTF-8 character set. 390 * 391 * @return boolean True on success. 392 * 393 * @since 3.1 394 */ 395 public function createDb($db, $options, $utf) 396 { 397 // Build the create database query. 398 try { 399 // Run the create database query. 400 $db->createDatabase($options, $utf); 401 } catch (\RuntimeException $e) { 402 // If an error occurred return false. 403 return false; 404 } 405 406 return true; 407 } 408 409 /** 410 * Method to import a database schema from a file. 411 * 412 * @param \Joomla\Database\DatabaseInterface $db JDatabase object. 413 * @param string $schema Path to the schema file. 414 * 415 * @return boolean True on success. 416 * 417 * @since 3.1 418 */ 419 public function populateDatabase($db, $schema) 420 { 421 $return = true; 422 423 // Get the contents of the schema file. 424 if (!($buffer = file_get_contents($schema))) { 425 Factory::getApplication()->enqueueMessage(Text::_('INSTL_SAMPLE_DATA_NOT_FOUND'), 'error'); 426 427 return false; 428 } 429 430 // Get an array of queries from the schema and process them. 431 $queries = $this->splitQueries($buffer); 432 433 foreach ($queries as $query) { 434 // Trim any whitespace. 435 $query = trim($query); 436 437 // If the query isn't empty and is not a MySQL or PostgreSQL comment, execute it. 438 if (!empty($query) && ($query[0] != '#') && ($query[0] != '-')) { 439 // Execute the query. 440 $db->setQuery($query); 441 442 try { 443 $db->execute(); 444 } catch (\RuntimeException $e) { 445 Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); 446 447 $return = false; 448 } 449 } 450 } 451 452 return $return; 453 } 454 455 /** 456 * Method to split up queries from a schema file into an array. 457 * 458 * @param string $query SQL schema. 459 * 460 * @return array Queries to perform. 461 * 462 * @since 3.1 463 */ 464 protected function splitQueries($query) 465 { 466 $buffer = array(); 467 $queries = array(); 468 $in_string = false; 469 470 // Trim any whitespace. 471 $query = trim($query); 472 473 // Remove comment lines. 474 $query = preg_replace("/\n\#[^\n]*/", '', "\n" . $query); 475 476 // Remove PostgreSQL comment lines. 477 $query = preg_replace("/\n\--[^\n]*/", '', "\n" . $query); 478 479 // Find function. 480 $funct = explode('CREATE OR REPLACE FUNCTION', $query); 481 482 // Save sql before function and parse it. 483 $query = $funct[0]; 484 485 // Parse the schema file to break up queries. 486 for ($i = 0; $i < strlen($query) - 1; $i++) { 487 if ($query[$i] == ';' && !$in_string) { 488 $queries[] = substr($query, 0, $i); 489 $query = substr($query, $i + 1); 490 $i = 0; 491 } 492 493 if ($in_string && ($query[$i] == $in_string) && $buffer[1] != "\\") { 494 $in_string = false; 495 } elseif (!$in_string && ($query[$i] == '"' || $query[$i] == "'") && (!isset($buffer[0]) || $buffer[0] != "\\")) { 496 $in_string = $query[$i]; 497 } 498 499 if (isset($buffer[1])) { 500 $buffer[0] = $buffer[1]; 501 } 502 503 $buffer[1] = $query[$i]; 504 } 505 506 // If the is anything left over, add it to the queries. 507 if (!empty($query)) { 508 $queries[] = $query; 509 } 510 511 // Add function part as is. 512 for ($f = 1, $fMax = count($funct); $f < $fMax; $f++) { 513 $queries[] = 'CREATE OR REPLACE FUNCTION ' . $funct[$f]; 514 } 515 516 return $queries; 517 } 518 }
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 |