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

Command icon 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

freelock created an issue. See original summary.

freelock changed the visibility of the branch 3546633-phpunit-functional-tests to hidden.

freelock changed the visibility of the branch 3546633-phpunit-functional-tests to active.

freelock’s picture

Fix 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.

quietone’s picture

Version: 11.2.x-dev » 11.x-dev

Hi, 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.

cilefen’s picture

@freelock core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php failed, which is a different one than you updated.

freelock’s picture

Struggling 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!

cilefen’s picture

DM me in Slack.

freelock’s picture

Status: Active » Needs review

Fixed 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!

smustgrave’s picture

ghost of drupal past’s picture

Thanks for working on this.

This is the only query parameter that should be passed through to the connection options

Since this is in the base connection class I doubt any such presumption could be made.

So maybe just do $database += $query_params instead of copying a single one over?

Similarly, perhaps in createUrlFromConnectionOptions unset the known keys after they are processed (username, password, host, database etc) and just shovel the rest into $query_params instead of hardcoding module and unix_socket? Is there any harm in putting too much in the url?

freelock’s picture

Well, 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?

ghost of drupal past’s picture

That'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?

mondrake’s picture

Status: Needs review » Needs work

Sorry what is the MR to review? There are 2 open now.

freelock’s picture

The 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.

mondrake’s picture

Commented inline

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.