Skip to content

Module Dependencies

WissKI is a set of Drupal modules with a directed dependency graph. Drupal enables declared dependencies before the dependent module. A PHP use of another WissKI module without a matching .info.yml dependency can therefore fatal at enable time (especially on Drupal 10). Cycles in the declared graph are also forbidden.

Core layering

wisski_core sits at the bottom of the WissKI stack: it declares no WissKI module dependencies. Higher layers depend on it (and often on each other) in one direction only.

To inspect the full declared graph, run drush wisski-devel:dump-dot. That prints a DOT description of the module dependency digraph. Render it with Graphviz (for example dot to SVG or PNG):

drush wisski-devel:dump-dot | dot -Tsvg -o wisski-deps.svg

Typical edges (always check the module's own .info.yml for the authoritative list):

Module Depends on (WissKI)
wisski_pathbuilder wisski_core
wisski_salz wisski_pathbuilder
wisski_core none
Adapter / feature modules Usually wisski_pathbuilder, wisski_salz and wisski_core or a concrete adapter such as wisski_adapter_sparql11_pb

legacy/ modules are excluded from the automated check and from normal maintenance.

Avoiding cycles with pathbuilder interfaces

Concrete engine and adapter implementations live in wisski_salz and the adapter modules. If wisski_core imported those concrete classes, it would need to depend on them — and they already depend on wisski_core, which would create a cycle.

To keep the graph acyclic, capability contracts live in wisski_core:

  • Base types under wisski_pathbuilder/src/Entities/ (notably EngineInterface and AdapterInterface)
  • Capability interfaces under wisski_core/src/Engines/ (for example Sparql11EngineInterface, Sparql11EngineWithPbInterface, PathbuilderEngineInterface, ImageEngineInterface, …)

Implementations in lower-to-higher modules implement those interfaces. Call sites in pathbuilder (and other modules that must not hard-depend on a concrete adapter) type-hint the interfaces only, then use instanceof when a specific capability is required.

When you need a new cross-module engine API, add or extend an interface in wisski_core rather than importing a concrete class from wisski_salz or an adapter into a module that those packages already depend on.

Checking dependencies

The unit test ModuleDependencyTest in wisski_core/tests/src/Unit/ drives ModuleDependencyChecker in the same directory. Together they:

  1. Build the declared WissKI-to-WissKI graph from every non-legacy .info.yml
  2. Fail on dependency cycles
  3. Scan production PHP for Drupal\wisski_* class use imports and require a matching declared dependency (direct; not transitive)

Outgoing import edges from modules listed in ModuleDependencyTest::SKIP_MODULES (currently wisski_test) are ignored.

Run:

phpunit wisski_core/tests/src/Unit/ModuleDependencyTest.php

If the test reports a missing edge, either add - wisski:… to the importing module's .info.yml, or redesign the import to go through a pathbuilder interface so the hard dependency is unnecessary.

Deptrac

Deptrac can additionally analyse PHP layer rules derived from the same module graph. Generate a config with Drush, write it to deptrac.yaml at the project root, then run Deptrac:

drush wisski-devel:dump-deptrac-config > deptrac.yaml
deptrac analyse

Do not commit a generated deptrac.yaml unless the project deliberately tracks one; Deptrac's cache (.deptrac.cache) is gitignored.