The example below works, and I see it used in some extensions, but it has no autocompletion and not catching typos.
Services.php: class TranslateServices implements ContainerInterface { public function getParsingPlaceholderFactory(): ParsingPlaceholderFactory { return $this->container->get( 'Translate:ParsingPlaceholderFactory' ); }
public function getTranslatablePageParser(): TranslatablePageParser { return $this->container->get( 'Translate:TranslatablePageParser' ); } }
ServiceWiring.php: return [ 'Translate:ParsingPlaceholderFactory' => function (): ParsingPlaceholderFactory { return new ParsingPlaceholderFactory(); },
'Translate:TranslatablePageParser' => function ( MediaWikiServices $services ) : TranslatablePageParser { return new TranslatablePageParser( $services->get( 'Translate:ParsingPlaceholderFactory' ) # <-------- ); }, ];
Do you see any downsides of using code like below instead?
'Translate:TranslatablePageParser' => function (): TranslatablePageParser { $services = TranslateServices::getInstance(); return new TranslatablePageParser( $services->getParsingPlaceholderFactory() ); },
I looked at other extensions and I noticed a lot of small differences among them: * Some extensions use static methods as opposed to wrapping the core service container * Some extensions use constants for service identifiers * Lots of different implementations of "To avoid name conflicts, the service names should be prefixed with the extension's name.": ** ExtensionService ** Extension.Service ** Extension:Service ** Extension_Service
Are we yet in a stage to agree on some (additional) conventions and document them somewhere? Maybe in https://www.mediawiki.org/wiki/Dependency_Injection
-Niklas
Hi Niklas!
Do you see any downsides of using code like below instead?
'Translate:TranslatablePageParser' => function (): TranslatablePageParser { $services = TranslateServices::getInstance(); return new TranslatablePageParser( $services->getParsingPlaceholderFactory() ); },
The only downside is that it uses global state. In a future in which we'll some day have multiple instances of MediaWikiServices, one for the local wiki and one for each "sibling" wiki we want to access, this wouldn't work.
But then, this future is probably still pretty far off, and this would be easy to change. On the other hand, a typo in $services->get( 'Translate:ParsingPlaceholderFactory' ) is spotted quickly, and the lack of auto completion in the wiring file (and only in the wiring file) isn't so terrible, is it?
A cleaner solution would be for TranslateServices to not implement ContainerInterface directly, but extend ServiceContainer. That way, it would have its own wiring, instead of contribution to the wiring in MediaWikiServices. And the first parameter passed to instantiator callbacks would be the TranslateServices, not MediaWikiServices. If you also want MediaWikiServices to be passed in as well as the second parameter, you can supply it to the constructor of ServiceContainer via the $extraInstantiationParams parameter.
By the way, you can manage your singleton of TranslateServices as a static variable, but if you want to prepare for the bright an shiny future, you can also make TranslateServices a service in MediaWikiServices :)
I'd go for something like
/* TranslateServices.php */ public static function wrap( MediaWikiServices $services ) { return new self( $services ); }
/* ServiceWiring.php */ 'Translate:TranslatablePageParser' => function ( MediaWikiServices $services ) { return new TranslatablePageParser( TranslateServices::wrap( $services )->getParsingPlaceholderFactory(); }; },
wikitech-l@lists.wikimedia.org