3 namespace Drupal\Core\Installer\Form;
5 use Drupal\Component\Utility\Crypt;
6 use Drupal\Core\Database\Database;
7 use Drupal\Core\Form\FormBase;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\Core\Render\RendererInterface;
10 use Symfony\Component\DependencyInjection\ContainerInterface;
13 * Provides a form to configure and rewrite settings.php.
17 class SiteSettingsForm extends FormBase {
29 * @var \Drupal\Core\Render\RendererInterface
34 * Constructs a new SiteSettingsForm.
36 * @param string $site_path
39 public function __construct($site_path, RendererInterface $renderer) {
40 $this->sitePath = $site_path;
41 $this->renderer = $renderer;
47 public static function create(ContainerInterface $container) {
49 $container->get('site.path'),
50 $container->get('renderer')
57 public function getFormId() {
58 return 'install_settings_form';
64 public function buildForm(array $form, FormStateInterface $form_state) {
65 $settings_file = './' . $this->sitePath . '/settings.php';
67 $form['#title'] = $this->t('Database configuration');
69 $drivers = drupal_get_database_types();
70 $drivers_keys = array_keys($drivers);
72 // Unless there is input for this form (for a non-interactive installation,
73 // input originates from the $settings array passed into install_drupal()),
74 // check whether database connection settings have been prepared in
75 // settings.php already.
76 // Note: The installer even executes this form if there is a valid database
77 // connection already, since the submit handler of this form is responsible
78 // for writing all $settings to settings.php (not limited to $databases).
79 $input = &$form_state->getUserInput();
80 if (!isset($input['driver']) && $database = Database::getConnectionInfo()) {
81 $input['driver'] = $database['default']['driver'];
82 $input[$database['default']['driver']] = $database['default'];
85 if (isset($input['driver'])) {
86 $default_driver = $input['driver'];
87 // In case of database connection info from settings.php, as well as for a
88 // programmed form submission (non-interactive installer), the table prefix
89 // information is usually normalized into an array already, but the form
90 // element only allows to configure one default prefix for all tables.
91 $prefix = &$input[$default_driver]['prefix'];
92 if (isset($prefix) && is_array($prefix)) {
93 $prefix = $prefix['default'];
95 $default_options = $input[$default_driver];
97 // If there is no database information yet, suggest the first available driver
98 // as default value, so that its settings form is made visible via #states
99 // when JavaScript is enabled (see below).
101 $default_driver = current($drivers_keys);
102 $default_options = [];
107 '#title' => $this->t('Database type'),
109 '#default_value' => $default_driver,
111 if (count($drivers) == 1) {
112 $form['driver']['#disabled'] = TRUE;
115 // Add driver specific configuration options.
116 foreach ($drivers as $key => $driver) {
117 $form['driver']['#options'][$key] = $driver->name();
119 $form['settings'][$key] = $driver->getFormOptions($default_options);
120 $form['settings'][$key]['#prefix'] = '<h2 class="js-hide">' . $this->t('@driver_name settings', ['@driver_name' => $driver->name()]) . '</h2>';
121 $form['settings'][$key]['#type'] = 'container';
122 $form['settings'][$key]['#tree'] = TRUE;
123 $form['settings'][$key]['advanced_options']['#parents'] = [$key];
124 $form['settings'][$key]['#states'] = [
126 ':input[name=driver]' => ['value' => $key],
131 $form['actions'] = ['#type' => 'actions'];
132 $form['actions']['save'] = [
134 '#value' => $this->t('Save and continue'),
135 '#button_type' => 'primary',
136 '#limit_validation_errors' => [
140 '#submit' => ['::submitForm'],
143 $form['errors'] = [];
144 $form['settings_file'] = ['#type' => 'value', '#value' => $settings_file];
152 public function validateForm(array &$form, FormStateInterface $form_state) {
153 $driver = $form_state->getValue('driver');
154 $database = $form_state->getValue($driver);
155 $drivers = drupal_get_database_types();
156 $reflection = new \ReflectionClass($drivers[$driver]);
157 $install_namespace = $reflection->getNamespaceName();
158 // Cut the trailing \Install from namespace.
159 $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\'));
160 $database['driver'] = $driver;
162 $form_state->set('database', $database);
163 foreach ($this->getDatabaseErrors($database, $form_state->getValue('settings_file')) as $name => $message) {
164 $form_state->setErrorByName($name, $message);
169 * Get any database errors and links them to a form element.
171 * @param array $database
172 * An array of database settings.
173 * @param string $settings_file
174 * The settings file that contains the database settings.
177 * An array of form errors keyed by the element name and parents.
179 protected function getDatabaseErrors(array $database, $settings_file) {
180 $errors = install_database_errors($database, $settings_file);
181 $form_errors = array_filter($errors, function ($value) {
182 // Errors keyed by something other than an integer already are linked to
184 return is_int($value);
187 // Find the generic errors.
188 $errors = array_diff_key($errors, $form_errors);
190 if (count($errors)) {
192 '#type' => 'inline_template',
193 '#template' => '{% trans %}Resolve all issues below to continue the installation. For help configuring your database server, see the <a href="https://www.drupal.org/getting-started/install">installation handbook</a>, or contact your hosting provider.{% endtrans%}{{ errors }}',
196 '#theme' => 'item_list',
202 // These are generic errors, so we do not have any specific key of the
203 // database connection array to attach them to; therefore, we just put
204 // them in the error array with standard numeric keys.
205 $form_errors[$database['driver'] . '][0'] = $this->renderer->renderPlain($error_message);
214 public function submitForm(array &$form, FormStateInterface $form_state) {
215 global $install_state;
217 // Update global settings array and save.
219 $database = $form_state->get('database');
220 $settings['databases']['default']['default'] = (object) [
221 'value' => $database,
224 $settings['settings']['hash_salt'] = (object) [
225 'value' => Crypt::randomBytesBase64(55),
228 // Remember the profile which was used.
229 $settings['settings']['install_profile'] = (object) [
230 'value' => $install_state['parameters']['profile'],
234 drupal_rewrite_settings($settings);
236 // Add the config directories to settings.php.
237 drupal_install_config_directories();
239 // Indicate that the settings file has been verified, and check the database
240 // for the last completed task, now that we have a valid connection. This
241 // last step is important since we want to trigger an error if the new
242 // database already has Drupal installed.
243 $install_state['settings_verified'] = TRUE;
244 $install_state['config_verified'] = TRUE;
245 $install_state['database_verified'] = TRUE;
246 $install_state['completed_task'] = install_verify_completed_task();