This RFC describes some principles to adopt to make use of dependency injection in MediaWiki. Details and examples are described at https://www.mediawiki.org/wiki/Requests_for_comment/Dependency_injection. The core of the proposal is to adopt the following as best practice:
- When a service (or application logic) needs access to another a service, it asks for it in the constructor. This is the actual injection of dependencies.
- Objects that need access to services can only be constructed via factories (not directly using the new operator).
- Services are constructed by other services (factories and registries being special types of service). At the top of this chain of registries/factories there is the application scope service locator which acts as the top level service registry.
- Access to global default instances ("singletons") should be restricted to static entry points (e.g. hook handlers and callbacks in bootstrap code). Ideally, there is only one such global default instance, namely the service locator.
- Injecting/passing around factories, registries, and especially "kitchen sinks" like RequestContext should be avoided. The service locator should never be passed as a parameter.
- Mutable global state should especially be avoided.
- Services should be represented by narrow interfaces (e.g. UserLookup).
- Registries use constructor callbacks (aka factory functions) to instantiate services.
- Bootstrap code should avoid instantiating services, but define constructor callback instead.
These principles effectively provide dependency injection "on foot", without the need for a DI framework or a declarative syntax for networks of service objects. Decisions regarding the life cycle of service objects (lazy initialization, etc) are left to plain old PHP code.
Initial code experiments:
- MediaWiki service locator: https://gerrit.wikimedia.org/r/#/c/245483/3
- Moving some singletons into the service locator: https://gerrit.wikimedia.org/r/#/c/245484/4