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.'),