Problem/Motivation
When using a non-standard Unix socket for a database connection, PHPUnit functional tests fail, unable to reach the database.
The Drupal Flake project creates a local development environment, using a socket in a subdirectory of the project. This works fine for both php-fpm connections, and CLI connections (for drush) -- however, PHPUnit does not preserve any unix_socket parameters set in the db connection string, and so tests that need to bootstrap Drupal into a database fail in the setup phase.
This occurs because:
1. The parent process correctly parses SIMPLETEST_DB containing unix_socket parameter (e.g., mysql://user@host/db?unix_socket=/path/to/socket)
2. PHPUnit's PhpUnitTestRunner calls Database::getConnectionInfoAsUrl() to create SIMPLETEST_DB for child processes
3. Database::getConnectionInfoAsUrl() calls Connection::createUrlFromConnectionOptions() which ignores the unix_socket parameter
4. Child processes receive incomplete database URLs without socket information, causing connection failures
This affects:
- Functional tests in containerized environments (Docker, Nix, etc.)
- Development environments using Unix sockets instead of TCP connections
- CI/CD pipelines with custom MySQL configurations
PHPUnit currently uses the pdo_mysql.default_socket value in the running php, overriding any unix_socket specified in any environment variable, phpunit.xml, DSN.
Steps to reproduce
With nix installed you can set up a local dev Drupal environment:
1. nix flake init -t git+https://git.drupalcode.org/project/drupal_flake
2. nix develop
3. start-detached
4. wait until pc-status shows the servers are running, usually 10 - 30 seconds after everything is built
5. phpunit-setup (custom wrapper that creates phpunit.xml, simpletest directories)
6. phpunit path/to/any/functional/test
Result: tests run, but any database interaction fails to connect.
Proposed resolution
Modify Drupal\Core\Database\Connection::createUrlFromConnectionOptions() to preserve the unix_socket parameter (and other query parameters) when reconstructing database URLs.
Changes needed:
1. Build query parameters array from connection options
2. Include unix_socket parameter if present in connection options
3. Use http_build_query() to properly encode parameters in the URL
This ensures that child processes receive complete database connection information including Unix socket paths.
Remaining tasks
- Create patch for Connection::createUrlFromConnectionOptions()
- Add unit test to verify unix_socket parameter preservation
- Add unit test to verify other query parameters are preserved
- Review for other database drivers (PostgreSQL, SQLite) that might need similar fixes
- Update documentation if needed
User interface changes
None.
Introduced terminology
None.
API changes
The change is backward compatible. The createUrlFromConnectionOptions() method signature remains unchanged, but the returned URL will now include query parameters that were previously omitted.
Before:
$url = Connection::createUrlFromConnectionOptions([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'test',
'unix_socket' => '/tmp/mysql.sock',
'module' => 'mysql'
]);
// Returns: mysql://localhost/test?module=mysql
After:
// Returns: mysql://localhost/test?module=mysql&unix_socket=%2Ftmp%2Fmysql.sock
Data model changes
None.
Release notes snippet
Fixed database connection URL reconstruction for child processes: Database connection URLs created for child processes (such as during functional tests) now properly preserve connection parameters like unix_socket. This fixes functional test failures in environments using Unix socket database connections instead of TCP.
Issue fork drupal-3546633
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #6
freelockFix in merge request, along with test... still needs a little cleanup for code standards. Had some weirdness trying to create a merge request -- the first one picked up an entirely different branch!
Not sure why the tests are failing here - they are passing on my local copy.
Comment #7
quietone commentedHi, in Drupal core changes are made on on 11.x (our main development branch) first, and are then back ported as needed according to the Core change policies. Thanks.
Comment #8
cilefen commented@freelock core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php failed, which is a different one than you updated.
Comment #10
freelockStruggling with gitlab here... when I first opened a MR, it picked up the entirely wrong commit. And now when changing the branch target it's showing 1000+ commits instead of just the one!
Comment #11
cilefen commentedDM me in Slack.
Comment #12
freelockFixed code standard issues, moved the tests into an existing UrlConversionTest classes, limited the scope of the change to just "unix_socket" to fix test failures, and sorted out the gitlab MR challenges - thanks @cilefen!
Comment #13
smustgrave commentedComment #14
ghost of drupal pastThanks for working on this.
Since this is in the base connection class I doubt any such presumption could be made.
So maybe just do
$database += $query_paramsinstead of copying a single one over?Similarly, perhaps in
createUrlFromConnectionOptionsunset the known keys after they are processed (username, password, host, database etc) and just shovel the rest into$query_paramsinstead of hardcoding module and unix_socket? Is there any harm in putting too much in the url?Comment #15
freelockWell, are there any other parameters that the library accepts?
I can definitely refactor to allow any other parameters to pass through -- but this feels like relaxing the schema on a config object -- the opposite direction to the trends I see in Drupal in general, becoming more strict about what is valid and what is not...
unix_socket seems like a big missing API that is fundamental to hooking up the database in many environments. Is there any other parameter to consider?
Looking at https://dev.mysql.com/doc/refman/8.4/en/connecting-using-uri-or-key-valu... , it looks like there are quite a few parameters available, around SSL handling, keys, compression, ssh...
So I guess there are other configurations that are possible that Drupal doesn't currently support, and we could easily add it here -- should we?
Comment #16
ghost of drupal pastThat's MySQL. What about other databases? This is the base Connection class which will be reused by every driver, core and contrib and custom, isn't it?
Comment #17
mondrakeSorry what is the MR to review? There are 2 open now.
Comment #18
freelockThe MR to review is MR !13232 - the other one I somehow created off a completely different issue or incorrect branch or something.
The main change is in the mysql driver -- but it needed support in the base connection class to even reach that driver. I think each driver has its own set of possible options -- I don't know if there are other real world cases where we might need to support other options.
Comment #19
mondrakeCommented inline