A reference Drupal project for testing is available at: https://github.com/syntlyx/drupal-example
How Drupal is structured
A standard Drupal Composer project looks like this:
project/
├── composer.json
├── composer.lock
├── vendor/
└── web/ # web root (varies, see below)
├── core/ # Drupal core
├── modules/
│ ├── contrib/ # contributed modules
│ └── custom/ # project-specific modules
├── themes/
│ ├── contrib/
│ └── custom/
├── profiles/ # install profiles
└── sites/
Detecting a Drupal project
PHPantom can reliably detect Drupal from composer.json:
"require": {
"drupal/core-recommended": "^10.0"
}
Relevant package names to detect: drupal/core, drupal/core-recommended, drupal/core-dev.
Resolving the web root
The web root is not always web/. It is defined in composer.json under:
"extra": {
"drupal-scaffold": {
"locations": {
"web-root": "docroot/"
}
}
}
Common values: web/, docroot/, public/, html/. If the key is absent, fall back to checking which of these directories contains core/lib/Drupal.php.
Paths to index
Once the web root is resolved, the following directories should be scanned:
| Path |
Contains |
{web-root}/core/ |
Drupal core classes, interfaces, hooks |
{web-root}/modules/contrib/ |
Contributed modules |
{web-root}/modules/custom/ |
Project-specific modules |
{web-root}/themes/contrib/ |
Contributed themes |
{web-root}/themes/custom/ |
Project themes |
{web-root}/profiles/ |
Install profiles |
{web-root}/sites/ |
Site-specific code |
PHP file extensions
Drupal uses several non-.php extensions for valid PHP files:
| Extension |
Purpose |
.module |
Hook implementations, main module file |
.install |
hook_schema(), update hooks, hook_install() |
.theme |
Theme hook implementations |
.profile |
Install profile hooks |
.inc |
Shared include files |
.engine |
Theme engine files (rare) |
All of these start with <?php and are plain PHP.
Paths to exclude
Test directories should be excluded from indexing — they add noise and can contain duplicate class definitions:
| Path |
Reason |
{web-root}/core/tests/ |
Drupal core test classes |
{web-root}/core/modules/*/tests/ |
Per-module core tests |
{web-root}/modules/contrib/*/tests/ |
Contrib module tests |
{web-root}/modules/custom/*/tests/ |
Custom module tests (optional, project preference) |
{web-root}/themes/contrib/*/tests/ |
Contrib theme tests |
vendor/*/tests/ |
Vendor package tests |
vendor/*/Tests/ |
Some packages use capital T |
Note that both tests/ and Tests/ variants exist in the wild — Drupal core and most contrib use lowercase, but some Composer packages (Symfony, Doctrine) use uppercase.
The .gitignore problem
Drupal's default .gitignore excludes the web root scaffold files:
/web/core
/web/modules/contrib
/web/themes/contrib
/vendor
These directories are excluded from version control because they are managed by Composer — but they must be indexed by PHPantom. web/core in particular contains all of Drupal's base interfaces, hooks, and services that every module depends on.
Using .gitignore to determine what to skip will silently drop the most important parts of the codebase.
A reference Drupal project for testing is available at: https://github.com/syntlyx/drupal-example
How Drupal is structured
A standard Drupal Composer project looks like this:
Detecting a Drupal project
PHPantom can reliably detect Drupal from
composer.json:Relevant package names to detect:
drupal/core,drupal/core-recommended,drupal/core-dev.Resolving the web root
The web root is not always
web/. It is defined incomposer.jsonunder:Common values:
web/,docroot/,public/,html/. If the key is absent, fall back to checking which of these directories containscore/lib/Drupal.php.Paths to index
Once the web root is resolved, the following directories should be scanned:
{web-root}/core/{web-root}/modules/contrib/{web-root}/modules/custom/{web-root}/themes/contrib/{web-root}/themes/custom/{web-root}/profiles/{web-root}/sites/PHP file extensions
Drupal uses several non-
.phpextensions for valid PHP files:.module.installhook_schema(), update hooks,hook_install().theme.profile.inc.engineAll of these start with
<?phpand are plain PHP.Paths to exclude
Test directories should be excluded from indexing — they add noise and can contain duplicate class definitions:
{web-root}/core/tests/{web-root}/core/modules/*/tests/{web-root}/modules/contrib/*/tests/{web-root}/modules/custom/*/tests/{web-root}/themes/contrib/*/tests/vendor/*/tests/vendor/*/Tests/Note that both
tests/andTests/variants exist in the wild — Drupal core and most contrib use lowercase, but some Composer packages (Symfony, Doctrine) use uppercase.The
.gitignoreproblemDrupal's default
.gitignoreexcludes the web root scaffold files:These directories are excluded from version control because they are managed by Composer — but they must be indexed by PHPantom.
web/corein particular contains all of Drupal's base interfaces, hooks, and services that every module depends on.Using
.gitignoreto determine what to skip will silently drop the most important parts of the codebase.