diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 6c8ee5e5f257297c5bacfcd0083b79dda6f2b3c8..d05298d4bf4425425d8392c38a5e749c158b9c85 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -19,9 +19,20 @@ /** * Minimum supported version of PHP. + * + * Drupal cannot be installed on versions of PHP older than this version. */ const DRUPAL_MINIMUM_PHP = '5.5.9'; +/** + * Minimum recommended version of PHP. + * + * Sites installing Drupal on PHP versions lower than this will see a warning + * message, but Drupal can still be installed. Used for (e.g.) PHP versions + * that have reached their EOL or will in the near future. + */ +const DRUPAL_RECOMMENDED_PHP = '7.1'; + /** * Minimum recommended value of PHP memory_limit. * diff --git a/core/modules/simpletest/src/InstallerTestBase.php b/core/modules/simpletest/src/InstallerTestBase.php index 92f33dcf5fb18337c372bbcf446e58f5884a2a0f..96d98e05aa209079a9c77230318e08f2b371830c 100644 --- a/core/modules/simpletest/src/InstallerTestBase.php +++ b/core/modules/simpletest/src/InstallerTestBase.php @@ -122,6 +122,9 @@ protected function setUp() { // Select profile. $this->setUpProfile(); + // Address the requirements problem screen, if any. + $this->setUpRequirementsProblem(); + // Configure settings. $this->setUpSettings(); @@ -195,6 +198,23 @@ protected function setUpSettings() { $this->drupalPostForm(NULL, $edit, $this->translations['Save and continue']); } + /** + * Installer step: Requirements problem. + * + * Override this method to test specific requirements warnings or errors + * during the installer. + * + * @see system_requirements() + */ + protected function setUpRequirementsProblem() { + // By default, skip the "recommended PHP version" warning on older test + // environments. This allows the installer to be tested consistently on + // both recommended PHP versions and older (but still supported) versions. + if (version_compare(phpversion(), DRUPAL_RECOMMENDED_PHP) < 0) { + $this->continueOnExpectedWarnings(['PHP']); + } + } + /** * Final installer step: Configure site. */ @@ -218,4 +238,44 @@ protected function refreshVariables() { } } + /** + * Continues installation when an expected warning is found. + * + * @param string[] $expected_warnings + * A list of warning summaries to expect on the requirements screen (e.g. + * 'PHP', 'PHP OPcode caching', etc.). If only the expected warnings + * are found, the test will click the "continue anyway" link to go to the + * next screen of the installer. If an expected warning is not found, or if + * a warning not in the list is present, a fail is raised. + */ + protected function continueOnExpectedWarnings($expected_warnings = []) { + // Don't try to continue if there are errors. + if (strpos($this->getTextContent(), 'Errors found') !== FALSE) { + return; + } + // Allow only details elements that are directly after the warning header + // or each other. There is no guaranteed wrapper we can rely on across + // distributions. When there are multiple warnings, the selectors will be: + // - h3#warning+details summary + // - h3#warning+details+details summary + // - etc. + // We add one more selector than expected warnings to confirm that there + // isn't any other warning before clicking the link. + // @todo Make this more reliable in + // https://www.drupal.org/project/drupal/issues/2927345. + $selectors = []; + for ($i = 0; $i <= count($expected_warnings); $i++) { + $selectors[] = 'h3#warning' . implode('', array_fill(0, $i + 1, '+details')) . ' summary'; + } + $warning_elements = $this->cssSelect(implode(', ', $selectors)); + + // Confirm that there are only the expected warnings. + $warnings = []; + foreach ($warning_elements as $warning) { + $warnings[] = trim((string) $warning); + } + $this->assertEqual($expected_warnings, $warnings); + $this->clickLink('continue anyway'); + } + } diff --git a/core/modules/system/src/Tests/Installer/DistributionProfileTranslationQueryTest.php b/core/modules/system/src/Tests/Installer/DistributionProfileTranslationQueryTest.php index 1b6868351d2f172c282ac25f858c150ba10306bf..85b1e44ec43cab49b9922495b1cc70d19c40d717 100644 --- a/core/modules/system/src/Tests/Installer/DistributionProfileTranslationQueryTest.php +++ b/core/modules/system/src/Tests/Installer/DistributionProfileTranslationQueryTest.php @@ -64,6 +64,27 @@ protected function visitInstaller() { // The unrouted URL assembler does not exist at this point, so we build the // URL ourselves. $this->drupalGet($GLOBALS['base_url'] . '/core/install.php' . '?langcode=fr'); + } + + /** + * {@inheritdoc} + */ + protected function setUpLanguage() { + // This step is skipped, because the distribution profile uses a fixed + // language. + } + + /** + * {@inheritdoc} + */ + protected function setUpProfile() { + // This step is skipped, because there is a distribution profile. + } + + /** + * {@inheritdoc} + */ + protected function setUpSettings() { // The language should have been automatically detected, all following // screens should be translated already. $elements = $this->xpath('//input[@type="submit"]/@value'); @@ -80,21 +101,8 @@ protected function visitInstaller() { $this->assertRaw($this->info['distribution']['install']['theme']); // Verify that the "Choose profile" step does not appear. $this->assertNoText('profile'); - } - /** - * {@inheritdoc} - */ - protected function setUpLanguage() { - // This step is skipped, because the distribution profile uses a fixed - // language. - } - - /** - * {@inheritdoc} - */ - protected function setUpProfile() { - // This step is skipped, because there is a distribution profile. + parent::setUpSettings(); } /** diff --git a/core/modules/system/src/Tests/Installer/DistributionProfileTranslationTest.php b/core/modules/system/src/Tests/Installer/DistributionProfileTranslationTest.php index 799affe37bda7c9def90e6d39e39e164bd1c2cce..a4c8091ecdf43773921d5ffa9316f8c80453fb7e 100644 --- a/core/modules/system/src/Tests/Installer/DistributionProfileTranslationTest.php +++ b/core/modules/system/src/Tests/Installer/DistributionProfileTranslationTest.php @@ -59,7 +59,27 @@ protected function visitInstaller() { file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de')); parent::visitInstaller(); + } + + /** + * {@inheritdoc} + */ + protected function setUpLanguage() { + // This step is skipped, because the distribution profile uses a fixed + // language. + } + + /** + * {@inheritdoc} + */ + protected function setUpProfile() { + // This step is skipped, because there is a distribution profile. + } + /** + * {@inheritdoc} + */ + protected function setUpSettings() { // The language should have been automatically detected, all following // screens should be translated already. $elements = $this->xpath('//input[@type="submit"]/@value'); @@ -76,22 +96,10 @@ protected function visitInstaller() { $this->assertRaw($this->info['distribution']['install']['theme']); // Verify that the "Choose profile" step does not appear. $this->assertNoText('profile'); - } - /** - * {@inheritdoc} - */ - protected function setUpLanguage() { - // This step is skipped, because the distribution profile uses a fixed - // language. + parent::setUpSettings(); } - /** - * {@inheritdoc} - */ - protected function setUpProfile() { - // This step is skipped, because there is a distribution profile. - } /** * Confirms that the installation succeeded. diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingInstallationTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingInstallationTest.php index 3ba045a0ad6083adfd632666b89106018d0df79e..442de6c107d9ad34f8d86bdab833db91b9a3b6e0 100644 --- a/core/modules/system/src/Tests/Installer/InstallerExistingInstallationTest.php +++ b/core/modules/system/src/Tests/Installer/InstallerExistingInstallationTest.php @@ -32,6 +32,7 @@ public function testInstaller() { $this->visitInstaller(); $this->setUpLanguage(); $this->setUpProfile(); + $this->setUpRequirementsProblem(); $this->setUpSettings(); $this->assertRaw('Drupal already installed'); } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index ce48afe725778ef04158ed85f304d4ac1b071708..ef6f124f511861cc4db244f3d0b127259fbf38cc 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -157,7 +157,11 @@ function system_requirements($phase) { } } - // Test PHP version and show link to phpinfo() if it's available + // Verify the user is running a supported PHP version. + // If the site is running a recommended version of PHP, just display it + // as an informational message on the status report. This will be overridden + // with an error or warning if the site is running older PHP versions for + // which Drupal has already or will soon drop support. $phpversion = $phpversion_label = phpversion(); if (function_exists('phpinfo')) { if ($phase === 'runtime') { @@ -169,6 +173,8 @@ function system_requirements($phase) { ]; } else { + // @todo Revisit whether this description makes sense in + // https://www.drupal.org/project/drupal/issues/2927318. $requirements['php'] = [ 'title' => t('PHP'), 'value' => $phpversion_label, @@ -183,11 +189,18 @@ function system_requirements($phase) { // If PHP is old, it's not safe to continue with the requirements check. return $requirements; } + // @todo Warn about specific end dates for our PHP 5.5, 5.6, and 7.0 support + // once each is set. + // @see https://www.drupal.org/project/drupal/issues/2927344 + if ((version_compare($phpversion, DRUPAL_RECOMMENDED_PHP) < 0) && ($phase === 'install' || $phase === 'runtime')) { + $requirements['php']['description'] = t('Your PHP installation is running version %version. Support for this version will be dropped in a future Drupal release. Upgrade to PHP version %recommended or higher to ensure your site continues to receive Drupal updates and remains secure. See PHP\'s version support documentation and the Drupal 8 PHP requirements handbook page for more information.', ['%version' => $phpversion, '%recommended' => DRUPAL_RECOMMENDED_PHP, ':php_requirements' => 'https://www.drupal.org/docs/8/system-requirements/php']); + $requirements['php']['severity'] = REQUIREMENT_WARNING; + } // Suggest to update to at least 5.5.21 or 5.6.5 for disabling multiple // statements. if (($phase === 'install' || \Drupal::database()->driver() === 'mysql') && !SystemRequirements::phpVersionWithPdoDisallowMultipleStatements($phpversion)) { - $requirements['php'] = [ + $requirements['php_multiple_statement'] = [ 'title' => t('PHP (multiple statement disabling)'), 'value' => $phpversion_label, 'description' => t('PHP versions higher than 5.6.5 or 5.5.21 provide built-in SQL injection protection for mysql databases. It is recommended to update.'),