Wikimedia Developer Support

Registering an optional service in MediaWikiServices

In I2df0f7ea3a, I tried to move an extension service to MediaWikiServices which is conditionally available depending on configuration: unless the WBQualityConstraintsSparqlEndpoint configuration setting has been set to a value other than its default '', the service is not available, and all the classes that get such a service injected deal with this by checking whether it is null before using it.

However, this resulted in a test failure from a MediaWiki core test: MediaWikiServicesTest::testDefaultServiceInstantiation asserts that the value returned by the wiring function is an object, which is not true for the null value which this wiring function returns by default.

Is it possible to have a service that is possibly null? (I found one such service using MediaWiki code search, in GoogleLogin’s ServiceWiring.php, but that might also be a bug.) And in that case, should the core test be updated to allow that?

Otherwise, how should I proceed? The best working solution I can see so far would be to define a “dummy” version of the class and return that if the service is not configured, but then all the classes which get the service injected would have to check whether their injected service is an instance of the dummy class or not, which seems ugly. (For at least one class, FormatChecker, there is no possible implementation of the dummy that would allow the class to call the service unconditionally, without caring whether it’s real or a dummy.)

The answer seems to be (summarizing an IRC discussion with @Tgr on #wikimedia-tech) that this is not directly possible. You should use a dummy implementation as the default service, and refactor users of the service so that they don’t need to care which implementation they’re seeing.

More importantly (as making it possible would be a trivial code change) I think it’d be bad practice to have nullable services. The goal of using services is to decouple responsibilities; from one component’s point of view other components should be black boxes where we only need to care about the interface contract (pass something in, get something back) so if a dummy implementation is hard to write that means things are not sufficiently decoupled.

Of course that’s the kind of advice that’s easy to give in the abstract without knowing the specifics of the problem, but often very hard to apply so it might not be very helpful… I looked at the patch you linked, but from that it’s hard to guess how the service is being used. Can’t a dummy constraint checker always just return success?

Well, the service is not a constraint checker, it’s a service used by a constraint checker. And the constraint checker should return a different status if the constraint check is not available (“not implemented”).

I don’t deny that it could be split up better, but it sounds like in this case MediaWikiServices can’t be used until the rest of the codebase has been brought “in shape”, which kinda stands in the way of incremental improvement.