Skip to content

Closes #320: Extensions in sites/<site-dir>/modules not discovered and autoloaded#956

Open
dpagini wants to merge 4 commits intomglaman:mainfrom
dpagini:320
Open

Closes #320: Extensions in sites/<site-dir>/modules not discovered and autoloaded#956
dpagini wants to merge 4 commits intomglaman:mainfrom
dpagini:320

Conversation

@dpagini
Copy link

@dpagini dpagini commented Mar 26, 2026

ExtensionDiscovery doesn't scan multisite directories, causing false "module is not found" errors

Problem

ExtensionDiscovery hardcodes the site path to sites/default:

$this->sitePath = 'sites/default';

In a Drupal multisite setup, modules can live under other site-specific directories like sites/acme/modules/. Because ExtensionDiscovery::scan() only searches sites/default for the ORIGIN_SITE weight, modules in any other site directory are never added to the ExtensionMap.

This causes the LoadIncludes rule (and ModuleLoadInclude) to report false positives:

File acme_module.inc could not be loaded from Drupal\Core\Extension\ModuleHandlerInterface::loadInclude because acme_module module is not found.

Steps to reproduce

  1. Place a module under a non-default site directory, e.g. sites/acme/modules/acme_module/
  2. Reference that module via loadInclude():
    \Drupal::moduleHandler()->loadInclude('acme_module', 'inc');
  3. Run PHPStan — the module is not found

Failing test

Failing test evidence for commit 718ef91

Branch 320 adds a test demonstrating this bug. The test places a fixture module under tests/fixtures/drupal/sites/acme/modules/ and asserts that loadInclude() resolves it without errors. It currently fails:

FAILURES!
Tests: 6, Assertions: 6, Failures: 1

1) LoadIncludesRuleTest::test with data set "multisite module in sites/acme/"
  File acme_module.inc could not be loaded from
  Drupal\Core\Extension\ModuleHandlerInterface::loadInclude
  because acme_module module is not found.

Proposed fix

Replace the single $sitePath property with discovery of all site directories under sites/*/. In scan(), iterate through each discovered site path with the ORIGIN_SITE weight, skipping sites/all (already handled) and sites/simpletest (irrelevant).

This mirrors what Drupal core's own ExtensionDiscovery does when no specific site path is provided — it scans all site directories.

Disclaimer: some contents written with help of AI

@dpagini
Copy link
Author

dpagini commented Mar 26, 2026

@aweingarten just pinging you here b/c you opened #320 (even though it was 6 years ago). Not sure if you have any thoughts on this one...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants