diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 499f552..5dee0a0 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -25,7 +25,15 @@ * @todo Move this to an appropriate autoloadable class. See * https://www.drupal.org/project/drupal/issues/2908079 */ -const DRUPAL_MINIMUM_PHP = '5.5.9'; +const DRUPAL_MINIMUM_PHP = '7.0.0'; + +/** + * Updates cannot be run on versions of PHP older than this version. + * + * @todo Move this to an appropriate autoloadable class. See + * https://www.drupal.org/project/drupal/issues/2908079 + */ +const DRUPAL_MINIMUM_UPDATE_PHP = '5.5.9'; /** * Minimum recommended version of PHP. diff --git a/core/includes/install.inc b/core/includes/install.inc index 70c82b4..13f0372 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -1116,7 +1116,7 @@ function install_profile_info($profile, $langcode = 'en') { 'description' => '', 'version' => NULL, 'hidden' => FALSE, - 'php' => DRUPAL_MINIMUM_PHP, + 'php' => DRUPAL_MINIMUM_UPDATE_PHP, 'config_install_path' => NULL, ]; $profile_path = drupal_get_path('profile', $profile); diff --git a/core/lib/Drupal/Core/Extension/ModuleExtensionList.php b/core/lib/Drupal/Core/Extension/ModuleExtensionList.php index c8f492b..07a1d83 100644 --- a/core/lib/Drupal/Core/Extension/ModuleExtensionList.php +++ b/core/lib/Drupal/Core/Extension/ModuleExtensionList.php @@ -28,7 +28,7 @@ class ModuleExtensionList extends ExtensionList { 'description' => '', 'package' => 'Other', 'version' => NULL, - 'php' => DRUPAL_MINIMUM_PHP, + 'php' => DRUPAL_MINIMUM_UPDATE_PHP, ]; /** diff --git a/core/lib/Drupal/Core/Extension/ProfileExtensionList.php b/core/lib/Drupal/Core/Extension/ProfileExtensionList.php index 7c41516..bb054d0 100644 --- a/core/lib/Drupal/Core/Extension/ProfileExtensionList.php +++ b/core/lib/Drupal/Core/Extension/ProfileExtensionList.php @@ -22,7 +22,7 @@ class ProfileExtensionList extends ExtensionList { 'description' => '', 'package' => 'Other', 'version' => NULL, - 'php' => DRUPAL_MINIMUM_PHP, + 'php' => DRUPAL_MINIMUM_UPDATE_PHP, ]; /** diff --git a/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php b/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php index c4d39ef..aef0d4e 100644 --- a/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php +++ b/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php @@ -21,7 +21,7 @@ class ThemeEngineExtensionList extends ExtensionList { 'description' => '', 'package' => 'Other', 'version' => NULL, - 'php' => DRUPAL_MINIMUM_PHP, + 'php' => DRUPAL_MINIMUM_UPDATE_PHP, ]; /** diff --git a/core/lib/Drupal/Core/Extension/ThemeExtensionList.php b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php index 07a2f3f..5468c3f 100644 --- a/core/lib/Drupal/Core/Extension/ThemeExtensionList.php +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php @@ -48,7 +48,7 @@ class ThemeExtensionList extends ExtensionList { 'comment_user_verification', ], 'screenshot' => 'screenshot.png', - 'php' => DRUPAL_MINIMUM_PHP, + 'php' => DRUPAL_MINIMUM_UPDATE_PHP, 'libraries' => [], 'libraries_extend' => [], 'libraries_override' => [], diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 960bb7d..d7bef10 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -186,24 +186,29 @@ function system_requirements($phase) { ]; } - if (version_compare($phpversion, DRUPAL_MINIMUM_PHP) < 0) { - $requirements['php']['description'] = t('Your PHP installation is too old. Drupal requires at least PHP %version.', ['%version' => DRUPAL_MINIMUM_PHP]); + $is_test_site = drupal_valid_test_ua() || getenv('DRUPAL_DEV_SITE_PATH'); + if (version_compare($phpversion, DRUPAL_MINIMUM_UPDATE_PHP) < 0) { + $requirements['php']['description'] = t('Your PHP installation is too old. Drupal requires at least PHP %version.', ['%version' => DRUPAL_MINIMUM_UPDATE_PHP]); $requirements['php']['severity'] = REQUIREMENT_ERROR; - // If PHP is old, it's not safe to continue with the requirements check. return $requirements; } - if ((version_compare($phpversion, DRUPAL_RECOMMENDED_PHP) < 0) && ($phase === 'install' || $phase === 'runtime')) { - // Warn if still on PHP 5. If at least PHP 7.0, relax from "warning" to - // "info", and show it at runtime only, to not scare users while installing. - if (version_compare($phpversion, '7.0') < 0) { - $requirements['php']['description'] = t('Drupal will drop support for this version on March 6, 2019. Upgrade to PHP version %recommended or higher to ensure your site can receive updates and remain secure. See PHP\'s version support documentation and the Drupal 8 PHP requirements handbook page for more information.', ['%recommended' => DRUPAL_RECOMMENDED_PHP, ':php_requirements' => 'https://www.drupal.org/docs/8/system-requirements/php']); + if (version_compare($phpversion, DRUPAL_MINIMUM_PHP) < 0) { + // Allow test installations of Drupal on the minimum update PHP. + if ($phase === 'update') { + $requirements['php']['description'] = t('Your PHP installation is too old. Drupal requires at least PHP %version.', ['%version' => DRUPAL_MINIMUM_PHP]); $requirements['php']['severity'] = REQUIREMENT_WARNING; } - else { - if ($phase === 'runtime') { - $requirements['php']['description'] = t('It is recommended to upgrade to PHP version %recommended or higher for the best ongoing support. See PHP\'s version support documentation and the Drupal 8 PHP requirements handbook page for more information.', ['%recommended' => DRUPAL_RECOMMENDED_PHP, ':php_requirements' => 'https://www.drupal.org/docs/8/system-requirements/php']); - $requirements['php']['severity'] = REQUIREMENT_INFO; - } + elseif (!$is_test_site) { + $requirements['php']['description'] = t('Your PHP installation is too old. Drupal requires at least PHP %version.', ['%version' => DRUPAL_MINIMUM_PHP]); + $requirements['php']['severity'] = REQUIREMENT_ERROR; + // If PHP is old, it's not safe to continue with the requirements check. + return $requirements; + } + } + if (version_compare($phpversion, DRUPAL_RECOMMENDED_PHP) < 0) { + if ($phase === 'runtime') { + $requirements['php']['description'] = t('It is recommended to upgrade to PHP version %recommended or higher for the best ongoing support. See PHP\'s version support documentation and the Drupal 8 PHP requirements handbook page for more information.', ['%recommended' => DRUPAL_RECOMMENDED_PHP, ':php_requirements' => 'https://www.drupal.org/docs/8/system-requirements/php']); + $requirements['php']['severity'] = REQUIREMENT_INFO; } } diff --git a/core/modules/system/tests/src/Functional/Update/SystemRequirementsTest.php b/core/modules/system/tests/src/Functional/Update/SystemRequirementsTest.php new file mode 100644 index 0000000..c9ad3b6 --- /dev/null +++ b/core/modules/system/tests/src/Functional/Update/SystemRequirementsTest.php @@ -0,0 +1,42 @@ +drupalCreateUser(['administer software updates']); + $this->drupalLogin($user); + $url = Url::fromRoute('system.db_update'); + $this->drupalGet($url); + $warning = sprintf('Your PHP installation is too old. Drupal requires at least PHP %s.', DRUPAL_MINIMUM_PHP); + + if (version_compare(phpversion(), DRUPAL_MINIMUM_PHP) < 0) { + $this->assertText('Warnings found'); + $this->assertText($warning); + } + else { + $this->assertNoText('Warnings found'); + $this->assertNoText($warning); + } + } + +} diff --git a/core/modules/system/tests/src/Functional/Update/UpdateScriptTest.php b/core/modules/system/tests/src/Functional/Update/UpdateScriptTest.php index da215d4..a52274e 100644 --- a/core/modules/system/tests/src/Functional/Update/UpdateScriptTest.php +++ b/core/modules/system/tests/src/Functional/Update/UpdateScriptTest.php @@ -5,6 +5,7 @@ use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\UpdateUiTrait; /** * Tests the update script access and functionality. @@ -13,6 +14,8 @@ */ class UpdateScriptTest extends BrowserTestBase { + use UpdateUiTrait; + /** * Modules to enable. * @@ -99,6 +102,7 @@ public function testRequirements() { // If there are no requirements warnings or errors, we expect to be able to // go through the update process uninterrupted. $this->drupalGet($this->updateUrl, ['external' => TRUE]); + $this->updateRequirementsProblem(); $this->clickLink(t('Continue')); $this->assertText(t('No pending updates.'), 'End of update process was reached.'); // Confirm that all caches were cleared. @@ -110,6 +114,7 @@ public function testRequirements() { // First, run this test with pending updates to make sure they can be run // successfully. + $this->drupalLogin($this->updateUser); $update_script_test_config->set('requirement_type', REQUIREMENT_WARNING)->save(); drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1); $this->drupalGet($this->updateUrl, ['external' => TRUE]); @@ -177,6 +182,7 @@ public function testNoUpdateFunctionality() { // Click through update.php with 'administer software updates' permission. $this->drupalLogin($this->updateUser); $this->drupalGet($this->updateUrl, ['external' => TRUE]); + $this->updateRequirementsProblem(); $this->clickLink(t('Continue')); $this->assertText(t('No pending updates.')); $this->assertNoLink('Administration pages'); @@ -188,6 +194,7 @@ public function testNoUpdateFunctionality() { $admin_user = $this->drupalCreateUser(['administer software updates', 'access administration pages']); $this->drupalLogin($admin_user); $this->drupalGet($this->updateUrl, ['external' => TRUE]); + $this->updateRequirementsProblem(); $this->clickLink(t('Continue')); $this->assertText(t('No pending updates.')); $this->assertLink('Administration pages'); @@ -220,6 +227,7 @@ public function testSuccessfulUpdateFunctionality() { $admin_user = $this->drupalCreateUser(['administer software updates', 'access administration pages', 'access site reports', 'access site in maintenance mode']); $this->drupalLogin($admin_user); $this->drupalGet($this->updateUrl, ['external' => TRUE]); + $this->updateRequirementsProblem(); $this->clickLink(t('Continue')); $this->clickLink(t('Apply pending updates')); $this->checkForMetaRefresh(); @@ -287,6 +295,7 @@ public function testSuccessfulMultilingualUpdateFunctionality() { // Click through update.php with 'access administration pages' and // 'access site reports' permissions. $this->drupalGet($this->updateUrl, ['external' => TRUE]); + $this->updateRequirementsProblem(); $this->clickLink(t('Continue')); $this->clickLink(t('Apply pending updates')); $this->checkForMetaRefresh(); @@ -319,6 +328,7 @@ protected function runUpdates($maintenance_mode) { $this->assertNoText('Operating in maintenance mode.'); } $this->drupalGet($this->updateUrl, ['external' => TRUE]); + $this->updateRequirementsProblem(); $this->clickLink(t('Continue')); $this->clickLink(t('Apply pending updates')); $this->checkForMetaRefresh(); diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php index 9810f34..5ea1e58 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php +++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php @@ -244,12 +244,7 @@ protected function setUpSettings() { * @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(), '7.0') < 0) { - $this->continueOnExpectedWarnings(['PHP']); - } + // Do nothing. } /** diff --git a/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php b/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php index a127ab4..d795f6b 100644 --- a/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php +++ b/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php @@ -10,6 +10,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Language\Language; use Drupal\Core\Url; +use Drupal\Tests\UpdateUiTrait; use Drupal\user\Entity\User; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpFoundation\Request; @@ -42,6 +43,7 @@ abstract class UpdatePathTestBase extends BrowserTestBase { use SchemaCheckTestTrait; + use UpdateUiTrait; /** * Modules to enable after the database is loaded. @@ -295,6 +297,7 @@ protected function runUpdates() { ]); $this->drupalGet($this->updateUrl); + $this->updateRequirementsProblem(); $this->clickLink(t('Continue')); $this->doSelectionTest(); diff --git a/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php b/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php index 9c5fd58..cba97b8 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php @@ -259,6 +259,6 @@ class TestThemeEngineExtensionList extends ThemeEngineExtensionList { } -if (!defined('DRUPAL_MINIMUM_PHP')) { - define('DRUPAL_MINIMUM_PHP', '5.5.9'); +if (!defined('DRUPAL_MINIMUM_UPDATE_PHP')) { + define('DRUPAL_MINIMUM_UPDATE_PHP', '5.5.9'); } diff --git a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php index 97a5e93..841073b 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php @@ -146,6 +146,6 @@ protected function themeRegistryRebuild() { } -if (!defined('DRUPAL_MINIMUM_PHP')) { - define('DRUPAL_MINIMUM_PHP', '5.5.9'); +if (!defined('DRUPAL_MINIMUM_UPDATE_PHP')) { + define('DRUPAL_MINIMUM_UPDATE_PHP', '5.5.9'); } diff --git a/core/tests/Drupal/Tests/UpdateUiTrait.php b/core/tests/Drupal/Tests/UpdateUiTrait.php new file mode 100644 index 0000000..1736ab0 --- /dev/null +++ b/core/tests/Drupal/Tests/UpdateUiTrait.php @@ -0,0 +1,61 @@ +continueOnExpectedWarnings(['PHP']); + } + } + + /** + * 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 "try again" link to go to the + * next screen of the update. 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($warning->getText()); + } + $this->assertEquals($expected_warnings, $warnings); + $this->clickLink('try again'); + $this->checkForMetaRefresh(); + } + +}