You are here

protected function UpgradeStatusForm::buildEnvironmentChecks in Upgrade Status 8.3

Same name and namespace in other branches
  1. 8.2 src/Form/UpgradeStatusForm.php \Drupal\upgrade_status\Form\UpgradeStatusForm::buildEnvironmentChecks()

Builds a list of environment checks.

Return value

array Build array. The overall environment status (TRUE, FALSE or NULL) is indicated in the 'status' key, while a 'description' key explains the environment requirements on a high level.

1 call to UpgradeStatusForm::buildEnvironmentChecks()
UpgradeStatusForm::buildForm in src/Form/UpgradeStatusForm.php
Form constructor.

File

src/Form/UpgradeStatusForm.php, line 670

Class

UpgradeStatusForm

Namespace

Drupal\upgrade_status\Form

Code

protected function buildEnvironmentChecks() {
  $status = TRUE;
  $header = [
    'requirement' => [
      'data' => $this
        ->t('Requirement'),
      'class' => 'requirement-label',
    ],
    'status' => [
      'data' => $this
        ->t('Status'),
      'class' => 'status-info',
    ],
  ];
  $build['data'] = [
    '#type' => 'table',
    '#header' => $header,
    '#rows' => [],
  ];
  if ($this->nextMajor == 10) {

    // @todo update this as the situation develops.
    $build['description'] = $this
      ->t('<a href=":environment">Drupal 10 environment requirements are still evolving</a>. Upgrades to Drupal 10 are planned to be supported from Drupal 9.3.x and Drupal 9.4.x.', [
      ':environment' => 'https://www.drupal.org/project/drupal/issues/3118147',
    ]);

    // Check PHP version.
    $version = PHP_VERSION;
    if (version_compare($version, '8.0.0') >= 0) {
      $class = 'no-known-error';
    }
    else {
      $class = 'known-error';
      $status = FALSE;
    }
    $build['data']['#rows'][] = [
      'class' => [
        $class,
      ],
      'data' => [
        'requirement' => [
          'class' => 'requirement-label',
          'data' => $this
            ->t('PHP version should be at least 8.0.0. Before updating to PHP 8, use <code>$ composer why-not php:8</code> to check if any projects need updating for compatibility. Also check custom projects manually.'),
        ],
        'status' => [
          'data' => $this
            ->t('Version @version', [
            '@version' => $version,
          ]),
          'class' => 'status-info',
        ],
      ],
    ];

    // Check JSON support in database.
    $class = 'no-known-error';
    $requirement = $this
      ->t('Supported.');
    try {
      $this->database
        ->query('SELECT JSON_TYPE(\'1\')');
    } catch (\Exception $e) {
      $class = 'known-error';
      $status = FALSE;
      $requirement = $this
        ->t('Not supported.');
    }
    $build['data']['#rows'][] = [
      'class' => [
        $class,
      ],
      'data' => [
        'requirement' => [
          'class' => 'requirement-label',
          'data' => $this
            ->t('Database JSON support required'),
        ],
        'status' => [
          'data' => $requirement,
          'class' => 'status-info',
        ],
      ],
    ];

    // Check user roles on the site for invalid permissions.
    $class = 'no-known-error';
    $requirement = [
      $this
        ->t('None found.'),
    ];
    $user_roles = Role::loadMultiple();
    $all_permissions = array_keys(\Drupal::service('user.permissions')
      ->getPermissions());
    foreach ($user_roles as $role) {
      $role_permissions = $role
        ->getPermissions();
      $valid_role_permissions = array_intersect($role_permissions, $all_permissions);
      $invalid_role_permissions = array_diff($role_permissions, $valid_role_permissions);
      if (!empty($invalid_role_permissions)) {
        $class = 'known-error';
        $status = FALSE;
        $requirement = [
          $this
            ->t('"@permissions" of user role: "@role".', [
            '@permissions' => implode('", "', $invalid_role_permissions),
            '@role' => $role
              ->label(),
          ]),
        ];
      }
    }
    $build['data']['#rows'][] = [
      'class' => [
        $class,
      ],
      'data' => [
        'requirement' => [
          'class' => 'requirement-label',
          'data' => $this
            ->t('Invalid permissions will trigger runtime exceptions in Drupal 10. Permissions should be defined in a permissions.yml file or a permission callback. See https://www.drupal.org/node/3193348'),
        ],
        'status' => [
          'data' => join(' ', $requirement),
          'class' => 'status-info',
        ],
      ],
    ];

    // Check for deprecated or obsolete core extensions.
    $class = 'no-known-error';
    $requirement = $this
      ->t('None installed.');
    $deprecated_or_obsolete = $this->projectCollector
      ->collectCoreDeprecatedAndObsoleteExtensions();
    if (!empty($deprecated_or_obsolete)) {
      $class = 'known-error';
      $status = FALSE;
      $requirement = join(', ', $deprecated_or_obsolete);
    }
    $build['data']['#rows'][] = [
      'class' => [
        $class,
      ],
      'data' => [
        'requirement' => [
          'class' => 'requirement-label',
          'data' => $this
            ->t('Deprecated or obsolete core extensions installed. These will be removed in the next major version.'),
        ],
        'status' => [
          'data' => $requirement,
          'class' => 'status-info',
        ],
      ],
    ];

    // Save the overall status indicator in the build array. It will be
    // popped off later to be used in the summary table.
    $build['status'] = $status;
    return $build;
  }
  $build['description'] = $this
    ->t('<a href=":upgrade">Upgrades to Drupal 9 are supported from Drupal 8.8.x and Drupal 8.9.x</a>. It is suggested to update to the latest Drupal 8 version available. <a href=":platform">Several hosting platform requirements have been raised for Drupal 9</a>.', [
    ':upgrade' => 'https://www.drupal.org/docs/9/how-to-prepare-your-drupal-7-or-8-site-for-drupal-9/upgrading-a-drupal-8-site-to-drupal-9',
    ':platform' => 'https://www.drupal.org/docs/9/how-drupal-9-is-made-and-what-is-included/environment-requirements-of-drupal-9',
  ]);

  // Check Drupal version. Link to update if available.
  $core_version_info = [
    '#type' => 'markup',
    '#markup' => $this
      ->t('Version @version and up to date.', [
      '@version' => \Drupal::VERSION,
    ]),
  ];
  $has_core_update = FALSE;
  $core_update_info = $this->releaseStore
    ->get('drupal');
  if (isset($core_update_info['releases']) && is_array($core_update_info['releases'])) {

    // Find the latest release that are higher than our current and is not beta/alpha/rc.
    foreach ($core_update_info['releases'] as $version => $release) {
      $major_version = explode('.', $version)[0];
      if (version_compare($version, \Drupal::VERSION) > 0 && empty($release['version_extra']) && $major_version === '8') {
        $link = $core_update_info['link'] . '/releases/' . $version;
        $core_version_info = [
          '#type' => 'link',
          '#title' => $this
            ->t('Version @current allows to upgrade but @new is available.', [
            '@current' => \Drupal::VERSION,
            '@new' => $version,
          ]),
          '#url' => Url::fromUri($link),
        ];
        $has_core_update = TRUE;
        break;
      }
    }
  }
  if (version_compare(\Drupal::VERSION, '8.8.0') >= 0) {
    if (!$has_core_update) {
      $class = 'no-known-error';
    }
    else {
      $class = 'known-warning';
    }
  }
  else {
    $status = FALSE;
    $class = 'known-error';
  }
  $build['data']['#rows'][] = [
    'class' => $class,
    'data' => [
      'requirement' => [
        'class' => 'requirement-label',
        'data' => $this
          ->t('Drupal core should be 8.8.x or 8.9.x'),
      ],
      'status' => [
        'data' => $core_version_info,
        'class' => 'status-info',
      ],
    ],
  ];

  // Check PHP version.
  $version = PHP_VERSION;
  if (version_compare($version, '7.3.0') >= 0) {
    $class = 'no-known-error';
  }
  else {
    $class = 'known-error';
    $status = FALSE;
  }
  $build['data']['#rows'][] = [
    'class' => [
      $class,
    ],
    'data' => [
      'requirement' => [
        'class' => 'requirement-label',
        'data' => $this
          ->t('PHP version should be at least 7.3.0'),
      ],
      'status' => [
        'data' => $this
          ->t('Version @version', [
          '@version' => $version,
        ]),
        'class' => 'status-info',
      ],
    ],
  ];

  // Check database version.
  $type = $this->database
    ->databaseType();
  $version = $this->database
    ->version();

  // If running on Drupal 8, the mysql driver might
  // mis-report the database version.
  if ($this->nextMajor == 9) {
    $versionFixer = new CorrectDbServerVersion($this->database);
    $version = $versionFixer
      ->getCorrectedDbServerVersion($version);
  }

  // MariaDB databases report as MySQL. Detect MariaDB separately based on code from
  // https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%21Driver%21mysql%21Connection.php/function/Connection%3A%3AgetMariaDbVersionMatch/9.0.x
  // See also https://www.drupal.org/node/3119156 for test values.
  if ($type == 'mysql') {

    // MariaDB may prefix its version string with '5.5.5-', which should be
    // ignored.
    // @see https://github.com/MariaDB/server/blob/f6633bf058802ad7da8196d01fd19d75c53f7274/include/mysql_com.h#L42.
    $regex = '/^(?:5\\.5\\.5-)?(\\d+\\.\\d+\\.\\d+.*-mariadb.*)/i';
    preg_match($regex, $version, $matches);
    if (!empty($matches[1])) {
      $type = 'MariaDB';
      $version = $matches[1];
      $requirement = $this
        ->t('When using MariaDB, minimum version is 10.3.7');
      if (version_compare($version, '10.3.7') >= 0) {
        $class = 'no-known-error';
      }
      elseif (version_compare($version, '10.1.0') >= 0) {
        $class = 'known-warning';
        $requirement .= ' ' . $this
          ->t('Alternatively, <a href=":driver">install the MariaDB 10.1 driver for Drupal 9</a> for now.', [
          ':driver' => 'https://www.drupal.org/project/mysql56',
        ]);
      }
      else {
        $status = FALSE;
        $class = 'known-error';
        $requirement .= ' ' . $this
          ->t('Once updated to at least 10.1, you can also <a href=":driver">install the MariaDB 10.1 driver for Drupal 9</a> for now.', [
          ':driver' => 'https://www.drupal.org/project/mysql56',
        ]);
      }
    }
    else {
      $type = 'MySQL or Percona Server';
      $requirement = $this
        ->t('When using MySQL/Percona, minimum version is 5.7.8');
      if (version_compare($version, '5.7.8') >= 0) {
        $class = 'no-known-error';
      }
      elseif (version_compare($version, '5.6.0') >= 0) {
        $class = 'known-warning';
        $requirement .= ' ' . $this
          ->t('Alternatively, <a href=":driver">install the MySQL 5.6 driver for Drupal 9</a> for now.', [
          ':driver' => 'https://www.drupal.org/project/mysql56',
        ]);
      }
      else {
        $status = FALSE;
        $class = 'known-error';
        $requirement .= ' ' . $this
          ->t('Once updated to at least 5.6, you can also <a href=":driver">install the MySQL 5.6 driver for Drupal 9</a> for now.', [
          ':driver' => 'https://www.drupal.org/project/mysql56',
        ]);
      }
    }
  }
  elseif ($type == 'pgsql') {
    $type = 'PostgreSQL';
    $requirement = $this
      ->t('When using PostgreSQL, minimum version is 10 <a href=":trgm">with the pg_trgm extension</a> (The extension is not checked here).', [
      ':trgm' => 'https://www.postgresql.org/docs/10/pgtrgm.html',
    ]);
    if (version_compare($version, '10') >= 0) {
      $class = 'no-known-error';
    }
    else {
      $status = FALSE;
      $class = 'known-error';
    }
  }
  elseif ($type == 'sqlite') {
    $type = 'SQLite';
    $requirement = $this
      ->t('When using SQLite, minimum version is 3.26');
    if (version_compare($version, '3.26') >= 0) {
      $class = 'no-known-error';
    }
    else {
      $status = FALSE;
      $class = 'known-error';
    }
  }
  $build['data']['#rows'][] = [
    'class' => [
      $class,
    ],
    'data' => [
      'requirement' => [
        'class' => 'requirement-label',
        'data' => [
          '#type' => 'markup',
          '#markup' => $requirement,
        ],
      ],
      'status' => [
        'data' => $type . ' ' . $version,
        'class' => 'status-info',
      ],
    ],
  ];

  // Check Apache. Logic is based on system_requirements() code.
  $request_object = \Drupal::request();
  $software = $request_object->server
    ->get('SERVER_SOFTWARE');
  if (strpos($software, 'Apache') !== FALSE && preg_match('!^Apache/([\\d\\.]+) !', $software, $found)) {
    $version = $found[1];
    if (version_compare($version, '2.4.7') >= 0) {
      $class = 'no-known-error';
    }
    else {
      $status = FALSE;
      $class = 'known-error';
    }
    $label = $this
      ->t('Version @version', [
      '@version' => $version,
    ]);
  }
  else {
    $class = '';
    $label = $this
      ->t('Version cannot be detected or not using Apache, check manually.');
  }
  $build['data']['#rows'][] = [
    'class' => [
      $class,
    ],
    'data' => [
      'requirement' => [
        'class' => 'requirement-label',
        'data' => $this
          ->t('When using Apache, minimum version is 2.4.7'),
      ],
      'status' => [
        'data' => $label,
        'class' => 'status-info',
      ],
    ],
  ];

  // Check Drush. We only detect site-local drush for now.
  if (class_exists('\\Drush\\Drush')) {
    $version = call_user_func('\\Drush\\Drush::getMajorVersion');
    if (version_compare($version, '10') >= 0) {
      $class = 'no-known-error';
    }
    else {
      $status = FALSE;
      $class = 'known-error';
    }
    $label = $this
      ->t('Version @version', [
      '@version' => $version,
    ]);
  }
  else {
    $class = '';
    $label = $this
      ->t('Version cannot be detected, check manually.');
  }
  $build['data']['#rows'][] = [
    'class' => $class,
    'data' => [
      'requirement' => [
        'class' => 'requirement-label',
        'data' => $this
          ->t('When using Drush, minimum version is 10'),
      ],
      'status' => [
        'data' => $label,
        'class' => 'status-info',
      ],
    ],
  ];

  // Check deprecated $config_directories if after Drupal 8.8.0. On older
  // Drupal versions, the replacement is not supported and the setting may
  // be generated by platforms like ddev, leading to false positives that
  // the user should not even resolve yet before updating core.
  if (version_compare(\Drupal::VERSION, '8.8.0') >= 0) {
    $class = 'no-known-error';
    $requirement = $this
      ->t('Use of $config_directories in settings.php is deprecated.');
    $label = $this
      ->t('Not used');
    $is_deprecated = $this
      ->isDeprecatedConfigDirectorySettingUsed();
    if ($is_deprecated !== FALSE) {
      $status = FALSE;
      $class = 'known-error';
      if ($is_deprecated === TRUE) {
        $label = $this
          ->t('Deprecated configuration used');
        $requirement .= ' ' . $this
          ->t('<a href=":settings">Use $settings[\'config_sync_directory\'] instead.</a>', [
          ':settings' => 'https://www.drupal.org/node/3018145',
        ]);
      }
      else {
        $label = $this
          ->t('Deprecated and new configuration used');
        $requirement .= ' ' . $this
          ->t('<a href=":settings">Use $settings[\'config_sync_directory\'] only.</a>', [
          ':settings' => 'https://www.drupal.org/node/3018145',
        ]);
      }
    }
    $build['data']['#rows'][] = [
      'class' => $class,
      'data' => [
        'requirement' => [
          'class' => 'requirement-label',
          'data' => [
            '#type' => 'markup',
            '#markup' => $requirement,
          ],
        ],
        'status' => [
          'data' => $label,
          'class' => 'status-info',
        ],
      ],
    ];
  }

  // Save the overall status indicator in the build array. It will be
  // popped off later to be used in the summary table.
  $build['status'] = $status;
  return $build;
}