Problem/Motivation
In Drupal 9.3, it was possible to do the following within settings.php
:
$databases['default']['default'] = [
...
];
Database::setMultipleConnectionInfo($databases);
try {
Database::getConnection();
}
catch (Exception $e) {
// Do something
}
In Drupal 9.4, this now fails because the database driver is in core/modules/$driver, and that's not in the autoloader until after settings.php completes.
This can be worked around by adding $class_loader->addPsr4('Drupal\\mysql\\', 'core/modules/mysql/src/')
prior to calling Database::getConnection();
, but for sites with settings.php files that don't do that yet, this is a regression.
Acquia Cloud is one example where code like the above in settings.php (or rather, a .inc file included by settings.php) is done. I don't know if other Drupal hosting platforms employ similar constructs.
Steps to reproduce
Proposed resolution
Given that a workaround is possible, I don't know if we want to address this issue in core. If we do, then perhaps something like moving the current Settings::initialize()
code for setting up the autoloader into Database::getConnection()
might solve it? Maybe other ideas?
Remaining tasks
This is postponed on #3292014: Make Drupal's primary class loader available to code that runs before the container is initialized
User interface changes
API changes
Data model changes
Release notes snippet
Comment | File | Size | Author |
---|---|---|---|
#17 | interdiff.txt | 5.45 KB | effulgentsia |
#17 | 3290924-17.patch | 12.86 KB | effulgentsia |
#15 | interdiff.txt | 2.38 KB | effulgentsia |
#15 | 3290924-15.patch | 10.57 KB | effulgentsia |
#15 | 3290924-15-test-only.patch | 2.03 KB | effulgentsia |
Comments
Comment #2
xjmComment #3
effulgentsia CreditAttribution: effulgentsia at Acquia commentedComment #4
effulgentsia CreditAttribution: effulgentsia at Acquia commentedMaybe something like this?
Comment #5
effulgentsia CreditAttribution: effulgentsia at Acquia commentedWith a code comment.
Comment #6
catchI didn't like the sound of this when I read the sentence, but the patch itself looks pretty good to me.
Comment #9
effulgentsia CreditAttribution: effulgentsia at Acquia commentedComment #10
effulgentsia CreditAttribution: effulgentsia at Acquia commented#9 would still require people who are affected by this issue to change their settings.php files to pass in
$class_loader
when callingsetMultipleConnectionInfo()
.Here's a patch that fixes this issue without requiring any modifications to existing settings.php files, by defaulting to registering an additional class loader when $class_loader isn't passed in.
We already have precedent for doing this in convertDbUrlToConnectionInfo(). There that was deemed okay because that's for test runners and console commands and not part of the typical page request. Doing it here, however, puts it in the typical page request, but only for the cases where settings.php itself needs to open a database connection and doesn't itself pass in $class_loader.
If we really don't like the idea of using an additional class loader, even if limited to only a subset of sites, then I wonder if it's worth adding something like
\Drupal::(set|get)ClassLoader()
methods, so that we can make the class loader that's inSettings::initialize()
available to code that gets called indirectly by settings.php or other early bootstrap code.Comment #12
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThis doesn't yet fix the failure in #10, but meanwhile, here's a test-only patch that demonstrates the bug, and the full patch is just #10 plus this test.
Comment #15
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThis fixes both failures. Since this updates the test as well, uploading the updated test-only patch too.
I'm going to explore this next.
Comment #17
effulgentsia CreditAttribution: effulgentsia at Acquia commentedHere's an attempt to make Drupal's primary class loader available as a psuedo-service (
\Drupal::classLoader()
) during the entirety of the request, at least for normal requests that go through DrupalKernel. Test runners might still need to fuss with it, as this patch does in SettingsTest.Comment #18
daffie CreditAttribution: daffie commentedThe patch from comment #15 looks very good to me. Just 2 nitpicks:
We are already doing this in
addConnectionInfo()
, do we remove it here?The function Database::convertDbUrlToConnectionInfo() also has the same code. Can we replace that code with self::getClassLoader()?
The patch from comment #17 looks to me like as we should do the class loader stuff in a saperate issue and after that is done, fix the problem from this issue. Also there are a couple more instances in DRUPAL_ROOT\core\lib where we have code like
new ClassLoader()
.@effulgentsia: As you are working on this issue and you are core framework manager, I leave it up to you which route we should to take (the patch from comment #15 or the one from comment #17). My preference is the one from comment #15.
Comment #19
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThanks, @daffie! Good point about spinning off a separate issue for
\Drupal::classLoader()
. I'll do that this week. In the meantime, I think it'd like to postpone this on that. My concern with #15 is that it creates a class loader that's separate from Drupal's primary one. I think that's messy. For example, if there's code later in Drupal's request that does aDrupal::service('class_loader')->unregister()
, it should expect all of Drupal's class loading to be unregistered, not for there to be another autoloader that's still running. Also, if settings.php decorates $class_loader, with say something that does logging, then that logging should work on database driver classes too.I realize that we have a few other places where we have an additional class loader already, such as for finishing a PHP request after enabling a new module, or in Database::convertDbUrlToConnectionInfo(), but those are special code paths that don't typically run in production. I'm hesitant to extend the pattern to a typical Drupal page request.
If we end up deciding against making the primary class loader available during bootstrap within \Drupal:: or somewhere else, then we can circle back to #15's approach of creating a separate class loader, but I'd like to at least try for the single class loader approach first.
Comment #20
effulgentsia CreditAttribution: effulgentsia at Acquia commentedI opened #3292014: Make Drupal's primary class loader available to code that runs before the container is initialized for #19.
Comment #21
masipila CreditAttribution: masipila as a volunteer commentedI was trying to update my 9.3.x to 9.4.1 and I believe I'm affected by this regression. My site is built with composer, using the drupal/recommended-project.
My symptoms:
I read through all comments above and looked at some of the patches of this issue.
Patch #5 has this inline comment:
I believe I have this Drupal 8 style database connection array, since my site originates from the early Drupal 8 versions.
My database array in settings.php was this, and it indeed has the 'namespace' element that the inline comment quoted above talks about.
However, even if I try to update it to this, what is currently present in the 9.4.x default.settings.php, my issue persists:
I'd like to update my settings.php to be of the current style, but I can't see any other difference in the $databases array than that the 'namespace' has been removed and 'collation' has been added.
I understand the discussion above about the workarounds, but I guess the more elegant approach for me and other sites from the early D8 days would be to modify the settings.php to reflect what it currently needs to be, it's just that I don't spot what other aspects I'm missing... Any hints would be appreciated!
Cheers,
Markus
p.s. I do not want to de-rail the scope of this issue. The reason why I'm posting this comment here is that this specific issue is linked from the 9.4.0 release notes, which I did read. And I'm posting this comment here so that other community members who read the release notes would find this as well.
Edit: Added the link to 9.4.x default.settings.php
Comment #22
masipila CreditAttribution: masipila as a volunteer commentedOkay, figured it out. Writing here for others that might be affected by the same things.
It turned out that I had two separate issues:
1. I was affected by the Composer July Timebomb. The 'allow-plugins' section needs to be added to composer.json, see
https://github.com/drupal/recommended-project/blob/9.4.x/composer.json#L32
2. After I added that and removed the old 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql' from the $databases array in settings.php, my site was back to normal and I was able to complete the 9.4.x updates.
Hope this helps if somebody else is affected in the same way that I was.
Cheers,
Markus
Comment #23
quietone CreditAttribution: quietone at PreviousNext commentedAdd postponed issue to the IS>
Comment #24
vlad.dancerWe are affected by this too on Acquia environments:
On drush cr:
On drush status:
The recommended workaround on Acquia (only for dev/staging) is to set databases classes namespace in settings.(acquia.)php:
Comment #25
nomisgnos CreditAttribution: nomisgnos commentedWe noticed the error when upgrading from 9.4.7 to 9.4.8. The workaround does fix the issue.
We are also on Acquia.
@vlad.dancer
Why only for dev and staging? Why not prod?
Comment #26
peezy CreditAttribution: peezy as a volunteer commentedAcquia's article has been updated to read "this workaround should be tested on lower environments (dev or stage) before applying to a production environment."
Comment #27
effulgentsia CreditAttribution: effulgentsia at Acquia commentedRelated to #24 - #26, Drupal 9.4.8 introduced a new variant to this issue's original regression here as outlined in #3294695-24: Drupal 8 BC for database driver namespace fails for replicas. In 9.4.0 - 9.4.7, only the getConnection() call was affected. In 9.4.8, all database-related code that runs between the conclusion of settings.php and the completion of Drupal's full bootstrap is affected. Primarily, that's Drush commands. The workaround in the issue summary is successful at resolving both flavors of this regression.
Acquia employed a slightly different workaround at the platform level to resolve the 9.4.0 regression for our customers, and is presently working on releasing an updated workaround to resolve the 9.4.8 regression. In the meantime, individual sites can resolve it via this issue summary's workaround as mentioned in #24.