Pop quiz: You’ve got your head wrapped around Magento 2’s dependency injection. You’re taking a stroll through the code base, and you see this constructor.
#File: lib/Magento/PubSub/Event/QueueHandler.php
public function __construct(MagentoPubSubEventQueueReaderInterface $eventQueue,
MagentoPubSubJobQueueWriterInterface $jobQueue,
MagentoPubSubJobFactoryInterface $jobFactory,
MagentoPubSubSubscriptionCollectionInterface $subscriptionSet
) {
$this->_eventQueue = $eventQueue;
$this->_jobQueue = $jobQueue;
$this->_jobFactory = $jobFactory;
$this->_subscriptionSet = $subscriptionSet;
}
Since you know about Magento’s automatic dependency injection, you know the _eventQueue
property should contain an object that’s an instance of the MagentoPubSubJobQueueWriterInterface
class.
Except MagentoPubSubJobQueueWriterInterface
isn’t a class. It’s a PHP programatic interface
#File: lib/Magento/PubSub/Event/QueueReaderInterface.php
namespace MagentoPubSubEvent;
interface QueueReaderInterface
{
//...
}
What do you do now, hotshot?
Interfaces and Dependency Injection
Don’t worry, Magento’s automatic dependency injection is not a homunculus who blows up buses or instantiates objects from interfaces. Instead, if you use an interface as a type hint in a Magento object’s constructor, the system will search for that interface’s preferred class.
A module developer (the core team, you, etc.) specifies these preferences in a module’s di.xml
file. In the above example, the interface’s full name is MagentoPubSubEventQueueReaderInterface
. If we look in the following file
#File: app/code/Magento/Webhook/etc/di.xml
<preference for="MagentoPubSubEventQueueReaderInterface"
type="MagentoWebhookModelEventQueueReader" />
We can see the authors of the Magento_Webhook
module have specified a preference for the MagentoPubSubEventQueueReaderInterface
interface as the class MagentoWebhookModelEventQueueReader
.
What does this mean? When the automatic dependency injection system sees
MagentoPubSubEventQueueReaderInterface
public function __construct(MagentoPubSubEventQueueReaderInterface $eventQueue, ...
it will instantiate the object in $eventQueue
with the MagentoWebhookModelEventQueueReader
class.
While this all is a little confusing the first time you encounter it, I like the direction. Tying the auto-injected classes to a specific interface (i.e. a core PHP language feature) seems like an win, and will help prevent configuration swapping of classes (i.e “class rewrites” in Magento 1) from breaking the original class’s implicit contract.
In less nerdy terms, it would be impossible for someone to rewrite the class MagentoWebhookModelEventQueueReader
without implementing all the methods in the QueueReaderInterface
interface. In Magento 1 the same rewrite could (accidentally) omit some necessary QueueReader
method that would only be discovered when/if the method was used at runtime, maybe days after the initial deployment. This “interfaces in dependency injection” feature brings Magento 2 developers some compile-time-like checking that our Java and C# friends take for granted.