abstract class DatabaseTasks {
protected $pdoDriver;
protected $databaseNotFoundErrorCode;
protected $connectionRefusedErrorCode;
protected $tasks = array(
array(
'function' => 'checkEngineVersion',
'arguments' => array(),
),
array(
'function' => 'checkUtf8mb4',
'arguments' => array(),
),
array(
'arguments' => array(
'CREATE TABLE {backdrop_install_test} (id int NULL)',
'Backdrop can use CREATE TABLE database commands.',
'Failed to <strong>CREATE</strong> a test table on your database server with the command %query. The server reports the following message: %error.<p>Are you sure the configured username has the necessary permissions to create tables in the database?</p>',
TRUE,
),
),
array(
'arguments' => array(
'INSERT INTO {backdrop_install_test} (id) VALUES (1)',
'Backdrop can use INSERT database commands.',
'Failed to <strong>INSERT</strong> a value into a test table on your database server. We tried inserting a value with the command %query and the server reported the following error: %error.',
),
),
array(
'arguments' => array(
'UPDATE {backdrop_install_test} SET id = 2',
'Backdrop can use UPDATE database commands.',
'Failed to <strong>UPDATE</strong> a value in a test table on your database server. We tried updating a value with the command %query and the server reported the following error: %error.',
),
),
array(
'arguments' => array(
'DELETE FROM {backdrop_install_test}',
'Backdrop can use DELETE database commands.',
'Failed to <strong>DELETE</strong> a value from a test table on your database server. We tried deleting a value with the command %query and the server reported the following error: %error.',
),
),
array(
'arguments' => array(
'DROP TABLE {backdrop_install_test}',
'Backdrop can use DROP TABLE database commands.',
'Failed to <strong>DROP</strong> a test table from your database server. We tried dropping a table with the command %query and the server reported the following error %error.',
),
),
);
protected $results = array();
protected function hasPdoDriver() {
return in_array($this->pdoDriver, PDO::getAvailableDrivers());
}
protected function fail($message) {
$this->results[$message] = FALSE;
}
protected function pass($message) {
$this->results[$message] = TRUE;
}
public function installable() {
return $this->hasPdoDriver() && empty($this->error);
}
abstract public function name();
public function minimumVersion() {
return NULL;
}
public function runTasks() {
if ($this->connect()) {
foreach ($this->tasks as $task) {
if (!isset($task['function'])) {
$task['function'] = 'runTestQuery';
}
if (method_exists($this, $task['function'])) {
if (FALSE === call_user_func_array(array($this, $task['function']), $task['arguments'])) {
break;
}
}
else {
throw new DatabaseTaskException(st("Failed to run all tasks against the database server. The task %task wasn't found.", array('%task' => $task['function'])));
}
}
}
$message = '';
foreach ($this->results as $result => $success) {
if (!$success) {
$message .= '<p>' . $result . '</p>';
}
}
if (!empty($message)) {
$message .= '<p>For more help with configuring your database server, see the <a href="https://backdropcms.org/installation">Installation Instructions</a> page.</p>';
throw new DatabaseTaskException($message);
}
}
protected function connect() {
$original_connection_info = $modified_connection_info = Database::getConnectionInfo();
$last_exception = FALSE;
$extra_help = '';
try {
db_set_active();
Database::getConnection();
}
catch (PDOException $e) {
$original_failed_exception = $last_exception = $e;
}
if ($last_exception && ($last_exception->getCode() === $this->connectionRefusedErrorCode)) {
$localhost_invalid = $original_connection_info['default']['host'] === 'localhost';
$local_ip_invalid = $original_connection_info['default']['host'] === '127.0.0.1';
if ($localhost_invalid) {
$modified_connection_info['default']['host'] = '127.0.0.1';
$modified_connection_info['default']['port'] = '3306';
}
elseif ($local_ip_invalid) {
$modified_connection_info['default']['host'] = 'localhost';
$modified_connection_info['default']['port'] = '';
}
if ($original_connection_info != $modified_connection_info) {
Database::removeConnection('default');
Database::addConnectionInfo('default', 'default', $modified_connection_info['default']);
try {
Database::getConnection('default', 'default');
$extra_help = st('Connecting to "@broken_host" was not successful, but connecting to "@working_host" with the same credentials was successful. Try setting the database host (under Advanced options) to "@working_host".', array(
'@broken_host' => $localhost_invalid ? 'localhost' : '127.0.0.1',
'@working_host' => $localhost_invalid ? '127.0.0.1' : 'localhost',
));
}
catch (Exception $e) {
$last_exception = $e;
}
}
}
if ($last_exception && ($last_exception->getCode() === $this->databaseNotFoundErrorCode)) {
$database = $modified_connection_info['default']['database'];
unset($modified_connection_info['default']['database']);
if (preg_match('/[^A-Za-z0-9_.]+/', $database)) {
$this->fail(st('Database %database is not valid. Only use alphanumeric characters and underscores in the database name.', array('%database' => $database)));
return FALSE;
}
Database::removeConnection('default');
Database::addConnectionInfo('default', 'default', $modified_connection_info['default']);
try {
Database::getConnection()->createDatabase($database);
Database::removeConnection('default');
return $this->connect();
}
catch (PDOException $e) {
$last_exception = $e;
}
}
if ($last_exception) {
$host = $original_connection_info['default']['host'];
if ($original_connection_info['default']['port']) {
$host .= ':' . $original_connection_info['default']['port'];
}
$exception_message = preg_replace('/SQLSTATE\[.*\]/', '', $original_failed_exception->getMessage());
$error = st('Failed to connect to your database server. The server reports the following message: %error.', array('%error' => $exception_message));
$error .= '<div class="database-extra-help">';
if ($extra_help) {
$error .= $extra_help;
}
else {
$error .= '<div class="item-list"><ul><li>';
$error .= st('Is the database server running on %host?', array('%host' => $host));
$error .= '</li><li>';
$error .= st('Is %database the correct database name?', array('%database' => $original_connection_info['default']['database']));
$error .= '</li><li>';
$error .= st('Are the username and password correct?');
$error .= '</li></ul></div>';
}
$error .= '</div>';
$this->fail($error);
return FALSE;
}
$this->pass('Backdrop can CONNECT to the database ok.');
return TRUE;
}
protected function runTestQuery($query, $pass, $fail, $fatal = FALSE) {
try {
db_query($query);
$this->pass(st($pass));
return NULL;
}
catch (Exception $e) {
$this->fail(st($fail, array('%query' => $query, '%error' => $e->getMessage(), '%name' => $this->name())));
return !$fatal;
}
}
protected function checkEngineVersion() {
if ($this->minimumVersion() && version_compare(Database::getConnection()->version(), $this->minimumVersion(), '<')) {
$this->fail(st("The database version %version is less than the minimum required version %minimum_version.", array('%version' => Database::getConnection()->version(), '%minimum_version' => $this->minimumVersion())));
}
}
protected function checkUtf8mb4() {
$connection = Database::getConnection();
$connection_info = Database::getConnectionInfo();
if (!$connection->utf8mb4IsActive()) {
if ($connection->utf8mb4IsSupported()) {
$connection_info['default']['charset'] = 'utf8mb4';
Database::removeConnection('default');
Database::addConnectionInfo('default', 'default', $connection_info['default']);
Database::getConnection();
}
}
}
public function getFormOptions($database) {
$form['database'] = array(
'#type' => 'textfield',
'#title' => st('MySQL Database name'),
'#default_value' => empty($database['database']) ? '' : $database['database'],
'#size' => 45,
'#required' => TRUE,
);
$form['username'] = array(
'#type' => 'textfield',
'#title' => st('Database username'),
'#default_value' => empty($database['username']) ? '' : $database['username'],
'#required' => TRUE,
'#size' => 45,
);
$form['password'] = array(
'#type' => 'password',
'#title' => st('Database password'),
'#default_value' => empty($database['password']) ? '' : $database['password'],
'#required' => FALSE,
'#size' => 45,
'#password_toggle' => TRUE,
);
$form['advanced_options'] = array(
'#type' => 'fieldset',
'#title' => st('Advanced options'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#description' => st("These options are only necessary for some sites. If you're not sure what you should enter here, leave the default settings or check with your hosting provider."),
'#weight' => 10,
);
$profile = backdrop_get_profile();
$prefix = ($profile == 'standard') ? 'backdrop_' : $profile . '_';
$form['advanced_options']['prefix'] = array(
'#type' => 'textfield',
'#title' => st('Table prefix'),
'#default_value' => '',
'#size' => 45,
'#description' => st('If more than one application will be sharing this database, enter a table prefix such as %prefix for your @profile site here.', array(
'@profile' => backdrop_install_profile_distribution_name(),
'%prefix' => $prefix,
)),
'#weight' => 10,
);
$form['advanced_options']['host'] = array(
'#type' => 'textfield',
'#title' => st('Database host'),
'#default_value' => empty($database['host']) ? '127.0.0.1' : $database['host'],
'#size' => 45,
'#maxlength' => 255,
'#required' => TRUE,
'#description' => st('If your database is located on a different server, change this.'),
);
$form['advanced_options']['port'] = array(
'#type' => 'number',
'#title' => st('Database port'),
'#default_value' => empty($database['port']) ? '' : $database['port'],
'#min' => 1,
'#max' => 65535,
'#description' => st('If your database server is listening to a non-standard port, enter its number.'),
);
$form['advanced_options']['charset'] = array(
'#type' => 'hidden',
'#default_value' => empty($database['charset']) ? 'utf8mb4' : $database['charset'],
);
$form['advanced_options']['collation'] = array(
'#type' => 'hidden',
'#default_value' => empty($database['collation']) ? 'utf8mb4_general_ci' : $database['collation'],
);
return $form;
}
public function validateDatabaseSettings($database) {
$errors = array();
if (!empty($database['prefix']) && is_string($database['prefix']) && !preg_match('/^[A-Za-z0-9_.]+$/', $database['prefix'])) {
$errors[$database['driver'] . '][advanced_options][prefix'] = st('The database table prefix you have entered, %prefix, is invalid. The table prefix can only contain alphanumeric characters, periods, or underscores.', array('%prefix' => $database['prefix']));
}
if (!empty($database['port']) && !is_numeric($database['port'])) {
$errors[$database['driver'] . '][advanced_options][port'] = st('Database port must be a number.');
}
return $errors;
}
}