Issue #3265325 by xjm, Wim Leers, daffie: Raise a warning instead of an error when installing on MINIMUM_SUPPORTED_PHP
(cherry picked from commit e8b2bec983
)
merge-requests/1575/head
parent
e5590b2254
commit
3e6b51d96d
|
@ -5,6 +5,7 @@ namespace Drupal\Tests\config\Functional;
|
|||
use Drupal\FunctionalTests\Installer\InstallerTestBase;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\Tests\RequirementsPageTrait;
|
||||
|
||||
/**
|
||||
* Tests install profile config overrides can not add unmet dependencies.
|
||||
|
@ -13,6 +14,8 @@ use Drupal\Core\Serialization\Yaml;
|
|||
*/
|
||||
class ConfigInstallProfileUnmetDependenciesTest extends InstallerTestBase {
|
||||
|
||||
use RequirementsPageTrait;
|
||||
|
||||
/**
|
||||
* The installation profile to install.
|
||||
*
|
||||
|
|
|
@ -294,27 +294,17 @@ function system_requirements($phase) {
|
|||
':php_requirements' => 'https://www.drupal.org/docs/9/how-drupal-9-is-made-and-what-is-included/environment-requirements-of-drupal-9#s-php-version-requirement',
|
||||
]
|
||||
);
|
||||
$requirements['php']['severity'] = REQUIREMENT_ERROR;
|
||||
|
||||
// If the PHP version is also below the absolute minimum allowed, it's not
|
||||
// safe to continue with the requirements check.
|
||||
// safe to continue with the requirements check, and should always be an
|
||||
// error.
|
||||
if (version_compare($phpversion, \Drupal::MINIMUM_PHP) < 0) {
|
||||
$requirements['php']['severity'] = REQUIREMENT_ERROR;
|
||||
return $requirements;
|
||||
}
|
||||
// Otherwise downgrade the error to a warning during updates. Even if there
|
||||
// are some problems with the site's PHP version, it's still better for the
|
||||
// site to keep its Drupal codebase up to date.
|
||||
elseif ($phase === 'update') {
|
||||
$requirements['php']['severity'] = REQUIREMENT_WARNING;
|
||||
}
|
||||
// Since we allow sites with unsupported PHP versions to still run Drupal
|
||||
// updates, we also need to be able to run tests with those PHP versions,
|
||||
// which requires the ability to install test sites. Not all tests are
|
||||
// required to pass on these PHP versions, but we want to monitor which
|
||||
// ones do and don't.
|
||||
elseif ($phase === 'install' && drupal_valid_test_ua()) {
|
||||
$requirements['php']['severity'] = REQUIREMENT_INFO;
|
||||
}
|
||||
// Otherwise, the message should be an error at runtime, and a warning
|
||||
// during installation or update.
|
||||
$requirements['php']['severity'] = ($phase === 'runtime') ? REQUIREMENT_ERROR : REQUIREMENT_WARNING;
|
||||
}
|
||||
// For PHP versions that are still supported but no longer recommended,
|
||||
// inform users of what's recommended, allowing them to take action before it
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\system\Functional\System;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\RequirementsPageTrait;
|
||||
|
||||
/**
|
||||
* Tests the output of PHP requirements on the status report.
|
||||
*
|
||||
* @group system
|
||||
*/
|
||||
class PhpRequirementTest extends BrowserTestBase {
|
||||
|
||||
use RequirementsPageTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'administer site configuration',
|
||||
'access site reports',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// By default, Drupal installation (and BrowserTestBase) do not configure
|
||||
// trusted host patterns, which leads to an error on the status report.
|
||||
// Configure them so that the site is properly configured and so that we
|
||||
// can cleanly test the errors related to PHP versions.
|
||||
$settings['settings']['trusted_host_patterns'] = (object) [
|
||||
'value' => ['^' . preg_quote(\Drupal::request()->getHost()) . '$'],
|
||||
'required' => TRUE,
|
||||
];
|
||||
|
||||
$this->writeSettings($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests status report messages regarding the PHP version.
|
||||
*/
|
||||
public function testStatusPage() {
|
||||
// Go to Administration.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$phpversion = phpversion();
|
||||
// Verify that the PHP version is shown on the page.
|
||||
$this->assertSession()->pageTextContains($phpversion);
|
||||
|
||||
// Verify that an error is displayed about the PHP version if it is below
|
||||
// the minimum supported PHP.
|
||||
if (version_compare($phpversion, \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
|
||||
$this->assertErrorSummaries(['PHP']);
|
||||
$this->assertSession()->pageTextContains('Your PHP installation is too old. Drupal requires at least PHP ' . \Drupal::MINIMUM_SUPPORTED_PHP);
|
||||
}
|
||||
// Otherwise, there should be no error.
|
||||
else {
|
||||
$this->assertSession()->pageTextNotContains('Your PHP installation is too old. Drupal requires at least PHP ' . \Drupal::MINIMUM_SUPPORTED_PHP);
|
||||
$this->assertSession()->pageTextNotContains('Errors found');
|
||||
}
|
||||
|
||||
// There should be an informational message if the PHP version is below the
|
||||
// recommended version.
|
||||
if (version_compare($phpversion, \Drupal::RECOMMENDED_PHP) < 0) {
|
||||
$this->assertSession()->pageTextContains('It is recommended to upgrade to PHP version ' . \Drupal::RECOMMENDED_PHP . ' or higher');
|
||||
}
|
||||
// Otherwise, the message should not be there.
|
||||
else {
|
||||
$this->assertSession()->pageTextNotContains('It is recommended to upgrade to PHP version ' . \Drupal::RECOMMENDED_PHP . ' or higher');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -47,6 +47,15 @@ class InstallerConfigDirectorySetNoDirectoryErrorTest extends InstallerTestBase
|
|||
// screen.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpRequirementsProblem() {
|
||||
// The parent method asserts that there are no requirements errors, but
|
||||
// this test expects a requirements error in the test method below.
|
||||
// Therefore, we override this method to suppress the parent's assertions.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -50,6 +50,15 @@ class InstallerExistingBrokenDatabaseSettingsTest extends InstallerTestBase {
|
|||
// This form will never be reached.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpRequirementsProblem() {
|
||||
// The parent method asserts that there are no requirements errors, but
|
||||
// this test expects a requirements error in the test method below.
|
||||
// Therefore, we override this method to suppress the parent's assertions.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -34,6 +34,15 @@ EOF;
|
|||
// There are errors therefore there is nothing to do here.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpRequirementsProblem() {
|
||||
// The parent method asserts that there are no requirements errors, but
|
||||
// this test expects a requirements error in the test method below.
|
||||
// Therefore, we override this method to suppress the parent's assertions.
|
||||
}
|
||||
|
||||
/**
|
||||
* Final installer step: Configure site.
|
||||
*/
|
||||
|
|
|
@ -26,6 +26,15 @@ class InstallerProfileRequirementsTest extends InstallerTestBase {
|
|||
// This form will never be reached.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpRequirementsProblem() {
|
||||
// The parent method asserts that there are no requirements errors, but
|
||||
// this test expects a requirements error in the test method below.
|
||||
// Therefore, we override this method to suppress the parent's assertions.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -252,7 +252,9 @@ abstract class InstallerTestBase extends BrowserTestBase {
|
|||
* @see system_requirements()
|
||||
*/
|
||||
protected function setUpRequirementsProblem() {
|
||||
// Do nothing.
|
||||
if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
|
||||
$this->continueOnExpectedWarnings(['PHP']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,18 +47,32 @@ ENDPO;
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpProfile() {
|
||||
// Do nothing, because this test only tests the language installation
|
||||
// step's results.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpSettings() {
|
||||
// Do nothing, because this test only tests the language installation
|
||||
// step's results.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpRequirementsProblem() {
|
||||
// Do nothing, because this test only tests the language installation
|
||||
// step's results.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpSite() {
|
||||
// Do nothing, because this test only tests the language installation
|
||||
// step's results.
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -85,9 +85,6 @@ class QuickStartTest extends TestCase {
|
|||
* Tests the quick-start command.
|
||||
*/
|
||||
public function testQuickStartCommand() {
|
||||
if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
if (version_compare(\SQLite3::version()['versionString'], Tasks::SQLITE_MINIMUM_VERSION) < 0) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
@ -139,41 +136,10 @@ class QuickStartTest extends TestCase {
|
|||
$process->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the installer throws a requirement error on older PHP versions.
|
||||
*/
|
||||
public function testPhpRequirement() {
|
||||
if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) >= 0) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
$install_command = [
|
||||
$this->php,
|
||||
'core/scripts/drupal',
|
||||
'quick-start',
|
||||
'standard',
|
||||
"--site-name='Test site {$this->testDb->getDatabasePrefix()}'",
|
||||
'--suppress-login',
|
||||
];
|
||||
$process = new Process($install_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
|
||||
$process->setTimeout(500);
|
||||
$process->run();
|
||||
$error_output = $process->getErrorOutput();
|
||||
$this->assertStringContainsString('Your PHP installation is too old.', $error_output);
|
||||
$this->assertStringContainsString('Drupal requires at least PHP', $error_output);
|
||||
$this->assertStringContainsString(\Drupal::MINIMUM_SUPPORTED_PHP, $error_output);
|
||||
|
||||
// Stop the web server.
|
||||
$process->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the quick-start commands.
|
||||
*/
|
||||
public function testQuickStartInstallAndServerCommands() {
|
||||
if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
if (version_compare(\SQLite3::version()['versionString'], Tasks::SQLITE_MINIMUM_VERSION) < 0) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
|
|
@ -24,10 +24,6 @@ trait RequirementsPageTrait {
|
|||
/**
|
||||
* Continues installation when the expected warnings are found.
|
||||
*
|
||||
* This function is no longer called by any core test, but it is retained for
|
||||
* use by contrib/custom tests. It is not deprecated, because it remains the
|
||||
* recommended function to call for its purpose.
|
||||
*
|
||||
* @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
|
||||
|
@ -43,38 +39,70 @@ trait RequirementsPageTrait {
|
|||
}
|
||||
|
||||
/**
|
||||
* Assert the given warning summaries are present on the page.
|
||||
* Asserts the given warning summaries are present on the page.
|
||||
*
|
||||
* If an expected warning is not found, or if a warning not in the list is
|
||||
* present, a fail is raised.
|
||||
*
|
||||
* @param string[] $warning_summaries
|
||||
* @param string[] $summaries
|
||||
* A list of warning summaries to expect on the requirements screen (e.g.
|
||||
* 'PHP', 'PHP OPcode caching', etc.).
|
||||
*/
|
||||
protected function assertWarningSummaries(array $warning_summaries) {
|
||||
// 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:
|
||||
protected function assertWarningSummaries(array $summaries) {
|
||||
$this->assertRequirementSummaries($summaries, 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the given error summaries are present on the page.
|
||||
*
|
||||
* If an expected error is not found, or if an error not in the list is
|
||||
* present, a fail is raised.
|
||||
*
|
||||
* @param string[] $summaries
|
||||
* A list of error summaries to expect on the requirements screen (e.g.
|
||||
* 'PHP', 'PHP OPcode caching', etc.).
|
||||
*/
|
||||
protected function assertErrorSummaries(array $summaries) {
|
||||
$this->assertRequirementSummaries($summaries, 'error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the given requirements section summaries are present on the page.
|
||||
*
|
||||
* If an expected requirements message is not found, or if a message not in
|
||||
* the list is present, a fail is raised.
|
||||
*
|
||||
* @param string[] $summaries
|
||||
* A list of warning summaries to expect on the requirements screen (e.g.
|
||||
* 'PHP', 'PHP OPcode caching', etc.).
|
||||
* @param string $type
|
||||
* The type of requirement, either 'warning' or 'error'.
|
||||
*/
|
||||
protected function assertRequirementSummaries(array $summaries, string $type) {
|
||||
// Allow only details elements that are directly after the warning/error
|
||||
// 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.
|
||||
// For errors, the selectors are the same except that they are h3#error.
|
||||
// We add one more selector than expected requirements to confirm that
|
||||
// there isn't any other requirement message before clicking the link.
|
||||
// @todo Make this more reliable in
|
||||
// https://www.drupal.org/project/drupal/issues/2927345.
|
||||
$selectors = [];
|
||||
for ($i = 0; $i <= count($warning_summaries); $i++) {
|
||||
$selectors[] = 'h3#warning' . implode('', array_fill(0, $i + 1, '+details')) . ' summary';
|
||||
for ($i = 0; $i <= count($summaries); $i++) {
|
||||
$selectors[] = 'h3#' . $type . implode('', array_fill(0, $i + 1, '+details')) . ' summary';
|
||||
}
|
||||
$warning_elements = $this->cssSelect(implode(', ', $selectors));
|
||||
$elements = $this->cssSelect(implode(', ', $selectors));
|
||||
|
||||
// Confirm that there are only the expected warnings.
|
||||
$warnings = [];
|
||||
foreach ($warning_elements as $warning) {
|
||||
$warnings[] = trim($warning->getText());
|
||||
// Confirm that there are only the expected requirements.
|
||||
$requirements = [];
|
||||
foreach ($elements as $requirement) {
|
||||
$requirements[] = trim($requirement->getText());
|
||||
}
|
||||
$this->assertEquals($warning_summaries, $warnings);
|
||||
$this->assertEquals($summaries, $requirements);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue